From c92f2fe70279f834b4edd2927f445527cdfc19ec Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 8 Aug 2016 17:31:52 +0200 Subject: [PATCH 01/58] method for guessing k-regular sequences --- src/sage/combinat/k_regular_sequence.py | 175 ++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ad016982083..c03ef5cc7f0 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -174,3 +174,178 @@ def __init__(self, k, base, category=None): def _repr_(self): return 'Set of {}-regular sequences over {}'.format(self.k, self.base()) + + def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None, + verbose=False): + r""" + + EXAMPLES: + + Binary sum of digits:: + + sage: @cached_function + ....: def s(n): + ....: if n == 0: + ....: return 0 + ....: return s(n//2) + ZZ(is_odd(n)) + sage: all(s(n) == sum(n.digits(2)) for n in srange(10)) + True + sage: [s(n) for n in srange(10)] + [0, 1, 1, 2, 1, 2, 2, 3, 1, 2] + + :: + + sage: from sage.combinat.k_regular_sequence import kRegularSequences + sage: Seq2 = kRegularSequences(2, ZZ) + sage: S1 = Seq2.guess(s, verbose=True) + including f_{1*m+0} + M_0: f_{2*m+0} = (1) * X_m + including f_{2*m+1} + M_1: f_{2*m+1} = (0, 1) * X_m + M_0: f_{4*m+1} = (0, 1) * X_m + M_1: f_{4*m+3} = (-1, 2) * X_m + sage: S1.info() + matrices: + ( + [1 0] [ 0 -1] + [0 1], [ 1 2] + ) + initial: + (0, 1) + selection: + (1, 0) + + :: + + sage: C = Seq2((Matrix([[1]]), Matrix([[1]])), vector([1]), vector([1])); C + 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... + sage: S2 = Seq2.guess(s, sequence=C) + sage: S2.info() + matrices: + ( + [1 0] [1 1] + [0 1], [0 1] + ) + initial: + (1, 0) + selection: + (0, 1) + + TESTS:: + + sage: Seq2.guess(lambda n: 2, sequence=C).info() + matrices: + ([1], [1]) + initial: + (1) + selection: + (2) + """ + from sage.arith.srange import srange, xsrange + from sage.matrix.constructor import Matrix + from sage.misc.mrange import cantor_product + from sage.modules.free_module_element import vector + + k = self.k + if n_max is None: + n_max = 100 + if d_max is None: + d_max = 10 + if domain is None: + domain = self.base() # TODO + if sequence is None: + matrices = [[] for _ in srange(k)] + class ES(object): + def __getitem__(self, m): + return tuple() + sequence = ES() + else: + matrices = [M.rows() for M in sequence.matrices] + sequence = sequence.parent()(sequence.matrices, initial=sequence.initial) + + zero = domain(0) + one = domain(1) + + def values(m, lines): + return tuple(sequence[m]) + tuple(f(k**t_R * m + r_R) for t_R, r_R, s_R in lines) + + @cached_function(key=lambda lines: len(lines)) # we assume that existing lines are not changed (we allow appending of new lines) + def some_inverse_U_matrix(lines): + d = len(sequence[0]) + len(lines) + + for m_indices in cantor_product(xsrange(n_max), repeat=d, min_slope=1): + U = Matrix(domain, d, d, [values(m, lines) for m in m_indices]).transpose() + try: + return U.inverse(), m_indices + except ZeroDivisionError: + pass + else: + raise RuntimeError + + def guess_linear_dependence(t_L, r_L, lines): + iU, m_indices = some_inverse_U_matrix(lines) + X_L = vector(f(k**t_L * m + r_L) for m in m_indices) + return X_L * iU + + def verify_linear_dependence(t_L, r_L, linear_dependence, lines): + return all(f(k**t_L * m + r_L) == + linear_dependence * vector(values(m, lines)) + for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) + + def find_linear_dependence(t_L, r_L, lines): + linear_dependence = guess_linear_dependence(t_L, r_L, lines) + if not verify_linear_dependence(t_L, r_L, linear_dependence, lines): + raise ValueError + return linear_dependence + + selection = None + if sequence[0]: + try: + solution = find_linear_dependence(0, 0, []) + except ValueError: + pass + else: + selection = vector(solution) + + to_branch = [] + lines = [] + def include(line): + to_branch.append(line) + lines.append(line) + if verbose: + t, r, s = line + print('including f_{{{}*m+{}}}'.format(k**t, r)) + + if selection is None: + line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s + include(line_L) + selection = vector((len(sequence[0]) + len(lines)-1)*(zero,) + (one,)) + + while to_branch: + line_R = to_branch.pop(0) + t_R, r_R, s_R = line_R + if t_R >= d_max: + raise RuntimeError + + t_L = t_R + 1 + for s_L in srange(k): + r_L = k**t_R * s_L + r_R + line_L = t_L, r_L, s_L + + try: + solution = find_linear_dependence(t_L, r_L, lines) + except ValueError: + include(line_L) + solution = (len(lines)-1)*(zero,) + (one,) + if verbose: + # Using sage.misc.misc.verbose also prints all inversions + # in FLINT at level 1; thus not what we want + print('M_{}: f_{{{}*m+{}}} = {} * X_m'.format(s_L, k**t_L, r_L, solution)) + matrices[s_L].append(solution) + + d = len(sequence[0]) + len(lines) + matrices = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]).transpose() + for M in matrices) + initial = vector(values(0, lines)) + selection = vector(pad_right(tuple(selection), d, zero=zero)) + return self(matrices, initial, selection) From 8cc46116eaf6a9dabaeb7f3a39bb75c45908217d Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 9 Aug 2016 17:41:45 +0200 Subject: [PATCH 02/58] function "value" --- src/sage/combinat/k_regular_sequence.py | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index c03ef5cc7f0..db1ae6685a5 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -52,6 +52,33 @@ def pad_right(T, length, zero=0): return T + type(T)(zero for _ in xrange(length - len(T))) +def value(D, k): + r""" + Return the value of the expansion with digits `D` in base `k`, i.e. + + .. MATH:: + + \sum_{0\leq j < \operator{len}D} D[j] k^j. + + INPUT: + + - ``D`` -- a tuple or other iterable. + + - ``k`` -- the base. + + OUTPUT: + + An element in the common parent of the base `k` and of the entries + of `D`. + + EXAMPLES:: + + sage: from sage.combinat.k_regular_sequence import value + sage: value(42.digits(7), 7) + 42 + """ + return sum(d * k**j for j, d in enumerate(D)) + from sage.structure.element import Element class kRegularSequence(Element): From f91a85017e59c43651172668576c84aaaf95cc8b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 9 Aug 2016 17:41:57 +0200 Subject: [PATCH 03/58] function "split_interlace" --- src/sage/combinat/k_regular_sequence.py | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index db1ae6685a5..8bfe3c2e22a 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -79,6 +79,54 @@ def value(D, k): """ return sum(d * k**j for j, d in enumerate(D)) + +def split_interlace(n, k, p): + r""" + Split each digit in the `k`-ary expansion of `n` into `p` parts and + return the value of the expansion obtained by each of these parts. + + INPUT: + + - ``n`` -- an integer. + + - ``k`` -- an integer specifying the base. + + - ``p`` -- a positive integer specifying in how many parts + the input ``n`` is split. This has to be a divisor of ``k``. + + OUTPUT: + + A tuple of integers. + + EXAMPLES:: + + sage: from sage.combinat.k_regular_sequence import split_interlace + sage: [(n, split_interlace(n, 4, 2)) for n in srange(20)] + [(0, (0, 0)), (1, (1, 0)), (2, (0, 1)), (3, (1, 1)), + (4, (2, 0)), (5, (3, 0)), (6, (2, 1)), (7, (3, 1)), + (8, (0, 2)), (9, (1, 2)), (10, (0, 3)), (11, (1, 3)), + (12, (2, 2)), (13, (3, 2)), (14, (2, 3)), (15, (3, 3)), + (16, (4, 0)), (17, (5, 0)), (18, (4, 1)), (19, (5, 1))] + sage: [(n, split_interlace(n, 6, 3)) for n in srange(9)] + [(0, (0, 0, 0)), (1, (1, 0, 0)), (2, (0, 1, 0)), + (3, (1, 1, 0)), (4, (0, 0, 1)), (5, (1, 0, 1)), + (6, (2, 0, 0)), (7, (3, 0, 0)), (8, (2, 1, 0))] + + TESTS:: + + sage: split_interlace(42, 4, 3) + Traceback (most recent call last): + ... + ValueError: p=3 is not a divisor of k=4. + """ + if k % p != 0: + raise ValueError('p={} is not a divisor of k={}.'.format(p, k)) + ki = k // p + return tuple(value(D, ki) + for D in zip(*(d.digits(ki, padto=p) + for d in n.digits(k, padto=1)))) + + from sage.structure.element import Element class kRegularSequence(Element): From a755c2223ed15b30763b98b6424c160670a9b935 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 9 Aug 2016 17:42:17 +0200 Subject: [PATCH 04/58] one linebreak to avoid a long line --- src/sage/combinat/k_regular_sequence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 8bfe3c2e22a..352dfe22247 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -336,7 +336,8 @@ def __getitem__(self, m): sequence = ES() else: matrices = [M.rows() for M in sequence.matrices] - sequence = sequence.parent()(sequence.matrices, initial=sequence.initial) + sequence = sequence.parent()(sequence.matrices, + initial=sequence.initial) zero = domain(0) one = domain(1) From 0ed52548af5917bd43d449de7400af51fbb8cb96 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 9 Aug 2016 17:43:56 +0200 Subject: [PATCH 05/58] docstring of pad_right --- src/sage/combinat/k_regular_sequence.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 352dfe22247..289203c0628 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -37,15 +37,34 @@ import itertools from sage.misc.cachefunc import cached_function, cached_method + def pad_right(T, length, zero=0): r""" - TESTS:: + Pad ``T`` to the right by ``zero``s to have + at least the given ``length``. + + INPUT: + + - ``T`` -- A tuple, list or other iterable. + + - ``length`` -- a nonnegative integer. + + - ``zero`` -- (default: ``0``) the elements to pad with. + + OUTPUT: + + An object of the same type as ``T``. + + EXAMPLES:: sage: from sage.combinat.k_regular_sequence import pad_right sage: pad_right((1,2,3), 10) (1, 2, 3, 0, 0, 0, 0, 0, 0, 0) sage: pad_right((1,2,3), 2) (1, 2, 3) + + TESTS:: + sage: pad_right([1,2,3], 10) [1, 2, 3, 0, 0, 0, 0, 0, 0, 0] """ From 99799a9e37102f69eaf120261747db253cb144a6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 10 Aug 2016 09:46:59 +0200 Subject: [PATCH 06/58] switch to Python's logging --- src/sage/combinat/k_regular_sequence.py | 34 +++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 289203c0628..07196822171 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -269,8 +269,7 @@ def _repr_(self): return 'Set of {}-regular sequences over {}'.format(self.k, self.base()) - def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None, - verbose=False): + def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): r""" EXAMPLES: @@ -291,13 +290,15 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None, sage: from sage.combinat.k_regular_sequence import kRegularSequences sage: Seq2 = kRegularSequences(2, ZZ) - sage: S1 = Seq2.guess(s, verbose=True) - including f_{1*m+0} - M_0: f_{2*m+0} = (1) * X_m - including f_{2*m+1} - M_1: f_{2*m+1} = (0, 1) * X_m - M_0: f_{4*m+1} = (0, 1) * X_m - M_1: f_{4*m+3} = (-1, 2) * X_m + sage: import logging + sage: logging.basicConfig(level=logging.INFO) + sage: S1 = Seq2.guess(s) + INFO:...:including f_{1*m+0} + INFO:...:M_0: f_{2*m+0} = (1) * X_m + INFO:...:including f_{2*m+1} + INFO:...:M_1: f_{2*m+1} = (0, 1) * X_m + INFO:...:M_0: f_{4*m+1} = (0, 1) * X_m + INFO:...:M_1: f_{4*m+3} = (-1, 2) * X_m sage: S1.info() matrices: ( @@ -308,6 +309,7 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None, (0, 1) selection: (1, 0) + sage: logging.shutdown(); _ = reload(logging) :: @@ -335,6 +337,9 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None, selection: (2) """ + import logging + logger = logging.getLogger(__name__) + from sage.arith.srange import srange, xsrange from sage.matrix.constructor import Matrix from sage.misc.mrange import cantor_product @@ -407,9 +412,8 @@ def find_linear_dependence(t_L, r_L, lines): def include(line): to_branch.append(line) lines.append(line) - if verbose: - t, r, s = line - print('including f_{{{}*m+{}}}'.format(k**t, r)) + t, r, s = line + logger.info('including f_{%s*m+%s}', k**t, r) if selection is None: line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s @@ -432,10 +436,8 @@ def include(line): except ValueError: include(line_L) solution = (len(lines)-1)*(zero,) + (one,) - if verbose: - # Using sage.misc.misc.verbose also prints all inversions - # in FLINT at level 1; thus not what we want - print('M_{}: f_{{{}*m+{}}} = {} * X_m'.format(s_L, k**t_L, r_L, solution)) + logger.info('M_%s: f_{%s*m+%s} = %s * X_m', + s_L, k**t_L, r_L, solution) matrices[s_L].append(solution) d = len(sequence[0]) + len(lines) From cc14b636ab1d535fc81e212a1b2b5cb43d67a82f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 10 Aug 2016 13:50:35 +0200 Subject: [PATCH 07/58] =?UTF-8?q?post-merge=20=C3=84nderungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/combinat/k_regular_sequence.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 19c03a72dc3..e4f2d3717f3 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -304,8 +304,7 @@ def info(self): EXAMPLES:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) - sage: Seq2((Matrix([[1, 0], [0, 1]]), Matrix([[0, -1], [1, 2]])), - ....: initial=vector([0, 1]), selection=vector([1, 0])).info() + sage: Seq2.guess(lambda n: sum(n.digits(2))).info() matrices: ( [1 0] [ 0 -1] @@ -444,8 +443,7 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): :: - sage: from sage.combinat.k_regular_sequence import kRegularSequences - sage: Seq2 = kRegularSequences(2, ZZ) + sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging sage: logging.basicConfig(level=logging.INFO) sage: S1 = Seq2.guess(s) From c349170d049c006fdba809edaf623a2497e647e0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 21 Aug 2016 18:50:38 +0200 Subject: [PATCH 08/58] fix code and doctests to adapt with upstream recognizable series --- src/sage/combinat/k_regular_sequence.py | 56 ++++++++++--------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 1e1ff5752f5..933c3e4bd66 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -477,16 +477,11 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): INFO:...:M_1: f_{2*m+1} = (0, 1) * X_m INFO:...:M_0: f_{4*m+1} = (0, 1) * X_m INFO:...:M_1: f_{4*m+3} = (-1, 2) * X_m - sage: S1.info() - matrices: + sage: S1.mu[0], S1.mu[1], S1.left, S1.right ( [1 0] [ 0 -1] - [0 1], [ 1 2] + [0 1], [ 1 2], (0, 1), (1, 0) ) - initial: - (0, 1) - selection: - (1, 0) sage: logging.shutdown(); _ = reload(logging) :: @@ -494,26 +489,17 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): sage: C = Seq2((Matrix([[1]]), Matrix([[1]])), vector([1]), vector([1])); C 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... sage: S2 = Seq2.guess(s, sequence=C) - sage: S2.info() - matrices: + sage: S2.mu[0], S2.mu[1], S2.left, S2.right ( [1 0] [1 1] - [0 1], [0 1] + [0 1], [0 1], (1, 0), (0, 1) ) - initial: - (1, 0) - selection: - (0, 1) TESTS:: - sage: Seq2.guess(lambda n: 2, sequence=C).info() - matrices: - ([1], [1]) - initial: - (1) - selection: - (2) + sage: S = Seq2.guess(lambda n: 2, sequence=C) + sage: S.mu[0], S.mu[1], S.left, S.right + ([1], [1], (1), (2)) """ import logging logger = logging.getLogger(__name__) @@ -531,15 +517,15 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): if domain is None: domain = self.base() # TODO if sequence is None: - matrices = [[] for _ in srange(k)] + mu = [[] for _ in srange(k)] class ES(object): def __getitem__(self, m): return tuple() sequence = ES() else: - matrices = [M.rows() for M in sequence.matrices] - sequence = sequence.parent()(sequence.matrices, - initial=sequence.initial) + mu = [M.rows() for M in sequence.mu] + sequence = sequence.parent()(sequence.mu, + left=sequence.left) zero = domain(0) one = domain(1) @@ -576,14 +562,14 @@ def find_linear_dependence(t_L, r_L, lines): raise ValueError return linear_dependence - selection = None + right = None if sequence[0]: try: solution = find_linear_dependence(0, 0, []) except ValueError: pass else: - selection = vector(solution) + right = vector(solution) to_branch = [] lines = [] @@ -593,10 +579,10 @@ def include(line): t, r, s = line logger.info('including f_{%s*m+%s}', k**t, r) - if selection is None: + if right is None: line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s include(line_L) - selection = vector((len(sequence[0]) + len(lines)-1)*(zero,) + (one,)) + right = vector((len(sequence[0]) + len(lines)-1)*(zero,) + (one,)) while to_branch: line_R = to_branch.pop(0) @@ -616,11 +602,11 @@ def include(line): solution = (len(lines)-1)*(zero,) + (one,) logger.info('M_%s: f_{%s*m+%s} = %s * X_m', s_L, k**t_L, r_L, solution) - matrices[s_L].append(solution) + mu[s_L].append(solution) d = len(sequence[0]) + len(lines) - matrices = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]).transpose() - for M in matrices) - initial = vector(values(0, lines)) - selection = vector(pad_right(tuple(selection), d, zero=zero)) - return self(matrices, initial, selection) + mu = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]).transpose() + for M in mu) + left = vector(values(0, lines)) + right = vector(pad_right(tuple(right), d, zero=zero)) + return self(mu, left, right) From 355d24d78fa341fed083a2ac74c6c39ab5e511cb Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 15:16:46 +0200 Subject: [PATCH 09/58] simplify code by using features of recognizable series better (and adapting to removing None-sense) --- src/sage/combinat/k_regular_sequence.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ba04a84d05f..99c3f92729e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -559,24 +559,21 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): domain = self.base() # TODO if sequence is None: mu = [[] for _ in srange(k)] - class ES(object): - def __getitem__(self, m): - return tuple() - sequence = ES() + seq = lambda m: tuple() else: mu = [M.rows() for M in sequence.mu] - sequence = sequence.parent()(sequence.mu, - left=sequence.left) + seq = lambda m: sequence.left * sequence._mu_of_word_( + self._n_to_index_(m)) zero = domain(0) one = domain(1) def values(m, lines): - return tuple(sequence[m]) + tuple(f(k**t_R * m + r_R) for t_R, r_R, s_R in lines) + return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R, s_R in lines) @cached_function(key=lambda lines: len(lines)) # we assume that existing lines are not changed (we allow appending of new lines) def some_inverse_U_matrix(lines): - d = len(sequence[0]) + len(lines) + d = len(seq(0)) + len(lines) for m_indices in cantor_product(xsrange(n_max), repeat=d, min_slope=1): U = Matrix(domain, d, d, [values(m, lines) for m in m_indices]).transpose() @@ -604,7 +601,7 @@ def find_linear_dependence(t_L, r_L, lines): return linear_dependence right = None - if sequence[0]: + if seq(0): try: solution = find_linear_dependence(0, 0, []) except ValueError: @@ -623,7 +620,7 @@ def include(line): if right is None: line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s include(line_L) - right = vector((len(sequence[0]) + len(lines)-1)*(zero,) + (one,)) + right = vector((len(seq(0)) + len(lines)-1)*(zero,) + (one,)) while to_branch: line_R = to_branch.pop(0) @@ -645,7 +642,7 @@ def include(line): s_L, k**t_L, r_L, solution) mu[s_L].append(solution) - d = len(sequence[0]) + len(lines) + d = len(seq(0)) + len(lines) mu = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]).transpose() for M in mu) left = vector(values(0, lines)) From 348fcff54a0ab067f37f1274b4738f6b439fdf06 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 17:22:35 +0200 Subject: [PATCH 10/58] solve transpositioning problem --- src/sage/combinat/k_regular_sequence.py | 56 ++++++++++++++++++------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 99c3f92729e..fecf47b171a 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -506,7 +506,7 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): sage: [s(n) for n in srange(10)] [0, 1, 1, 2, 1, 2, 2, 3, 1, 2] - :: + Variant 1:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging @@ -518,29 +518,57 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): INFO:...:M_1: f_{2*m+1} = (0, 1) * X_m INFO:...:M_0: f_{4*m+1} = (0, 1) * X_m INFO:...:M_1: f_{4*m+3} = (-1, 2) * X_m + sage: S1 + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: S1.mu[0], S1.mu[1], S1.left, S1.right ( - [1 0] [ 0 -1] - [0 1], [ 1 2], (0, 1), (1, 0) + [1 0] [ 0 1] + [0 1], [-1 2], (1, 0), (0, 1) ) sage: logging.shutdown(); _ = reload(logging) - :: + Variant 2:: sage: C = Seq2((Matrix([[1]]), Matrix([[1]])), vector([1]), vector([1])); C 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... sage: S2 = Seq2.guess(s, sequence=C) + sage: S2 + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: S2.mu[0], S2.mu[1], S2.left, S2.right ( - [1 0] [1 1] - [0 1], [0 1], (1, 0), (0, 1) + [1 0] [1 0] + [0 1], [1 1], (0, 1), (1, 0) + ) + + The sequence of all natural numbers:: + + sage: S = Seq2.guess(lambda n: n) + sage: S + 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + sage: S.mu[0], S.mu[1], S.left, S.right + ( + [2 0] [ 0 1] + [2 1], [-2 3], (1, 0), (0, 1) + ) + + The indicator function of the even integers:: + + sage: S = Seq2.guess(lambda n: ZZ(is_even(n))) + sage: S + 2-regular sequence 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, ... + sage: S.mu[0], S.mu[1], S.left, S.right + ( + [0 1] [0 0] + [0 1], [0 1], (1, 0), (1, 1) ) TESTS:: sage: S = Seq2.guess(lambda n: 2, sequence=C) + sage: S + 2-regular sequence 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ... sage: S.mu[0], S.mu[1], S.left, S.right - ([1], [1], (1), (2)) + ([1], [1], (2), (1)) """ import logging logger = logging.getLogger(__name__) @@ -600,14 +628,14 @@ def find_linear_dependence(t_L, r_L, lines): raise ValueError return linear_dependence - right = None + left = None if seq(0): try: solution = find_linear_dependence(0, 0, []) except ValueError: pass else: - right = vector(solution) + left = vector(solution) to_branch = [] lines = [] @@ -617,10 +645,10 @@ def include(line): t, r, s = line logger.info('including f_{%s*m+%s}', k**t, r) - if right is None: + if left is None: line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s include(line_L) - right = vector((len(seq(0)) + len(lines)-1)*(zero,) + (one,)) + left = vector((len(seq(0)) + len(lines)-1)*(zero,) + (one,)) while to_branch: line_R = to_branch.pop(0) @@ -643,8 +671,8 @@ def include(line): mu[s_L].append(solution) d = len(seq(0)) + len(lines) - mu = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]).transpose() + mu = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]) for M in mu) - left = vector(values(0, lines)) - right = vector(pad_right(tuple(right), d, zero=zero)) + right = vector(values(0, lines)) + left = vector(pad_right(tuple(left), d, zero=zero)) return self(mu, left, right) From 2e99c5c260bd1be7d33b7e5afa70c3ff40af767c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 22 Aug 2016 17:26:23 +0200 Subject: [PATCH 11/58] another example --- src/sage/combinat/k_regular_sequence.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index fecf47b171a..d60db88e04e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -562,6 +562,17 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): [0 1], [0 1], (1, 0), (1, 1) ) + The indicator function of the odd integers:: + + sage: S = Seq2.guess(lambda n: ZZ(is_odd(n))) + sage: S + 2-regular sequence 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ... + sage: S.mu[0], S.mu[1], S.left, S.right + ( + [0 0] [0 1] + [0 1], [0 1], (1, 0), (0, 1) + ) + TESTS:: sage: S = Seq2.guess(lambda n: 2, sequence=C) From 3e955e10fa460071a074414bf1328db307b190b6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 25 Aug 2016 11:31:32 +0200 Subject: [PATCH 12/58] guessing partial sums --- src/sage/combinat/k_regular_sequence.py | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 41909f9d67f..933547ea849 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -579,6 +579,62 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): 2-regular sequence 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ... sage: S.mu[0], S.mu[1], S.left, S.right ([1], [1], (2), (1)) + + :: + + sage: S = Seq2((Matrix([1]), Matrix([2])), vector([1]), vector([1])) + sage: S + 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 + sage: G.mu[0], G.mu[1], G.left, G.right + + :: + + sage: S = Seq2((Matrix([1]), Matrix([3])), vector([1]), vector([1])) + sage: S + 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 + sage: G.mu[0], G.mu[1], G.left, G.right + + :: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) + sage: S + 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 + sage: G.mu[0], G.mu[1], G.left, G.right + sage: H = G.minimized() + sage: H.mu[0], H.mu[1], H.left, H.right + + :: + + sage: Seq3 = kRegularSequenceSpace(3, QQ) + sage: S = Seq3((Matrix([1]), Matrix([3]), Matrix([2])), vector([1]), vector([1])) + sage: S + sage: from itertools import islice + sage: L = []; ps = 0 + sage: for s in islice(S, 110): + ....: ps += s + ....: L.append(ps) + sage: G = Seq3.guess(lambda n: L[n]) + sage: G + sage: G.mu[0], G.mu[1], G.mu[2], G.left, G.right + """ import logging logger = logging.getLogger(__name__) From 90e4ed536436d33389a8f93ca5b9092e88b2d188 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 25 Aug 2016 19:28:40 +0200 Subject: [PATCH 13/58] doctests --- src/sage/combinat/k_regular_sequence.py | 47 +++++++++---------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 8347f2ed4a5..4ae4e1318fe 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -992,10 +992,11 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): sage: S.mu[0], S.mu[1], S.left, S.right ([1], [1], (2), (1)) - :: + We guess some partial sums sequences:: sage: S = Seq2((Matrix([1]), Matrix([2])), vector([1]), vector([1])) sage: S + 2-regular sequence 1, 2, 2, 4, 2, 4, 4, 8, 2, 4, ... sage: from itertools import islice sage: L = []; ps = 0 sage: for s in islice(S, 110): @@ -1003,41 +1004,21 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): ....: L.append(ps) sage: G = Seq2.guess(lambda n: L[n]) sage: G + 2-regular sequence 1, 3, 5, 9, 11, 15, 19, 27, 29, 33, ... sage: G.mu[0], G.mu[1], G.left, G.right - - :: - - sage: S = Seq2((Matrix([1]), Matrix([3])), vector([1]), vector([1])) - sage: S - 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 - sage: G.mu[0], G.mu[1], G.left, G.right - - :: - - sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) - sage: S - 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 - sage: G.mu[0], G.mu[1], G.left, G.right - sage: H = G.minimized() - sage: H.mu[0], H.mu[1], H.left, H.right + ( + [ 0 1] [3 0] + [-3 4], [3 2], (1, 0), (1, 1) + ) + sage: G == S.partial_sums(include_n=True) + True :: sage: Seq3 = kRegularSequenceSpace(3, QQ) sage: S = Seq3((Matrix([1]), Matrix([3]), Matrix([2])), vector([1]), vector([1])) sage: S + 3-regular sequence 1, 3, 2, 3, 9, 6, 2, 6, 4, 3, ... sage: from itertools import islice sage: L = []; ps = 0 sage: for s in islice(S, 110): @@ -1045,8 +1026,14 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): ....: L.append(ps) sage: G = Seq3.guess(lambda n: L[n]) sage: G + 3-regular sequence 1, 4, 6, 9, 18, 24, 26, 32, 36, 39, ... sage: G.mu[0], G.mu[1], G.mu[2], G.left, G.right - + ( + [ 0 1] [18/5 2/5] [ 6 0] + [-6 7], [18/5 27/5], [24 2], (1, 0), (1, 1) + ) + sage: G == S.partial_sums(include_n=True) + True """ import logging logger = logging.getLogger(__name__) From 899e56edeb5652be9130f9f48ddb8d63d3bf5584 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 25 Aug 2016 20:19:47 +0200 Subject: [PATCH 14/58] finish .guess (docstrings etc) --- src/sage/combinat/k_regular_sequence.py | 31 ++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 4ae4e1318fe..60b7a1b9fc4 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -900,8 +900,28 @@ def _n_to_index_(self, n): return W(n.digits(self.k)) - def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): + def guess(self, f, n_max=100, max_dimension=10, sequence=None): r""" + Guess a `k`-regular sequence of `(f(n))_{n\geq0}`. + + INPUT: + + - ``f`` -- a function (callable) which determines the sequence. + It takes nonnegative integers as an input. + + - ``n_max`` -- (default: ``100``) a positive integer. The resulting + `k`-regular sequence coincides with `f` on the first ``n_max`` + terms. + + - ``max_dimension`` -- (default: ``10``) a positive integer specifying + the maxium dimension which is tried when guessing the sequence. + + - ``sequence`` -- (default: ``None``) a `k`-regular sequence used + for bootstrapping this guessing. + + OUTPUT: + + A :class:`kRegularSequence`. EXAMPLES: @@ -1044,12 +1064,7 @@ def guess(self, f, n_max=None, d_max=None, domain=None, sequence=None): from sage.modules.free_module_element import vector k = self.k - if n_max is None: - n_max = 100 - if d_max is None: - d_max = 10 - if domain is None: - domain = self.base() # TODO + domain = self.coefficients() if sequence is None: mu = [[] for _ in srange(k)] seq = lambda m: tuple() @@ -1118,7 +1133,7 @@ def include(line): while to_branch: line_R = to_branch.pop(0) t_R, r_R, s_R = line_R - if t_R >= d_max: + if t_R >= max_dimension: raise RuntimeError t_L = t_R + 1 From dcc7cd10d93039d84503d0128c3925b0c17adff0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 11 May 2021 16:45:57 +0200 Subject: [PATCH 15/58] Trac #21204: cherry-pick to avoid merge conflict --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index b2647f6c1cb..edbd8625570 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -883,7 +883,7 @@ def _n_to_index_(self, n): try: return W(n.digits(self.k)) except OverflowError: - raise ValueError('value {} of index is negative'.format(n)) + raise ValueError('value {} of index is negative'.format(n)) from None def guess(self, f, n_max=100, max_dimension=10, sequence=None): r""" From c0519f07490f7f6114765a12a72ced1956f811b7 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 11 May 2021 16:51:50 +0200 Subject: [PATCH 16/58] Trac #21204: fixup code and tests --- src/sage/combinat/k_regular_sequence.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index edbd8625570..6437556ce86 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -941,6 +941,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): [1 0] [ 0 1] [0 1], [-1 2], (1, 0), (0, 1) ) + + sage: from importlib import reload sage: logging.shutdown(); _ = reload(logging) Variant 2:: @@ -1049,7 +1051,7 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): from sage.modules.free_module_element import vector k = self.k - domain = self.coefficients() + domain = self.coefficient_ring() if sequence is None: mu = [[] for _ in srange(k)] seq = lambda m: tuple() From 00047cea6b8830b154d8b8954ceaecd24d32ab20 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 25 Jun 2021 15:03:59 +0200 Subject: [PATCH 17/58] Trac #21204: fix punctuation --- src/sage/combinat/k_regular_sequence.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a16be0a94f6..9667834a11e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -143,14 +143,14 @@ def value(D, k): INPUT: - - ``D`` -- a tuple or other iterable. + - ``D`` -- a tuple or other iterable - - ``k`` -- the base. + - ``k`` -- the base OUTPUT: An element in the common parent of the base `k` and of the entries - of `D`. + of `D` EXAMPLES:: @@ -168,16 +168,16 @@ def split_interlace(n, k, p): INPUT: - - ``n`` -- an integer. + - ``n`` -- an integer - - ``k`` -- an integer specifying the base. + - ``k`` -- an integer specifying the base - ``p`` -- a positive integer specifying in how many parts the input ``n`` is split. This has to be a divisor of ``k``. OUTPUT: - A tuple of integers. + A tuple of integers EXAMPLES:: @@ -896,21 +896,21 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): INPUT: - ``f`` -- a function (callable) which determines the sequence. - It takes nonnegative integers as an input. + It takes nonnegative integers as an input - ``n_max`` -- (default: ``100``) a positive integer. The resulting `k`-regular sequence coincides with `f` on the first ``n_max`` - terms. + terms - ``max_dimension`` -- (default: ``10``) a positive integer specifying - the maxium dimension which is tried when guessing the sequence. + the maxium dimension which is tried when guessing the sequence - ``sequence`` -- (default: ``None``) a `k`-regular sequence used - for bootstrapping this guessing. + for bootstrapping this guessing OUTPUT: - A :class:`kRegularSequence`. + A :class:`kRegularSequence` EXAMPLES: From 25366008bb9543067d2f366764727c6f891fc2ce Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Sun, 2 Oct 2022 20:48:17 +0200 Subject: [PATCH 18/58] Fix pycodestyle issue E306 expected 1 blank line before a nested definition, found 0 sage/combinat/k_regular_sequence.py:1251:9: E306 --- src/sage/combinat/k_regular_sequence.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index fe97cfad09d..86f1dea7902 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1247,6 +1247,7 @@ def find_linear_dependence(t_L, r_L, lines): to_branch = [] lines = [] + def include(line): to_branch.append(line) lines.append(line) From bb32fee0e3492170a749a8f31c7078a71433ed77 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 11 Oct 2022 12:54:12 +0200 Subject: [PATCH 19/58] Trac #21204: Fix typo --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 86f1dea7902..be4d7a14279 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1040,7 +1040,7 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): terms - ``max_dimension`` -- (default: ``10``) a positive integer specifying - the maxium dimension which is tried when guessing the sequence + the maximum dimension which is tried when guessing the sequence - ``sequence`` -- (default: ``None``) a `k`-regular sequence used for bootstrapping this guessing From 4792a8f41cf37b1b702e2f44dbff3bf8d06b01a0 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 11 Oct 2022 12:55:06 +0200 Subject: [PATCH 20/58] Add ALGORITHM section to docstring --- src/sage/combinat/k_regular_sequence.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index be4d7a14279..e8700268ab5 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1049,6 +1049,25 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): A :class:`kRegularSequence` + ALGORITHM: + + For the purposes of this description, the left vector valued sequence + associated with a regular sequence is consists of the left vector + multiplied by the corresponding matrix product, but without the right + vector of the regular sequence. + + The algorithm maintains a left vector valued sequence consisting + of the left vector valued sequence of the argument ``sequence`` + (replaced by an empty tuple if ``sequence`` is `None`) plus several + components of the shape `m \mapsto f(k^t\cdot m +r)` for suitable + ``t`` and ``r``. + + Implicitly, the algorithm also maintains a `d \times n_max` matrix ``A`` + (where ``d`` is the dimension of the left vector valued sequence) + whose columns are the current left vector valued sequence evaluated at + the non-negative integers less than `n_max` and ensures that this + matrix has full row rank. + EXAMPLES: Binary sum of digits:: From b65aba394d03dfebb94e675cc342d5fa56daed49 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 11 Oct 2022 12:55:28 +0200 Subject: [PATCH 21/58] Add some further comments --- src/sage/combinat/k_regular_sequence.py | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index e8700268ab5..0d7b6dd5a89 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1223,14 +1223,37 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): zero = domain(0) one = domain(1) + # A `line` will be a triple `(t, r, s)` corresponding to an entry + # `k**t * m + r` belonging to `M_s` + # TODO: what is `M_s`? + + # The elements of `lines` will be correspond to the current components + # of the left vector valued sequence described in the algorithm section + # of the docstring. + def values(m, lines): + """ + Return current (as defined by ``lines``) left vector valued + sequence for argument ``m``. + """ return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R, s_R in lines) @cached_function(key=lambda lines: len(lines)) # we assume that existing lines are not changed (we allow appending of new lines) def some_inverse_U_matrix(lines): + r""" + Find an invertible `d \times d` times submatrix of the matrix + ``A`` described in the algorithm section of the docstring. + + The output is the inverse of the invertible submatrix and + the corresponding list of column indices (i.e., arguments to + the current left vector valued sequence). + """ d = len(seq(0)) + len(lines) for m_indices in cantor_product(xsrange(n_max), repeat=d, min_slope=1): + # Iterate over all increasing lists of length d consisting + # of non-negative integers less than `n_max`. + U = Matrix(domain, d, d, [values(m, lines) for m in m_indices]).transpose() try: return U.inverse(), m_indices @@ -1240,11 +1263,24 @@ def some_inverse_U_matrix(lines): raise RuntimeError def guess_linear_dependence(t_L, r_L, lines): + r""" + Based on an invertible submatrix of ``A`` as described in the + algorithm section of the docstring, find a candidate for a + linear combination of the rows of ``A`` yielding the subsequence + with parameters ``t_L`` and ``r_L``, i.e., + `m \mapsto f(k**t_L * m + r_L)`. + """ iU, m_indices = some_inverse_U_matrix(lines) X_L = vector(f(k**t_L * m + r_L) for m in m_indices) return X_L * iU def verify_linear_dependence(t_L, r_L, linear_dependence, lines): + r""" + Determine whether the subsequence with parameters ``t_L`` and + ``r_L``, i.e., `m \mapsto f(k**t_L * m + r_L)`, is the linear + combination ``linear_dependence`` of the current vector valued + sequence. + """ return all(f(k**t_L * m + r_L) == linear_dependence * vector(values(m, lines)) for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) From 113acab0698156553a7b34e0a01e7ff16c125f38 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 14:37:57 +0200 Subject: [PATCH 22/58] rename to linear_combination --- src/sage/combinat/k_regular_sequence.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 0d7b6dd5a89..57addf265b5 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1262,7 +1262,7 @@ def some_inverse_U_matrix(lines): else: raise RuntimeError - def guess_linear_dependence(t_L, r_L, lines): + def guess_linear_combination(t_L, r_L, lines): r""" Based on an invertible submatrix of ``A`` as described in the algorithm section of the docstring, find a candidate for a @@ -1274,27 +1274,27 @@ def guess_linear_dependence(t_L, r_L, lines): X_L = vector(f(k**t_L * m + r_L) for m in m_indices) return X_L * iU - def verify_linear_dependence(t_L, r_L, linear_dependence, lines): + def verify_linear_combination(t_L, r_L, linear_combination, lines): r""" Determine whether the subsequence with parameters ``t_L`` and ``r_L``, i.e., `m \mapsto f(k**t_L * m + r_L)`, is the linear - combination ``linear_dependence`` of the current vector valued + combination ``linear_combination`` of the current vector valued sequence. """ return all(f(k**t_L * m + r_L) == - linear_dependence * vector(values(m, lines)) + linear_combination * vector(values(m, lines)) for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) - def find_linear_dependence(t_L, r_L, lines): - linear_dependence = guess_linear_dependence(t_L, r_L, lines) - if not verify_linear_dependence(t_L, r_L, linear_dependence, lines): + def find_linear_combination(t_L, r_L, lines): + linear_combination = guess_linear_combination(t_L, r_L, lines) + if not verify_linear_combination(t_L, r_L, linear_combination, lines): raise ValueError - return linear_dependence + return linear_combination left = None if seq(0): try: - solution = find_linear_dependence(0, 0, []) + solution = find_linear_combination(0, 0, []) except ValueError: pass else: @@ -1326,7 +1326,7 @@ def include(line): line_L = t_L, r_L, s_L try: - solution = find_linear_dependence(t_L, r_L, lines) + solution = find_linear_combinations(t_L, r_L, lines) except ValueError: include(line_L) solution = (len(lines)-1)*(zero,) + (one,) From 028b29dda5ca6b0c1e04ac52261e2ca8a3bb0943 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 14:38:58 +0200 Subject: [PATCH 23/58] rename to linear_combination_candidate --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 57addf265b5..a5aa8602bfb 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1262,7 +1262,7 @@ def some_inverse_U_matrix(lines): else: raise RuntimeError - def guess_linear_combination(t_L, r_L, lines): + def linear_combination_candidate(t_L, r_L, lines): r""" Based on an invertible submatrix of ``A`` as described in the algorithm section of the docstring, find a candidate for a @@ -1286,7 +1286,7 @@ def verify_linear_combination(t_L, r_L, linear_combination, lines): for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) def find_linear_combination(t_L, r_L, lines): - linear_combination = guess_linear_combination(t_L, r_L, lines) + linear_combination = linear_combination_candidate(t_L, r_L, lines) if not verify_linear_combination(t_L, r_L, linear_combination, lines): raise ValueError return linear_combination From 305519dcfc104493e3a124b1deac75997c6da37d Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 14:44:41 +0200 Subject: [PATCH 24/58] fixup renameing --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a5aa8602bfb..66a3f4933d7 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1326,7 +1326,7 @@ def include(line): line_L = t_L, r_L, s_L try: - solution = find_linear_combinations(t_L, r_L, lines) + solution = find_linear_combination(t_L, r_L, lines) except ValueError: include(line_L) solution = (len(lines)-1)*(zero,) + (one,) From 46daf57f99c52c3883b055678f9ab34f2d32ef36 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 14:52:50 +0200 Subject: [PATCH 25/58] remove unused third component of "line" --- src/sage/combinat/k_regular_sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 66a3f4933d7..2f9f01a88f4 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1236,7 +1236,7 @@ def values(m, lines): Return current (as defined by ``lines``) left vector valued sequence for argument ``m``. """ - return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R, s_R in lines) + return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R in lines) @cached_function(key=lambda lines: len(lines)) # we assume that existing lines are not changed (we allow appending of new lines) def some_inverse_U_matrix(lines): @@ -1306,24 +1306,24 @@ def find_linear_combination(t_L, r_L, lines): def include(line): to_branch.append(line) lines.append(line) - t, r, s = line + t, r = line logger.info('including f_{%s*m+%s}', k**t, r) if left is None: - line_L = (0, 0, 0) # entries (t, r, s) --> k**t * m + r, belong to M_s + line_L = (0, 0) # entries (t, r) --> k**t * m + r include(line_L) left = vector((len(seq(0)) + len(lines)-1)*(zero,) + (one,)) while to_branch: line_R = to_branch.pop(0) - t_R, r_R, s_R = line_R + t_R, r_R = line_R if t_R >= max_dimension: raise RuntimeError t_L = t_R + 1 for s_L in srange(k): r_L = k**t_R * s_L + r_R - line_L = t_L, r_L, s_L + line_L = t_L, r_L try: solution = find_linear_combination(t_L, r_L, lines) From 9be3124163f00798d5d0914bee9b19856ffd78b6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 14:54:16 +0200 Subject: [PATCH 26/58] remove split_interlace as never used --- src/sage/combinat/k_regular_sequence.py | 47 ------------------------- 1 file changed, 47 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 2f9f01a88f4..1fc182df441 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -162,53 +162,6 @@ def value(D, k): return sum(d * k**j for j, d in enumerate(D)) -def split_interlace(n, k, p): - r""" - Split each digit in the `k`-ary expansion of `n` into `p` parts and - return the value of the expansion obtained by each of these parts. - - INPUT: - - - ``n`` -- an integer - - - ``k`` -- an integer specifying the base - - - ``p`` -- a positive integer specifying in how many parts - the input ``n`` is split. This has to be a divisor of ``k``. - - OUTPUT: - - A tuple of integers - - EXAMPLES:: - - sage: from sage.combinat.k_regular_sequence import split_interlace - sage: [(n, split_interlace(n, 4, 2)) for n in srange(20)] - [(0, (0, 0)), (1, (1, 0)), (2, (0, 1)), (3, (1, 1)), - (4, (2, 0)), (5, (3, 0)), (6, (2, 1)), (7, (3, 1)), - (8, (0, 2)), (9, (1, 2)), (10, (0, 3)), (11, (1, 3)), - (12, (2, 2)), (13, (3, 2)), (14, (2, 3)), (15, (3, 3)), - (16, (4, 0)), (17, (5, 0)), (18, (4, 1)), (19, (5, 1))] - sage: [(n, split_interlace(n, 6, 3)) for n in srange(9)] - [(0, (0, 0, 0)), (1, (1, 0, 0)), (2, (0, 1, 0)), - (3, (1, 1, 0)), (4, (0, 0, 1)), (5, (1, 0, 1)), - (6, (2, 0, 0)), (7, (3, 0, 0)), (8, (2, 1, 0))] - - TESTS:: - - sage: split_interlace(42, 4, 3) - Traceback (most recent call last): - ... - ValueError: p=3 is not a divisor of k=4. - """ - if k % p != 0: - raise ValueError('p={} is not a divisor of k={}.'.format(p, k)) - ki = k // p - return tuple(value(D, ki) - for D in zip(*(d.digits(ki, padto=p) - for d in n.digits(k, padto=1)))) - - class kRegularSequence(RecognizableSeries): def __init__(self, parent, mu, left=None, right=None): r""" From 9fc2663749bd9b5cd446268e0c5725bd9f27582f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 15:09:35 +0200 Subject: [PATCH 27/58] rewrite preparation of left to be more readable --- src/sage/combinat/k_regular_sequence.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 1fc182df441..102db25a163 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1167,7 +1167,7 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): domain = self.coefficient_ring() if sequence is None: mu = [[] for _ in srange(k)] - seq = lambda m: tuple() + seq = lambda m: vector([]) else: mu = [M.rows() for M in sequence.mu] seq = lambda m: sequence.left * sequence._mu_of_word_( @@ -1238,20 +1238,22 @@ def verify_linear_combination(t_L, r_L, linear_combination, lines): linear_combination * vector(values(m, lines)) for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) + class NoLinearCombination(ValueError): + pass + def find_linear_combination(t_L, r_L, lines): linear_combination = linear_combination_candidate(t_L, r_L, lines) if not verify_linear_combination(t_L, r_L, linear_combination, lines): - raise ValueError + raise NoLinearCombination return linear_combination - left = None - if seq(0): + if seq(0).is_zero(): + left = None + else: try: - solution = find_linear_combination(0, 0, []) - except ValueError: - pass - else: - left = vector(solution) + left = vector(find_linear_combination(0, 0, [])) + except NoLinearCombination: + left = None to_branch = [] lines = [] From 3fc45d50d053a477547524a72df0c0d06295112b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 15:17:28 +0200 Subject: [PATCH 28/58] fix rest/latex: \operatorname --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 102db25a163..c09fdaf06f3 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -140,7 +140,7 @@ def value(D, k): .. MATH:: - \sum_{0\leq j < \operator{len}D} D[j] k^j. + \sum_{0\leq j < \operatorname{len}D} D[j] k^j. INPUT: From c532a1e00f80a3f17a3db7c02aa74a06f69699bc Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 16:25:59 +0200 Subject: [PATCH 29/58] use .linear_representation --- src/sage/combinat/k_regular_sequence.py | 93 +++++++++++++++---------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index c09fdaf06f3..a85330fc748 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1049,11 +1049,13 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): INFO:...:M_1: f_{4*m+3} = (-1, 2) * X_m sage: S1 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... - sage: S1.mu[0], S1.mu[1], S1.left, S1.right - ( - [1 0] [ 0 1] - [0 1], [-1 2], (1, 0), (0, 1) - ) + sage: S1.linear_representation() + ((1, 0), + Finite family {0: [1 0] + [0 1], + 1: [ 0 1] + [-1 2]}, + (0, 1)) sage: from importlib import reload sage: logging.shutdown(); _ = reload(logging) @@ -1065,52 +1067,63 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: S2 = Seq2.guess(s, sequence=C) sage: S2 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... - sage: S2.mu[0], S2.mu[1], S2.left, S2.right - ( - [1 0] [1 0] - [0 1], [1 1], (0, 1), (1, 0) - ) + sage: S2.linear_representation() + ((0, 1), + Finite family {0: [1 0] + [0 1], + 1: [1 0] + [1 1]}, + (1, 0)) The sequence of all natural numbers:: sage: S = Seq2.guess(lambda n: n) sage: S 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... - sage: S.mu[0], S.mu[1], S.left, S.right - ( - [2 0] [ 0 1] - [2 1], [-2 3], (1, 0), (0, 1) - ) + sage: S.linear_representation() + ((1, 0), + Finite family {0: [2 0] + [2 1], + 1: [ 0 1] + [-2 3]}, + (0, 1)) The indicator function of the even integers:: sage: S = Seq2.guess(lambda n: ZZ(is_even(n))) sage: S 2-regular sequence 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, ... - sage: S.mu[0], S.mu[1], S.left, S.right - ( - [0 1] [0 0] - [0 1], [0 1], (1, 0), (1, 1) - ) + sage: S.linear_representation() + ((1, 0), + Finite family {0: [0 1] + [0 1], + 1: [0 0] + [0 1]}, + (1, 1)) The indicator function of the odd integers:: sage: S = Seq2.guess(lambda n: ZZ(is_odd(n))) sage: S 2-regular sequence 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ... - sage: S.mu[0], S.mu[1], S.left, S.right - ( - [0 0] [0 1] - [0 1], [0 1], (1, 0), (0, 1) - ) + sage: S.linear_representation() + ((1, 0), + Finite family {0: [0 0] + [0 1], + 1: [0 1] + [0 1]}, + (0, 1)) TESTS:: sage: S = Seq2.guess(lambda n: 2, sequence=C) sage: S 2-regular sequence 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ... - sage: S.mu[0], S.mu[1], S.left, S.right - ([1], [1], (2), (1)) + sage: S.linear_representation() + ((2), + Finite family {0: [1], + 1: [1]}, + (1)) We guess some partial sums sequences:: @@ -1125,11 +1138,13 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: G = Seq2.guess(lambda n: L[n]) sage: G 2-regular sequence 1, 3, 5, 9, 11, 15, 19, 27, 29, 33, ... - sage: G.mu[0], G.mu[1], G.left, G.right - ( - [ 0 1] [3 0] - [-3 4], [3 2], (1, 0), (1, 1) - ) + sage: G.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-3 4], + 1: [3 0] + [3 2]}, + (1, 1)) sage: G == S.partial_sums(include_n=True) True @@ -1147,11 +1162,15 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: G = Seq3.guess(lambda n: L[n]) sage: G 3-regular sequence 1, 4, 6, 9, 18, 24, 26, 32, 36, 39, ... - sage: G.mu[0], G.mu[1], G.mu[2], G.left, G.right - ( - [ 0 1] [18/5 2/5] [ 6 0] - [-6 7], [18/5 27/5], [24 2], (1, 0), (1, 1) - ) + sage: G.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-6 7], + 1: [18/5 2/5] + [18/5 27/5], + 2: [ 6 0] + [24 2]}, + (1, 1)) sage: G == S.partial_sums(include_n=True) True """ From fd320c15e497499a7acefc3b896aeb891c1a671c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 16:48:48 +0200 Subject: [PATCH 30/58] describe variants in example --- src/sage/combinat/k_regular_sequence.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a85330fc748..bb13c027854 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1035,7 +1035,7 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: [s(n) for n in srange(10)] [0, 1, 1, 2, 1, 2, 2, 3, 1, 2] - Variant 1:: + Let us guess a `2`-linear representation for `s(n)`:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging @@ -1060,9 +1060,10 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: from importlib import reload sage: logging.shutdown(); _ = reload(logging) - Variant 2:: + We guess again, but this time, we use a constant sequence + for bootstrapping the guessing process:: - sage: C = Seq2((Matrix([[1]]), Matrix([[1]])), vector([1]), vector([1])); C + sage: C = Seq2.one_hadamard(); C 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... sage: S2 = Seq2.guess(s, sequence=C) sage: S2 From e6a5a84d9e6d6bd8ddf4717a1781ae6a535dcd67 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 16:50:36 +0200 Subject: [PATCH 31/58] compare outputs of variants in example --- src/sage/combinat/k_regular_sequence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index bb13c027854..e77528daea1 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1075,6 +1075,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): 1: [1 0] [1 1]}, (1, 0)) + sage: S1 == S2 + True The sequence of all natural numbers:: From 221d33c9b82c620bd341a8eedfc4dceeb79dc183 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 17:32:07 +0200 Subject: [PATCH 32/58] logging-info: show sequences in k-kernel properly --- src/sage/combinat/k_regular_sequence.py | 51 ++++++++++++++++--------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index e77528daea1..3faebb76da6 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1040,14 +1040,9 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging sage: logging.basicConfig(level=logging.INFO) - sage: S1 = Seq2.guess(s) + sage: S1 = Seq2.guess(s); S1 INFO:...:including f_{1*m+0} - INFO:...:M_0: f_{2*m+0} = (1) * X_m INFO:...:including f_{2*m+1} - INFO:...:M_1: f_{2*m+1} = (0, 1) * X_m - INFO:...:M_0: f_{4*m+1} = (0, 1) * X_m - INFO:...:M_1: f_{4*m+3} = (-1, 2) * X_m - sage: S1 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: S1.linear_representation() ((1, 0), @@ -1057,16 +1052,14 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): [-1 2]}, (0, 1)) - sage: from importlib import reload - sage: logging.shutdown(); _ = reload(logging) - We guess again, but this time, we use a constant sequence for bootstrapping the guessing process:: sage: C = Seq2.one_hadamard(); C 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... - sage: S2 = Seq2.guess(s, sequence=C) - sage: S2 + sage: S2 = Seq2.guess(s, sequence=C); S2 + INFO:...:including 2-regular sequence 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... + INFO:...:including f_{1*m+0} 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: S2.linear_representation() ((0, 1), @@ -1080,8 +1073,9 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): The sequence of all natural numbers:: - sage: S = Seq2.guess(lambda n: n) - sage: S + sage: S = Seq2.guess(lambda n: n); S + INFO:...:including f_{1*m+0} + INFO:...:including f_{2*m+1} 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... sage: S.linear_representation() ((1, 0), @@ -1093,8 +1087,9 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): The indicator function of the even integers:: - sage: S = Seq2.guess(lambda n: ZZ(is_even(n))) - sage: S + sage: S = Seq2.guess(lambda n: ZZ(is_even(n))); S + INFO:...:including f_{1*m+0} + INFO:...:including f_{2*m+0} 2-regular sequence 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, ... sage: S.linear_representation() ((1, 0), @@ -1106,8 +1101,9 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): The indicator function of the odd integers:: - sage: S = Seq2.guess(lambda n: ZZ(is_odd(n))) - sage: S + sage: S = Seq2.guess(lambda n: ZZ(is_odd(n))); S + INFO:...:including f_{1*m+0} + INFO:...:including f_{2*m+1} 2-regular sequence 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, ... sage: S.linear_representation() ((1, 0), @@ -1119,6 +1115,22 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): TESTS:: + sage: from importlib import reload + sage: logging.shutdown(); _ = reload(logging) + sage: logging.basicConfig(level=logging.DEBUG) + sage: Seq2.guess(s) + INFO:...:including f_{1*m+0} + DEBUG:...:M_0: f_{2*m+0} = (1) * X_m + INFO:...:including f_{2*m+1} + DEBUG:...:M_1: f_{2*m+1} = (0, 1) * X_m + DEBUG:...:M_0: f_{4*m+1} = (0, 1) * X_m + DEBUG:...:M_1: f_{4*m+3} = (-1, 2) * X_m + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... + sage: from importlib import reload + sage: logging.shutdown(); _ = reload(logging) + + :: + sage: S = Seq2.guess(lambda n: 2, sequence=C) sage: S 2-regular sequence 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ... @@ -1194,6 +1206,7 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): mu = [M.rows() for M in sequence.mu] seq = lambda m: sequence.left * sequence._mu_of_word_( self._n_to_index_(m)) + logger.info('including %s', sequence) zero = domain(0) one = domain(1) @@ -1307,8 +1320,8 @@ def include(line): except ValueError: include(line_L) solution = (len(lines)-1)*(zero,) + (one,) - logger.info('M_%s: f_{%s*m+%s} = %s * X_m', - s_L, k**t_L, r_L, solution) + logger.debug('M_%s: f_{%s*m+%s} = %s * X_m', + s_L, k**t_L, r_L, solution) mu[s_L].append(solution) d = len(seq(0)) + len(lines) From 02976689ab84e166ab8b2d9edeb08f092d44dbbf Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 17:44:57 +0200 Subject: [PATCH 33/58] apply suggestion oneline-description of .guess Co-authored-by: cheuberg --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 3faebb76da6..facc297508b 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -981,7 +981,7 @@ def _n_to_index_(self, n): def guess(self, f, n_max=100, max_dimension=10, sequence=None): r""" - Guess a `k`-regular sequence of `(f(n))_{n\geq0}`. + Guess a `k`-regular sequence whose first terms coincide with `(f(n))_{n\geq0}`. INPUT: From 9e5682baf7903e3027f9cdacec3873b187a94828 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 17:59:58 +0200 Subject: [PATCH 34/58] rewrite documentation of parameter "sequence" --- src/sage/combinat/k_regular_sequence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 3faebb76da6..c6d2b3164a1 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -996,7 +996,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): the maximum dimension which is tried when guessing the sequence - ``sequence`` -- (default: ``None``) a `k`-regular sequence used - for bootstrapping this guessing + for bootstrapping the guessing by adding information of the + linear representation of ``sequence`` to the guessed representation OUTPUT: From f878f7521576fc7d3edc9508090e11954ecdb5b3 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 18:07:26 +0200 Subject: [PATCH 35/58] fixup catching NoLinearCombination exception --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index c6d2b3164a1..aabda239e38 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1318,7 +1318,7 @@ def include(line): try: solution = find_linear_combination(t_L, r_L, lines) - except ValueError: + except NoLinearCombination: include(line_L) solution = (len(lines)-1)*(zero,) + (one,) logger.debug('M_%s: f_{%s*m+%s} = %s * X_m', From d0ddcabac4dd87e65b50cb44b8c96cc5b733761e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 18:29:10 +0200 Subject: [PATCH 36/58] rename, describe and test max_exponent --- src/sage/combinat/k_regular_sequence.py | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index aabda239e38..d52321176cd 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -979,7 +979,7 @@ def _n_to_index_(self, n): except OverflowError: raise ValueError('value {} of index is negative'.format(n)) from None - def guess(self, f, n_max=100, max_dimension=10, sequence=None): + def guess(self, f, n_max=100, max_exponent=10, sequence=None): r""" Guess a `k`-regular sequence of `(f(n))_{n\geq0}`. @@ -992,8 +992,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): `k`-regular sequence coincides with `f` on the first ``n_max`` terms - - ``max_dimension`` -- (default: ``10``) a positive integer specifying - the maximum dimension which is tried when guessing the sequence + - ``max_exponent`` -- (default: ``10``) a positive integer specifying + the maximum exponent of `k` which is tried when guessing the sequence - ``sequence`` -- (default: ``None``) a `k`-regular sequence used for bootstrapping the guessing by adding information of the @@ -1148,8 +1148,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): 2-regular sequence 1, 2, 2, 4, 2, 4, 4, 8, 2, 4, ... sage: from itertools import islice sage: L = []; ps = 0 - sage: for s in islice(S, 110): - ....: ps += s + sage: for j in islice(S, 110): + ....: ps += j ....: L.append(ps) sage: G = Seq2.guess(lambda n: L[n]) sage: G @@ -1172,8 +1172,8 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): 3-regular sequence 1, 3, 2, 3, 9, 6, 2, 6, 4, 3, ... sage: from itertools import islice sage: L = []; ps = 0 - sage: for s in islice(S, 110): - ....: ps += s + sage: for j in islice(S, 110): + ....: ps += j ....: L.append(ps) sage: G = Seq3.guess(lambda n: L[n]) sage: G @@ -1189,6 +1189,13 @@ def guess(self, f, n_max=100, max_dimension=10, sequence=None): (1, 1)) sage: G == S.partial_sums(include_n=True) True + + :: + + sage: Seq2.guess(s, max_exponent=1) + Traceback (most recent call last): + ... + RuntimeError: aborting as exponents would be larger than max_esponent=1 """ import logging logger = logging.getLogger(__name__) @@ -1308,8 +1315,9 @@ def include(line): while to_branch: line_R = to_branch.pop(0) t_R, r_R = line_R - if t_R >= max_dimension: - raise RuntimeError + if t_R >= max_exponent: + raise RuntimeError(f'aborting as exponents would be larger ' + f'than max_esponent={max_exponent}') t_L = t_R + 1 for s_L in srange(k): From 100d9782165b3981d6e65a5576755f5b3ea37efd Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 18:29:31 +0200 Subject: [PATCH 37/58] write error message --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index d52321176cd..05b585135b4 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1256,7 +1256,7 @@ def some_inverse_U_matrix(lines): except ZeroDivisionError: pass else: - raise RuntimeError + raise RuntimeError('no inverse submatrix found') def linear_combination_candidate(t_L, r_L, lines): r""" From dbf2bdc95398d593ffabbeecea99c791e99ee680 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 26 May 2023 19:19:38 +0200 Subject: [PATCH 38/58] Apply suggestions from code review Co-authored-by: cheuberg --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 4d2df4aa784..f23b429cc34 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1195,7 +1195,7 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): sage: Seq2.guess(s, max_exponent=1) Traceback (most recent call last): ... - RuntimeError: aborting as exponents would be larger than max_esponent=1 + RuntimeError: aborting as exponents would be larger than max_exponent=1 """ import logging logger = logging.getLogger(__name__) @@ -1317,7 +1317,7 @@ def include(line): t_R, r_R = line_R if t_R >= max_exponent: raise RuntimeError(f'aborting as exponents would be larger ' - f'than max_esponent={max_exponent}') + f'than max_exponent={max_exponent}') t_L = t_R + 1 for s_L in srange(k): From 3a393234982ad4981e41768b04e722f430d7c4ee Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 7 Jun 2023 13:34:40 +0200 Subject: [PATCH 39/58] Apply suggestions from code review Co-authored-by: cheuberg --- src/sage/combinat/k_regular_sequence.py | 34 +++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index f23b429cc34..caadfa8f0f0 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -125,6 +125,9 @@ def pad_right(T, length, zero=0): (1, 2, 3, 0, 0, 0, 0, 0, 0, 0) sage: pad_right((1, 2, 3), 2) (1, 2, 3) + sage: from sage.combinat.k_regular_sequence import pad_right + sage: pad_right([(1, 2), (3, 4)], 4, (0, 0)) + [(1, 2), (3, 4), (0, 0), (0, 0)] TESTS:: @@ -993,7 +996,8 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): terms - ``max_exponent`` -- (default: ``10``) a positive integer specifying - the maximum exponent of `k` which is tried when guessing the sequence + the maximum exponent of `k` which is tried when guessing the sequence, + i.e., relations between `f(k^t n+r)` are used for `0\le t\le \operatorname{max_exponent}` and `0\le r < k^j` - ``sequence`` -- (default: ``None``) a `k`-regular sequence used for bootstrapping the guessing by adding information of the @@ -1006,20 +1010,20 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): ALGORITHM: For the purposes of this description, the left vector valued sequence - associated with a regular sequence is consists of the left vector + associated with a regular sequence consists of the left vector multiplied by the corresponding matrix product, but without the right vector of the regular sequence. The algorithm maintains a left vector valued sequence consisting of the left vector valued sequence of the argument ``sequence`` - (replaced by an empty tuple if ``sequence`` is `None`) plus several + (replaced by an empty tuple if ``sequence`` is ``None``) plus several components of the shape `m \mapsto f(k^t\cdot m +r)` for suitable ``t`` and ``r``. - Implicitly, the algorithm also maintains a `d \times n_max` matrix ``A`` + Implicitly, the algorithm also maintains a `d \times n_\max` matrix ``A`` (where ``d`` is the dimension of the left vector valued sequence) whose columns are the current left vector valued sequence evaluated at - the non-negative integers less than `n_max` and ensures that this + the non-negative integers less than `n_\max` and ensures that this matrix has full row rank. EXAMPLES: @@ -1052,6 +1056,8 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): 1: [ 0 1] [-1 2]}, (0, 1)) + + The ``INFO`` messages mean that the right vector valued sequence is the sequence `(s(n), s(2n+1))^\top`. We guess again, but this time, we use a constant sequence for bootstrapping the guessing process:: @@ -1219,11 +1225,10 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): zero = domain(0) one = domain(1) - # A `line` will be a triple `(t, r, s)` corresponding to an entry - # `k**t * m + r` belonging to `M_s` - # TODO: what is `M_s`? + # A `line` will be a pair `(t, r)` corresponding to an entry + # `k**t * m + r` - # The elements of `lines` will be correspond to the current components + # The elements of `lines` will correspond to the current components # of the left vector valued sequence described in the algorithm section # of the docstring. @@ -1234,10 +1239,12 @@ def values(m, lines): """ return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R in lines) - @cached_function(key=lambda lines: len(lines)) # we assume that existing lines are not changed (we allow appending of new lines) + @cached_function(key=lambda lines: len(lines)) + # we assume that existing lines are not changed + # (we allow appending of new lines) def some_inverse_U_matrix(lines): r""" - Find an invertible `d \times d` times submatrix of the matrix + Find an invertible `d \times d` submatrix of the matrix ``A`` described in the algorithm section of the docstring. The output is the inverse of the invertible submatrix and @@ -1256,7 +1263,7 @@ def some_inverse_U_matrix(lines): except ZeroDivisionError: pass else: - raise RuntimeError('no inverse submatrix found') + raise RuntimeError('no invertible submatrix found') def linear_combination_candidate(t_L, r_L, lines): r""" @@ -1310,7 +1317,8 @@ def include(line): if left is None: line_L = (0, 0) # entries (t, r) --> k**t * m + r include(line_L) - left = vector((len(seq(0)) + len(lines)-1)*(zero,) + (one,)) + assert len(lines) == 1 + left = vector(len(seq(0))*(zero,) + (one,)) while to_branch: line_R = to_branch.pop(0) From ec28f89d9e54c8e4408266e603343e297581f04a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 7 Jun 2023 15:59:02 +0200 Subject: [PATCH 40/58] remove unneeded import --- src/sage/combinat/k_regular_sequence.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index caadfa8f0f0..b7efe90d1db 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -125,7 +125,6 @@ def pad_right(T, length, zero=0): (1, 2, 3, 0, 0, 0, 0, 0, 0, 0) sage: pad_right((1, 2, 3), 2) (1, 2, 3) - sage: from sage.combinat.k_regular_sequence import pad_right sage: pad_right([(1, 2), (3, 4)], 4, (0, 0)) [(1, 2), (3, 4), (0, 0), (0, 0)] From 29238d90c8d4da0c3b4374ba97e00241a60d3488 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 7 Jun 2023 15:59:17 +0200 Subject: [PATCH 41/58] make NoLinearCombination a RuntimeError --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index b7efe90d1db..9629d33fd03 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1287,7 +1287,7 @@ def verify_linear_combination(t_L, r_L, linear_combination, lines): linear_combination * vector(values(m, lines)) for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) - class NoLinearCombination(ValueError): + class NoLinearCombination(RuntimeError): pass def find_linear_combination(t_L, r_L, lines): From 1b118beb31ad4075c8f345338cbb4cbc9c19c509 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 7 Jun 2023 16:00:17 +0200 Subject: [PATCH 42/58] simplify code by removing variables --- src/sage/combinat/k_regular_sequence.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 9629d33fd03..cffe77f9ae6 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1307,21 +1307,18 @@ def find_linear_combination(t_L, r_L, lines): to_branch = [] lines = [] - def include(line): - to_branch.append(line) - lines.append(line) - t, r = line + def include(t, r): + to_branch.append((t, r)) + lines.append((t, r)) logger.info('including f_{%s*m+%s}', k**t, r) if left is None: - line_L = (0, 0) # entries (t, r) --> k**t * m + r - include(line_L) + include(0, 0) # entries (t, r) --> k**t * m + r assert len(lines) == 1 left = vector(len(seq(0))*(zero,) + (one,)) while to_branch: - line_R = to_branch.pop(0) - t_R, r_R = line_R + t_R, r_R = to_branch.pop(0) if t_R >= max_exponent: raise RuntimeError(f'aborting as exponents would be larger ' f'than max_exponent={max_exponent}') @@ -1329,12 +1326,10 @@ def include(line): t_L = t_R + 1 for s_L in srange(k): r_L = k**t_R * s_L + r_R - line_L = t_L, r_L - try: solution = find_linear_combination(t_L, r_L, lines) except NoLinearCombination: - include(line_L) + include(t_L, r_L) # entries (t, r) --> k**t * m + r solution = (len(lines)-1)*(zero,) + (one,) logger.debug('M_%s: f_{%s*m+%s} = %s * X_m', s_L, k**t_L, r_L, solution) From 67562fbab060d21433782e0508053dabd4745706 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 7 Jun 2023 16:01:03 +0200 Subject: [PATCH 43/58] consequently rename to linear_combination --- src/sage/combinat/k_regular_sequence.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index cffe77f9ae6..81cc196a493 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1327,13 +1327,13 @@ def include(t, r): for s_L in srange(k): r_L = k**t_R * s_L + r_R try: - solution = find_linear_combination(t_L, r_L, lines) + linear_combination = find_linear_combination(t_L, r_L, lines) except NoLinearCombination: include(t_L, r_L) # entries (t, r) --> k**t * m + r - solution = (len(lines)-1)*(zero,) + (one,) + linear_combination = (len(lines)-1)*(zero,) + (one,) logger.debug('M_%s: f_{%s*m+%s} = %s * X_m', - s_L, k**t_L, r_L, solution) - mu[s_L].append(solution) + s_L, k**t_L, r_L, linear_combination) + mu[s_L].append(linear_combination) d = len(seq(0)) + len(lines) mu = tuple(Matrix(domain, [pad_right(tuple(row), d, zero=zero) for row in M]) From c15742dbba34dd081a5d2454ad6d9e8b67322743 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 19:50:37 +0200 Subject: [PATCH 44/58] more consistent naming in DEBUG output --- src/sage/combinat/k_regular_sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 81cc196a493..f2401f37be8 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1126,11 +1126,11 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): sage: logging.basicConfig(level=logging.DEBUG) sage: Seq2.guess(s) INFO:...:including f_{1*m+0} - DEBUG:...:M_0: f_{2*m+0} = (1) * X_m + DEBUG:...:M_0: f_{2*m+0} = (1) * F_m INFO:...:including f_{2*m+1} - DEBUG:...:M_1: f_{2*m+1} = (0, 1) * X_m - DEBUG:...:M_0: f_{4*m+1} = (0, 1) * X_m - DEBUG:...:M_1: f_{4*m+3} = (-1, 2) * X_m + DEBUG:...:M_1: f_{2*m+1} = (0, 1) * F_m + DEBUG:...:M_0: f_{4*m+1} = (0, 1) * F_m + DEBUG:...:M_1: f_{4*m+3} = (-1, 2) * F_m 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: from importlib import reload sage: logging.shutdown(); _ = reload(logging) @@ -1331,7 +1331,7 @@ def include(t, r): except NoLinearCombination: include(t_L, r_L) # entries (t, r) --> k**t * m + r linear_combination = (len(lines)-1)*(zero,) + (one,) - logger.debug('M_%s: f_{%s*m+%s} = %s * X_m', + logger.debug('M_%s: f_{%s*m+%s} = %s * F_m', s_L, k**t_L, r_L, linear_combination) mu[s_L].append(linear_combination) From c198d90dd02a17be5129f2028715595ccfbed1ca Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 19:51:50 +0200 Subject: [PATCH 45/58] fix left/right issue when bootstraping guessing --- src/sage/combinat/k_regular_sequence.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index f2401f37be8..380974b03a2 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1201,6 +1201,23 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): Traceback (most recent call last): ... RuntimeError: aborting as exponents would be larger than max_exponent=1 + + :: + + sage: R = kRegularSequenceSpace(2, QQ) + sage: one = R.one_hadamard() + sage: S = R.guess(lambda n: sum(n.bits()), sequence=one) + one + sage: T = R.guess(lambda n: n*n, sequence=S, n_max=4); T + 2-regular sequence 0, 1, 4, 9, 16, 25, 36, 163/3, 64, 89, ... + sage: T.linear_representation() + ((0, 0, 1), + Finite family {0: [1 0 0] + [0 1 0] + [0 0 4], + 1: [ 0 1 0] + [ -1 2 0] + [13/3 -5/3 16/3]}, + (1, 2, 0)) """ import logging logger = logging.getLogger(__name__) @@ -1217,8 +1234,8 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): seq = lambda m: vector([]) else: mu = [M.rows() for M in sequence.mu] - seq = lambda m: sequence.left * sequence._mu_of_word_( - self._n_to_index_(m)) + seq = lambda m: (sequence._mu_of_word_(self._n_to_index_(m)) + * sequence.right) logger.info('including %s', sequence) zero = domain(0) From ae8a2871c3e473d1c4c511b3bb834f591e020494 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 19:52:07 +0200 Subject: [PATCH 46/58] fixup n_max --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 380974b03a2..00545a9a3d6 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1269,7 +1269,7 @@ def some_inverse_U_matrix(lines): """ d = len(seq(0)) + len(lines) - for m_indices in cantor_product(xsrange(n_max), repeat=d, min_slope=1): + for m_indices in cantor_product(xsrange(n_max+1), repeat=d, min_slope=1): # Iterate over all increasing lists of length d consisting # of non-negative integers less than `n_max`. From 6aab949e4b1caafdba54c67f9739b9e36002ec87 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 19:52:19 +0200 Subject: [PATCH 47/58] add another test --- src/sage/combinat/k_regular_sequence.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 00545a9a3d6..922f1a75d99 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1218,6 +1218,15 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): [ -1 2 0] [13/3 -5/3 16/3]}, (1, 2, 0)) + + :: + + sage: two = Seq2.one_hadamard() * 2 + sage: two.linear_representation() + ((1), Finite family {0: [1], 1: [1]}, (2)) + sage: two_again = Seq2.guess(lambda n: 2, sequence=two) + sage: two_again.linear_representation() + ((1), Finite family {0: [1], 1: [1]}, (2)) """ import logging logger = logging.getLogger(__name__) From 250a98c2e58f2c90efc427a78cebf7898742fd7a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 19:59:02 +0200 Subject: [PATCH 48/58] add another test --- src/sage/combinat/k_regular_sequence.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 922f1a75d99..a0d527660dd 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1227,6 +1227,17 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): sage: two_again = Seq2.guess(lambda n: 2, sequence=two) sage: two_again.linear_representation() ((1), Finite family {0: [1], 1: [1]}, (2)) + + :: + + sage: def s(k): + ....: return k + sage: S1 = Seq2.guess(s) + sage: S2 = Seq2.guess(s, sequence=S1) + sage: S1 + 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + sage: S2 + 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... """ import logging logger = logging.getLogger(__name__) From 9c772e59eb33597925eae304afa6b40132bbd1f6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 20:16:56 +0200 Subject: [PATCH 49/58] better handling of n_max --- src/sage/combinat/k_regular_sequence.py | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a0d527660dd..a033a63ad03 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -981,7 +981,7 @@ def _n_to_index_(self, n): except OverflowError: raise ValueError('value {} of index is negative'.format(n)) from None - def guess(self, f, n_max=100, max_exponent=10, sequence=None): + 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}`. @@ -990,9 +990,9 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): - ``f`` -- a function (callable) which determines the sequence. It takes nonnegative integers as an input - - ``n_max`` -- (default: ``100``) a positive integer. The resulting - `k`-regular sequence coincides with `f` on the first ``n_max`` - terms + - ``n_verify`` -- (default: ``100``) a positive integer. The resulting + `k`-regular sequence coincides with `f` on the first ``n_verify`` + terms. - ``max_exponent`` -- (default: ``10``) a positive integer specifying the maximum exponent of `k` which is tried when guessing the sequence, @@ -1019,10 +1019,10 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): components of the shape `m \mapsto f(k^t\cdot m +r)` for suitable ``t`` and ``r``. - Implicitly, the algorithm also maintains a `d \times n_\max` matrix ``A`` + Implicitly, the algorithm also maintains a `d \times n_\mathrm{verify}` matrix ``A`` (where ``d`` is the dimension of the left vector valued sequence) whose columns are the current left vector valued sequence evaluated at - the non-negative integers less than `n_\max` and ensures that this + the non-negative integers less than `n_\mathrm{verify}` and ensures that this matrix has full row rank. EXAMPLES: @@ -1207,7 +1207,7 @@ def guess(self, f, n_max=100, max_exponent=10, sequence=None): sage: R = kRegularSequenceSpace(2, QQ) sage: one = R.one_hadamard() sage: S = R.guess(lambda n: sum(n.bits()), sequence=one) + one - sage: T = R.guess(lambda n: n*n, sequence=S, n_max=4); T + sage: T = R.guess(lambda n: n*n, sequence=S, n_verify=4); T 2-regular sequence 0, 1, 4, 9, 16, 25, 36, 163/3, 64, 89, ... sage: T.linear_representation() ((0, 0, 1), @@ -1289,9 +1289,9 @@ def some_inverse_U_matrix(lines): """ d = len(seq(0)) + len(lines) - for m_indices in cantor_product(xsrange(n_max+1), repeat=d, min_slope=1): + for m_indices in cantor_product(xsrange(n_verify), repeat=d, min_slope=1): # Iterate over all increasing lists of length d consisting - # of non-negative integers less than `n_max`. + # of non-negative integers less than `n_verify`. U = Matrix(domain, d, d, [values(m, lines) for m in m_indices]).transpose() try: @@ -1319,10 +1319,18 @@ def verify_linear_combination(t_L, r_L, linear_combination, lines): ``r_L``, i.e., `m \mapsto f(k**t_L * m + r_L)`, is the linear combination ``linear_combination`` of the current vector valued sequence. + + Note that we only evaluate the subsequence of ``f`` where arguments + of ``f`` are at most ``n_verify``. This might lead to detection of + linear dependence which would not be true for higher values, but this + coincides with the documentation of ``n_verify``. + However, this is not a guarantee that the given function will never + be evaluated beyond ``n_verify``, determining an invertible submatrix + in ``some_inverse_U_matrix`` might require us to do so. """ return all(f(k**t_L * m + r_L) == linear_combination * vector(values(m, lines)) - for m in xsrange(0, (n_max - r_L) // k**t_L + 1)) + for m in xsrange(0, (n_verify - r_L) // k**t_L + 1)) class NoLinearCombination(RuntimeError): pass From d4edd45928288ef81bf387f959d1a201977635bb Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jun 2023 20:22:57 +0200 Subject: [PATCH 50/58] fix whitespaces --- src/sage/combinat/k_regular_sequence.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a033a63ad03..b085d22243c 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1056,7 +1056,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): [-1 2]}, (0, 1)) - The ``INFO`` messages mean that the right vector valued sequence is the sequence `(s(n), s(2n+1))^\top`. + The ``INFO`` messages mean that the right vector valued sequence is the sequence `(s(n), s(2n+1))^\top`. We guess again, but this time, we use a constant sequence for bootstrapping the guessing process:: @@ -1275,8 +1275,8 @@ def values(m, lines): """ return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R in lines) - @cached_function(key=lambda lines: len(lines)) - # we assume that existing lines are not changed + @cached_function(key=lambda lines: len(lines)) + # we assume that existing lines are not changed # (we allow appending of new lines) def some_inverse_U_matrix(lines): r""" From f2dd0559b725b8d9dc5195e6b810baf28b195866 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 9 Jun 2023 11:55:14 +0200 Subject: [PATCH 51/58] fix docstring (LaTeX) --- src/sage/combinat/k_regular_sequence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index b085d22243c..2b0df78f05f 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -996,7 +996,8 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): - ``max_exponent`` -- (default: ``10``) a positive integer specifying the maximum exponent of `k` which is tried when guessing the sequence, - i.e., relations between `f(k^t n+r)` are used for `0\le t\le \operatorname{max_exponent}` and `0\le r < k^j` + i.e., relations between `f(k^t n+r)` are used for + `0\le t\le \mathtt{max\_exponent}` and `0\le r < k^j` - ``sequence`` -- (default: ``None``) a `k`-regular sequence used for bootstrapping the guessing by adding information of the From 43f58c611bbbbd88887b29525f31550814759774 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 9 Jun 2023 12:46:03 +0200 Subject: [PATCH 52/58] remove W293 blank line contains whitespace --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 2b0df78f05f..d0eca67de67 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1056,7 +1056,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 1: [ 0 1] [-1 2]}, (0, 1)) - + The ``INFO`` messages mean that the right vector valued sequence is the sequence `(s(n), s(2n+1))^\top`. We guess again, but this time, we use a constant sequence From bb0efc5cfd2e8c6b719a22f311061a8e3eff3e33 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 9 Jun 2023 12:54:47 +0200 Subject: [PATCH 53/58] add comment and ref to #35748 --- src/sage/combinat/k_regular_sequence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index d0eca67de67..9c5a2298d31 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1290,6 +1290,8 @@ def some_inverse_U_matrix(lines): """ d = len(seq(0)) + len(lines) + # The following search for an inverse works but is inefficient; + # see :trac:`35748` for details. for m_indices in cantor_product(xsrange(n_verify), repeat=d, min_slope=1): # Iterate over all increasing lists of length d consisting # of non-negative integers less than `n_verify`. From a98c32dc29896698b371f928999b5ff96a5a6867 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 9 Jun 2023 13:05:03 +0200 Subject: [PATCH 54/58] test RuntimeError no invertible matrix --- src/sage/combinat/k_regular_sequence.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 9c5a2298d31..0b3d3ff3560 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1239,6 +1239,14 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... sage: S2 2-regular sequence 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + + :: + + sage: A = Seq2((Matrix([[1, 1], [1, 1]]), Matrix([[1, 1], [1, 1]])), left=(1, 1), right=(1, 1)) + sage: Seq2.guess(lambda n: n, sequence=A, n_verify=5) + Traceback (most recent call last): + ... + RuntimeError: no invertible submatrix found """ import logging logger = logging.getLogger(__name__) From a2d8b25f7372f5fc0fa45c1bce4c93ba479168a2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 17 Jul 2023 16:43:48 +0200 Subject: [PATCH 55/58] break long line in doctest --- src/sage/combinat/k_regular_sequence.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 0b3d3ff3560..ef5b2b368f9 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1242,7 +1242,9 @@ 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)) + sage: A = Seq2( + ....: (Matrix([[1, 1], [1, 1]]), Matrix([[1, 1], [1, 1]])), + ....: left=(1, 1), right=(1, 1)) sage: Seq2.guess(lambda n: n, sequence=A, n_verify=5) Traceback (most recent call last): ... From 1c56e7fed368a9a7200068f47f368f92542aa411 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 17 Jul 2023 17:04:53 +0200 Subject: [PATCH 56/58] implement coefficient_of_n (for __getitem__) --- src/sage/combinat/k_regular_sequence.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ef5b2b368f9..376ca634aac 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -238,7 +238,7 @@ def _repr_(self): preview=10) @cached_method - def __getitem__(self, n, **kwds): + def coefficient_of_n(self, n, **kwds): r""" Return the `n`-th entry of this sequence. @@ -256,6 +256,11 @@ def __getitem__(self, n, **kwds): sage: S[7] 3 + This is equivalent to:: + + sage: S.coefficient_of_n(7) + 3 + TESTS:: sage: S[-1] @@ -279,6 +284,8 @@ def __getitem__(self, n, **kwds): """ return self.coefficient_of_word(self.parent()._n_to_index_(n), **kwds) + __getitem__ = coefficient_of_n + def __iter__(self): r""" Return an iterator over the coefficients of this sequence. @@ -521,7 +528,7 @@ def subsequence(self, a, b): zero_M = self.mu[0].parent().zero() zero_R = self.right.parent().zero() - # Let v(n) = self.__getitem__(n, multiply_left=False) + # Let v(n) = self.coefficient_of_n(n, multiply_left=False) rule = {} # We will construct `kernel` and `rule` in such a way that for all # c in `kernel`, @@ -557,7 +564,7 @@ def matrix_row(r, c): b.get(c, 0) * self.left for c in kernel)), vector(chain.from_iterable( - (self.__getitem__(c, multiply_left=False) if c >= 0 else zero_R) + (self.coefficient_of_n(c, multiply_left=False) if c >= 0 else zero_R) for c in kernel))) return result @@ -1265,8 +1272,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): seq = lambda m: vector([]) else: mu = [M.rows() for M in sequence.mu] - seq = lambda m: (sequence._mu_of_word_(self._n_to_index_(m)) - * sequence.right) + seq = lambda m: sequence.coefficient_of_n(m, multiply_left=False) logger.info('including %s', sequence) zero = domain(0) From 1050e709297d8507c32212163a4f1f6a70bdf14a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 17 Jul 2023 17:36:10 +0200 Subject: [PATCH 57/58] fix left/right vector sequence issue in docstrings --- src/sage/combinat/k_regular_sequence.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 376ca634aac..610037e2f63 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1016,20 +1016,20 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): ALGORITHM: - For the purposes of this description, the left vector valued sequence - associated with a regular sequence consists of the left vector - multiplied by the corresponding matrix product, but without the right - vector of the regular sequence. + For the purposes of this description, the right vector valued sequence + associated with a regular sequence consists of the + corresponding matrix product multiplied by the right vector, + but without the right vector of the regular sequence. - The algorithm maintains a left vector valued sequence consisting - of the left vector valued sequence of the argument ``sequence`` + The algorithm maintains a right vector valued sequence consisting + of the right vector valued sequence of the argument ``sequence`` (replaced by an empty tuple if ``sequence`` is ``None``) plus several components of the shape `m \mapsto f(k^t\cdot m +r)` for suitable ``t`` and ``r``. Implicitly, the algorithm also maintains a `d \times n_\mathrm{verify}` matrix ``A`` - (where ``d`` is the dimension of the left vector valued sequence) - whose columns are the current left vector valued sequence evaluated at + (where ``d`` is the dimension of the right vector valued sequence) + whose columns are the current right vector valued sequence evaluated at the non-negative integers less than `n_\mathrm{verify}` and ensures that this matrix has full row rank. @@ -1282,12 +1282,12 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): # `k**t * m + r` # The elements of `lines` will correspond to the current components - # of the left vector valued sequence described in the algorithm section + # of the right vector valued sequence described in the algorithm section # of the docstring. def values(m, lines): """ - Return current (as defined by ``lines``) left vector valued + Return current (as defined by ``lines``) right vector valued sequence for argument ``m``. """ return tuple(seq(m)) + tuple(f(k**t_R * m + r_R) for t_R, r_R in lines) @@ -1302,7 +1302,7 @@ def some_inverse_U_matrix(lines): The output is the inverse of the invertible submatrix and the corresponding list of column indices (i.e., arguments to - the current left vector valued sequence). + the current right vector valued sequence). """ d = len(seq(0)) + len(lines) From 822ce11707ea5408f5f37550b299d49271777a90 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 17 Jul 2023 19:24:50 +0200 Subject: [PATCH 58/58] Update src/sage/combinat/k_regular_sequence.py Co-authored-by: cheuberg --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 610037e2f63..65a10ef34dd 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1019,7 +1019,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): For the purposes of this description, the right vector valued sequence associated with a regular sequence consists of the corresponding matrix product multiplied by the right vector, - but without the right vector of the regular sequence. + but without the left vector of the regular sequence. The algorithm maintains a right vector valued sequence consisting of the right vector valued sequence of the argument ``sequence``