diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 9551f10f0d3..cbba3ae95ba 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3102,8 +3102,9 @@ REFERENCES: with tunable clustering*, Phys. Rev. E (2002). vol 65, no 2, 026107. :doi:`10.1103/PhysRevE.65.026107`. -.. [HKL2021] Clemens Heuberger, Daniel Krenn, Gabriel F. Lipnik, "Asymptotic - Analysis of `q`-Recursive Sequences", :arxiv:`2105.04334`, 2021. +.. [HKL2022] Clemens Heuberger, Daniel Krenn, Gabriel F. Lipnik, "Asymptotic + Analysis of `q`-Recursive Sequences". Algorithmica **84** (2022), + 2480–2532. :doi:`10.1007/s00453-022-00950-y`. .. [HKOTY1999] \G. Hatayama, A. Kuniba, M. Okado, T. Tagaki, and Y. Yamada, *Remarks on fermionic formula*. Contemp. Math., **248** (1999). diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 707f03879ef..737b0ae1edf 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -21,6 +21,10 @@ might change without a formal deprecation. See https://github.com/sagemath/sage/issues/21202 for details. +:: + + sage: import logging + sage: logging.basicConfig() Examples ======== @@ -53,10 +57,10 @@ sage: tuple(u(n) for n in srange(10)) (0, 1, 3, 5, 9, 11, 15, 19, 27, 29) -There is a `2`-recursive sequence describing the numbers above as well:: +There is a `2`-regular sequence describing the numbers above as well:: - sage: U = Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), - ....: left=vector([0, 1]), right=vector([1, 0])).transposed() + sage: U = Seq2((Matrix([[3, 0], [2, 1]]), Matrix([[2, 1], [0, 3]])), + ....: left=vector([1, 0]), right=vector([0, 1])) sage: all(U[n] == u(n) for n in srange(30)) True @@ -165,6 +169,25 @@ def value(D, k): return sum(d * k**j for j, d in enumerate(D)) +class DegeneratedSequenceError(RuntimeError): + r""" + Exception raised if a degenerated sequence + (see :meth:`~kRegularSequence.is_degenerated`) is detected. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + """ + pass + + class kRegularSequence(RecognizableSeries): def __init__(self, parent, mu, left=None, right=None): r""" @@ -190,11 +213,20 @@ def __init__(self, parent, mu, left=None, right=None): from the right to the matrix product. If ``None``, then this multiplication is skipped. + When created via the parent :class:`kRegularSequenceSpace`, then + the following option is available. + + - ``allow_degenerated_sequence`` -- (default: ``False``) a boolean. If set, then + there will be no check if the input is a degenerated sequence + (see :meth:`is_degenerated`). + Otherwise the input is checked and a :class:`DegeneratedSequenceError` + is raised if such a sequence is detected. + EXAMPLES:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) - sage: S = Seq2((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), - ....: vector([0, 1]), vector([1, 0])).transposed(); S + sage: S = Seq2((Matrix([[3, 0], [6, 1]]), Matrix([[0, 1], [-6, 5]])), + ....: vector([1, 0]), vector([0, 1])); S 2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ... We can access the coefficients of a sequence by @@ -231,8 +263,8 @@ def _repr_(self): TESTS:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) - sage: s = Seq2((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), - ....: vector([0, 1]), vector([1, 0])).transposed() + sage: s = Seq2((Matrix([[3, 0], [6, 1]]), Matrix([[0, 1], [-6, 5]])), + ....: vector([1, 0]), vector([0, 1])) sage: repr(s) # indirect doctest '2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ...' """ @@ -316,6 +348,241 @@ def __iter__(self): from itertools import count return iter(self[n] for n in count()) + @cached_method + def is_degenerated(self): + r""" + Return whether this `k`-regular sequence is degenerated, + i.e., whether this `k`-regular sequence does not satisfiy + `\mu[0] \mathit{right} = \mathit{right}`. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) # indirect doctest + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + :: + + sage: C = Seq2((Matrix([[2, 0], [2, 1]]), Matrix([[0, 1], [-2, 3]])), + ....: vector([1, 0]), vector([0, 1])) + sage: C.is_degenerated() + False + """ + from sage.rings.integer_ring import ZZ + return (self.mu[ZZ(0)] * self.right) != self.right + + def _error_if_degenerated_(self): + r""" + Raise an error if this `k`-regular sequence is degenerated, + i.e., if this `k`-regular sequence does not satisfiy + `\mu[0] \mathit{right} = \mathit{right}`. + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), # indirect doctest + ....: left=vector([0, 1]), right=vector([1, 0])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + """ + if self.is_degenerated(): + raise DegeneratedSequenceError( + "degenerated sequence: mu[0]*right != right. " + "Using such a sequence might lead to wrong results. " + "You can use 'allow_degenerated_sequence=True' followed by " + "a call of method .regenerated() " + "for correcting this.") + + @cached_method + @minimize_result + def regenerated(self): + r""" + Return a `k`-regular sequence that satisfies + `\mu[0] \mathit{right} = \mathit{right}` with the same values as + this sequence. + + INPUT: + + - ``minimize`` -- (default: ``None``) a boolean or ``None``. + If ``True``, then :meth:`~RecognizableSeries.minimized` is called after the operation, + if ``False``, then not. If this argument is ``None``, then + the default specified by the parent's ``minimize_results`` is used. + + OUTPUT: + + A :class:`kRegularSequence` + + ALGORITHM: + + Theorem B of [HKL2022]_ with `n_0 = 1`. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + However, we can regenerate the sequence `S`:: + + sage: H = S.regenerated() + sage: H + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: H.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-2 3], + 1: [3 0] + [6 0]}, + (1, 1)) + sage: H.is_degenerated() + False + + TESTS:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: H = S.regenerated(minimize=False) + sage: H.linear_representation() + ((1, 0), + Finite family {0: [ 2|-1] + [--+--] + [ 0| 1], + 1: [3|0] + [-+-] + [0|0]}, + (1, 1)) + sage: H.is_degenerated() + False + """ + if not self.is_degenerated(): + return self + + from sage.matrix.constructor import Matrix + from sage.matrix.special import zero_matrix, identity_matrix + from sage.modules.free_module_element import vector + + P = self.parent() + dim = self.dimension() + Zc = zero_matrix(dim, 1) + Zr = zero_matrix(1, dim) + I = identity_matrix(dim) + + itA = iter(P.alphabet()) + z = next(itA) + W0 = Matrix(dim, 1, (I - self.mu[z]) * self.right) + mu = {z: Matrix.block([[self.mu[z], W0], [Zr, 1]])} + mu.update((r, Matrix.block([[self.mu[r], Zc], [Zr, 0]])) + for r in itA) + + return P.element_class( + P, mu, + vector(tuple(self.left) + (0,)), + vector(tuple(self.right) + (1,))) + + def transposed(self, allow_degenerated_sequence=False): + r""" + Return the transposed sequence. + + INPUT: + + - ``allow_degenerated_sequence`` -- (default: ``False``) a boolean. If set, then + there will be no check if the transposed sequence is a degenerated sequence + (see :meth:`is_degenerated`). + Otherwise the transposed sequence is checked and a :class:`DegeneratedSequenceError` + is raised if such a sequence is detected. + + OUTPUT: + + A :class:`kRegularSequence` + + Each of the matrices in :meth:`mu ` is transposed. Additionally + the vectors :meth:`left ` and :meth:`right ` are switched. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: U = Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), + ....: left=vector([0, 1]), right=vector([1, 0]), + ....: allow_degenerated_sequence=True) + sage: U.is_degenerated() + True + sage: Ut = U.transposed() + sage: Ut.linear_representation() + ((1, 0), + Finite family {0: [3 0] + [2 1], + 1: [2 1] + [0 3]}, + (0, 1)) + sage: Ut.is_degenerated() + False + + sage: Ut.transposed() + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: Utt = Ut.transposed(allow_degenerated_sequence=True) + sage: Utt.is_degenerated() + True + + .. SEEALSO:: + + :meth:`RecognizableSeries.transposed ` + """ + element = super().transposed() + if not allow_degenerated_sequence: + element._error_if_degenerated_() + return element + + def _minimized_right_(self): + r""" + Return a regular sequence equivalent to this series, but + with a right minimized linear representation. + + OUTPUT: + + A :class:`kRegularSequence` + + .. SEEALSO:: + + :meth:`RecognizableSeries._minimized_right_ ` + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([[3, 0], [2, 1]]), Matrix([[2, 1], [0, 3]])), # indirect doctest + ....: left=vector([1, 0]), right=vector([0, 1])).minimized() + 2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ... + """ + return self.transposed(allow_degenerated_sequence=True)._minimized_left_().transposed(allow_degenerated_sequence=True) + @minimize_result def subsequence(self, a, b): r""" @@ -489,6 +756,27 @@ def subsequence(self, a, b): Traceback (most recent call last): ... ValueError: a=-1 is not nonnegative. + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + + This leads to the wrong result + :: + + sage: S.subsequence(1, -4) + 2-regular sequence 0, 0, 0, 0, 8, 12, 12, 18, 24, 36, ... + + We get the correct result by + :: + + sage: S.regenerated().subsequence(1, -4) + 2-regular sequence 0, 0, 0, 0, 1, 3, 6, 9, 12, 18, ... """ from itertools import chain from sage.rings.integer_ring import ZZ @@ -795,6 +1083,55 @@ def partial_sums(self, include_n=False): sage: C.partial_sums(include_n=True) 2-regular sequence 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, ... + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + + Therefore, building partial sums produces a wrong result:: + + sage: H = S.partial_sums(include_n=True, minimize=False) + sage: H + 2-regular sequence 1, 5, 16, 25, 62, 80, 98, 125, 274, 310, ... + sage: H = S.partial_sums(minimize=False) + sage: H + 2-regular sequence 0, 2, 10, 16, 50, 62, 80, 98, 250, 274, ... + + We can :meth:`~kRegularSequenceSpace.guess` the correct representation:: + + sage: from itertools import islice + sage: L = []; ps = 0 + sage: for s in islice(S, 110): + ....: ps += s + ....: L.append(ps) + sage: G = Seq2.guess(lambda n: L[n]) + sage: G + 2-regular sequence 1, 4, 10, 19, 31, 49, 67, 94, 118, 154, ... + sage: G.linear_representation() + ((1, 0, 0, 0), + Finite family {0: [ 0 1 0 0] + [ 0 0 0 1] + [ -5 5 1 0] + [ 10 -17 0 8], + 1: [ 0 0 1 0] + [ -5 3 3 0] + [ -5 0 6 0] + [-30 21 10 0]}, + (1, 1, 4, 1)) + sage: G.minimized().dimension() == G.dimension() + True + + Or we regenerate the sequence `S` first:: + + sage: S.regenerated().partial_sums(include_n=True, minimize=False) + 2-regular sequence 1, 4, 10, 19, 31, 49, 67, 94, 118, 154, ... + sage: S.regenerated().partial_sums(minimize=False) + 2-regular sequence 0, 1, 4, 10, 19, 31, 49, 67, 94, 118, ... + TESTS:: sage: E.linear_representation() @@ -994,6 +1331,63 @@ def _n_to_index_(self, n): except OverflowError: raise ValueError('value {} of index is negative'.format(n)) from None + def some_elements(self): + r""" + Return some elements of this `k`-regular sequence. + + See :class:`TestSuite` for a typical use case. + + OUTPUT: + + An iterator + + EXAMPLES:: + + sage: tuple(kRegularSequenceSpace(2, ZZ).some_elements()) + (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., + 2-regular sequence 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, ..., + 2-regular sequence 1, 1, 0, 1, -1, 0, 0, 1, -2, -1, ..., + 2-regular sequence 2, -1, 0, 0, 0, -1, 0, 0, 0, 0, ..., + 2-regular sequence 1, 1, 0, 1, 5, 0, 0, 1, -33, 5, ..., + 2-regular sequence -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, ..., + 2-regular sequence -59, -20, 0, -20, 0, 0, 0, -20, 0, 0, ..., + ... + 2-regular sequence 2210, 170, 0, 0, 0, 0, 0, 0, 0, 0, ...) + """ + return iter(element.regenerated() + for element + in super(kRegularSequenceSpace, self).some_elements( + allow_degenerated_sequence=True)) + + def _element_constructor_(self, *args, **kwds): + r""" + Return a `k`-regular sequence. + + See :class:`kRegularSequenceSpace` for details. + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True).regenerated() + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + """ + allow_degenerated_sequence = kwds.pop('allow_degenerated_sequence', False) + element = super(kRegularSequenceSpace, self)._element_constructor_(*args, **kwds) + if not allow_degenerated_sequence: + element._error_if_degenerated_() + return element + def guess(self, f, n_verify=100, max_exponent=10, sequence=None): r""" Guess a `k`-regular sequence whose first terms coincide with `(f(n))_{n\geq0}`. @@ -1057,7 +1451,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging - sage: logging.basicConfig(level=logging.INFO) + sage: logging.getLogger().setLevel(logging.INFO) sage: S1 = Seq2.guess(s); S1 INFO:...:including f_{1*m+0} INFO:...:including f_{2*m+1} @@ -1132,6 +1526,34 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 1: [0 1] [0 1]}, (0, 1)) + sage: logging.getLogger().setLevel(logging.WARN) + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + However, we can :meth:`~kRegularSequenceSpace.guess` a `2`-regular sequence of dimension `2`:: + + sage: G = Seq2.guess(lambda n: S[n]) + sage: G + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: G.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-2 3], + 1: [3 0] + [6 0]}, + (1, 1)) + + sage: G == S.regenerated() + True TESTS:: @@ -1160,7 +1582,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 1: [1]}, (1)) - We guess some partial sums sequences:: + We :meth:`~kRegularSequenceSpace.guess` some partial sums sequences:: sage: S = Seq2((Matrix([1]), Matrix([2])), vector([1]), vector([1])) sage: S @@ -1257,7 +1679,8 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): sage: A = Seq2( ....: (Matrix([[1, 1], [1, 1]]), Matrix([[1, 1], [1, 1]])), - ....: left=(1, 1), right=(1, 1)) + ....: left=(1, 1), right=(1, 1), + ....: allow_degenerated_sequence=True) sage: Seq2.guess(lambda n: n, sequence=A, n_verify=5) Traceback (most recent call last): ... @@ -1416,7 +1839,7 @@ def from_recurrence(self, *args, **kwds): r""" Construct the unique `k`-regular sequence which fulfills the given recurrence relations and initial values. The recurrence relations have to - have the specific shape of `k`-recursive sequences as described in [HKL2021]_, + have the specific shape of `k`-recursive sequences as described in [HKL2022]_, and are either given as symbolic equations, e.g., :: @@ -1465,7 +1888,7 @@ def from_recurrence(self, *args, **kwds): ``coefficient_ring``. The recurrence relations above uniquely determine a `k`-regular sequence; - see [HKL2021]_ for further information. + see [HKL2022]_ for further information. - ``function`` -- symbolic function ``f`` occurring in the equations @@ -1478,11 +1901,11 @@ def from_recurrence(self, *args, **kwds): following arguments are required: - ``M`` -- parameter of the recursive sequences, - see [HKL2021]_, Definition 3.1, as well as in the description of + see [HKL2022]_, Definition 3.1, as well as in the description of ``equations`` above - ``m`` -- parameter of the recursive sequences, - see [HKL2021]_, Definition 3.1, as well as in the description of + see [HKL2022]_, Definition 3.1, as well as in the description of ``equations`` above - ``coeffs`` -- a dictionary where ``coeffs[(r, j)]`` is the @@ -1500,7 +1923,7 @@ def from_recurrence(self, *args, **kwds): - ``inhomogeneities`` -- (default: ``{}``) a dictionary mapping integers ``r`` to the inhomogeneity `g_r` as given - in [HKL2021]_, Corollary D. All inhomogeneities have to be + in [HKL2022]_, Corollary D. All inhomogeneities have to be regular sequences from ``self`` or elements of ``coefficient_ring``. OUTPUT: a :class:`kRegularSequence` @@ -2568,13 +2991,13 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) OUTPUT: a namedtuple ``recurrence_rules`` consisting of - ``M``, ``m``, ``l``, ``u``, ``offset`` -- parameters of the recursive - sequences, see [HKL2021]_, Definition 3.1 + sequences, see [HKL2022]_, Definition 3.1 - ``ll``, ``uu``, ``n1``, ``dim`` -- parameters and dimension of the - resulting linear representation, see [HKL2021]_, Theorem A + resulting linear representation, see [HKL2022]_, Theorem A - ``coeffs`` -- a dictionary mapping ``(r, j)`` to the coefficients - `c_{r, j}` as given in [HKL2021]_, Equation (3.1). + `c_{r, j}` as given in [HKL2022]_, Equation (3.1). If ``coeffs[(r, j)]`` is not given for some ``r`` and ``j``, then it is assumed to be zero. @@ -2582,7 +3005,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) ``n``-th value of the sequence - ``inhomogeneities`` -- a dictionary mapping integers ``r`` - to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. + to the inhomogeneity `g_r` as given in [HKL2022]_, Corollary D. EXAMPLES:: @@ -2760,10 +3183,10 @@ def values(self, *, M, m, l, u, ll, coeffs, INPUT: - ``M``, ``m``, ``l``, ``u``, ``offset`` -- parameters of the - recursive sequences, see [HKL2021]_, Definition 3.1 + recursive sequences, see [HKL2022]_, Definition 3.1 - ``ll`` -- parameter of the resulting linear representation, - see [HKL2021]_, Theorem A + see [HKL2022]_, Theorem A - ``coeffs`` -- a dictionary where ``coeffs[(r, j)]`` is the coefficient `c_{r,j}` as given in :meth:`kRegularSequenceSpace.from_recurrence`. @@ -2777,7 +3200,7 @@ def values(self, *, M, m, l, u, ll, coeffs, determine the linear representation - ``inhomogeneities`` -- a dictionary mapping integers ``r`` - to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. + to the inhomogeneity `g_r` as given in [HKL2022]_, Corollary D. OUTPUT: @@ -2951,15 +3374,15 @@ def f(n): def ind(self, M, m, ll, uu): r""" Determine the index operator corresponding to the recursive - sequence as defined in [HKL2021]_. + sequence as defined in [HKL2022]_. INPUT: - ``M``, ``m`` -- parameters of the recursive sequences, - see [HKL2021]_, Definition 3.1 + see [HKL2022]_, Definition 3.1 - ``ll``, ``uu`` -- parameters of the resulting linear representation, - see [HKL2021]_, Theorem A + see [HKL2022]_, Theorem A OUTPUT: @@ -3015,7 +3438,7 @@ def ind(self, M, m, ll, uu): def shifted_inhomogeneities(self, recurrence_rules): r""" Return a dictionary of all needed shifted inhomogeneities as described - in the proof of Coroallary D in [HKL2021]_. + in the proof of Corollary D in [HKL2022]_. INPUT: @@ -3025,9 +3448,9 @@ def shifted_inhomogeneities(self, recurrence_rules): OUTPUT: A dictionary mapping `r` to the regular sequence - `\sum_i g_r(n + i)` for `g_r` as given in [HKL2021]_, Corollary D, + `\sum_i g_r(n + i)` for `g_r` as given in [HKL2022]_, Corollary D, and `i` between `\lfloor\ell'/k^{M}\rfloor` and - `\lfloor (k^{M-1} - k^{m} + u')/k^{M}\rfloor + 1`; see [HKL2021]_, + `\lfloor (k^{M-1} - k^{m} + u')/k^{M}\rfloor + 1`; see [HKL2022]_, proof of Corollary D. The first blocks of the corresponding vector-valued sequence (obtained from its linear representation) correspond to the sequences `g_r(n + i)` where @@ -3126,7 +3549,7 @@ def shifted_inhomogeneities(self, recurrence_rules): def v_eval_n(self, recurrence_rules, n): r""" - Return the vector `v(n)` as given in [HKL2021]_, Theorem A. + Return the vector `v(n)` as given in [HKL2022]_, Theorem A. INPUT: @@ -3195,7 +3618,7 @@ def matrix(self, recurrence_rules, rem, correct_offset=True): - ``correct_offset`` -- (default: ``True``) a boolean. If ``True``, then the resulting linear representation has no - offset. See [HKL2021]_ for more information. + offset. See [HKL2022]_ for more information. OUTPUT: a matrix diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5596216845a..e5856aacef9 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -318,6 +318,12 @@ def minimize_result(operation): if ``False``, then not. If this argument is ``None``, then the default specified by the parent's ``minimize_results`` is used. + .. NOTE:: + + If the result of ``operation`` is ``self``, then minimization is + not applied unless ``minimize=True`` is explicitly set, + in particular, independent of the parent's ``minimize_results``. + TESTS:: sage: from sage.combinat.recognizable_series import minimize_result @@ -352,14 +358,39 @@ def minimize_result(operation): some result minimized sage: S('some').operation(minimize=False) some result + + :: + + sage: class T(S): + ....: @minimize_result + ....: def nooperation(self): + ....: return self + sage: t = T('some') + sage: p.minimize_results = True + sage: t.nooperation() is t + True + sage: t.nooperation(minimize=True) is t + False + sage: t.nooperation(minimize=False) is t + True + sage: p.minimize_results = False + sage: t.nooperation() is t + True + sage: t.nooperation(minimize=True) is t + False + sage: t.nooperation(minimize=False) is t + True """ @wraps(operation) def minimized(self, *args, **kwds): minimize = kwds.pop('minimize', None) - if minimize is None: - minimize = self.parent().minimize_results result = operation(self, *args, **kwds) + if minimize is not True and result is self: + return result + + if minimize is None: + minimize = self.parent().minimize_results if minimize: result = result.minimized() @@ -1045,9 +1076,11 @@ def tr(M): T = M.transpose() T.set_immutable() return T - return self.parent()(self.mu.map(tr), - left=self.right, - right=self.left) + + P = self.parent() + return P.element_class(P, self.mu.map(tr), + left=self.right, + right=self.left) @cached_method def minimized(self): @@ -1871,12 +1904,16 @@ def _an_element_(self): for i, _ in enumerate(self.alphabet())), vector([z, e]), right=vector([e, z])) - def some_elements(self): + def some_elements(self, **kwds): r""" Return some elements of this recognizable series space. See :class:`TestSuite` for a typical use case. + INPUT: + + - ``kwds`` are passed on to the element constructor + OUTPUT: An iterator @@ -1913,7 +1950,7 @@ def some_elements(self): LR = list(islice(elements_V, 2)) if len(mu) != k or len(LR) != 2: break - yield self(mu, *LR) + yield self(mu, *LR, **kwds) @cached_method def one_hadamard(self):