From 55066e13a5da72641f1a9cc024744293d05e386b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Sep 2023 11:57:40 +0900 Subject: [PATCH 001/507] Initial implementation of integral() for lazy series; improvements to derivative. --- src/sage/data_structures/stream.py | 137 ++++++++- src/sage/rings/lazy_series.py | 478 ++++++++++++++++++++++++++++- 2 files changed, 610 insertions(+), 5 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 8838a4f1754..b1ae06cbcee 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -3371,7 +3371,7 @@ def __getitem__(self, n): sage: [f2[i] for i in range(-1, 4)] [0, 2, 6, 12, 20] """ - return (prod(n + k for k in range(1, self._shift + 1)) + return (ZZ.prod(range(n + 1, n + self._shift + 1)) * self._series[n + self._shift]) def __hash__(self): @@ -3433,6 +3433,141 @@ def is_nonzero(self): return self._series.is_nonzero() +class Stream_integral(Stream_unary): + """ + Operator for taking integrals of a non-exact stream. + + INPUT: + + - ``series`` -- a :class:`Stream` + - ``integration_constants`` -- a list of integration constants + - ``is_sparse`` -- boolean + """ + def __init__(self, series, integration_constants, is_sparse): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_exact, Stream_integral + sage: f = Stream_exact([1, 2, 3]) + sage: f2 = Stream_integral(f, [-2], True) + sage: TestSuite(f2).run() + """ + self._shift = len(integration_constants) + self._int_consts = tuple(integration_constants) + super().__init__(series, is_sparse, False) + + @lazy_attribute + def _approximate_order(self): + """ + Compute and return the approximate order of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_integral + sage: f = Stream_function(lambda n: (n+1)*(n+2), True, 2) + sage: h = Stream_integral(f, [0, 0], True) + sage: h._approximate_order + 0 + sage: [h[i] for i in range(10)] + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1] + sage: h._approximate_order + 4 + """ + # this is generally not the true order + return min(self._series._approximate_order + self._shift, 0) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_integral + sage: f = Stream_function(lambda n: n + 1, True, -3) + sage: [f[i] for i in range(-3, 4)] + [-2, -1, 0, 1, 2, 3, 4] + sage: f2 = Stream_integral(f, [0], True) + sage: [f2[i] for i in range(-3, 5)] + [0, 1, 1, 0, 1, 1, 1, 1] + + sage: f = Stream_function(lambda n: (n + 1)*(n+2), True, 2) + sage: [f[i] for i in range(-1, 4)] + [0, 0, 0, 12, 20] + sage: f2 = Stream_integral(f, [-1, -1, -1], True) + sage: [f2[i] for i in range(-1, 7)] + [0, -1, -1, -1/2, 0, 0, 1/5, 1/6] + """ + if 0 <= n < self._shift: + return (self._int_consts[n] / ZZ.prod(range(2, n + 1))) + return (self._series[n - self._shift] / + ZZ.prod(range(n - self._shift + 1, n + 1))) + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_integral + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: f = Stream_integral(a, [0, 1], True) + sage: g = Stream_integral(a, [0, 2], True) + sage: hash(f) == hash(f) + True + sage: hash(f) == hash(g) + False + """ + return hash((type(self), self._series, self._int_consts)) + + def __eq__(self, other): + """ + Return whether ``self`` and ``other`` are known to be equal. + + INPUT: + + - ``other`` -- a stream + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_integral + sage: a = Stream_function(lambda n: 2*n, False, 1) + sage: f = Stream_integral(a, [1], True) + sage: g = Stream_integral(a, [2], True) + sage: f == g + False + sage: f == Stream_integral(a, [1], True) + True + """ + return (isinstance(other, type(self)) + and self._int_consts == other._int_consts + and self._series == other._series) + + def is_nonzero(self): + r""" + Return ``True`` if and only if this stream is known + to be non-zero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_integral + sage: f = Stream_function(lambda n: 2*n, True, 1) + sage: f[1] + 2 + sage: f.is_nonzero() + True + sage: Stream_integral(f, [0], True).is_nonzero() + True + sage: f = Stream_function(lambda n: 0, False, 1) + sage: Stream_integral(f, [0, 0, 0], False).is_nonzero() + False + sage: Stream_integral(f, [0, 2], False).is_nonzero() + True + """ + return self._series.is_nonzero() or any(self._int_consts) + + class Stream_infinite_operator(Stream): r""" Stream defined by applying an operator an infinite number of times. diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 8086e15f9ef..7124419c847 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -257,6 +257,7 @@ Stream_truncated, Stream_function, Stream_derivative, + Stream_integral, Stream_dirichlet_convolve, Stream_dirichlet_invert, Stream_plethysm @@ -4533,6 +4534,172 @@ def derivative(self, *args): P.is_sparse()) return P.element_class(P, coeff_stream) + def integral(self, variable=None, integration_constants=None): + r""" + Return the integral of ``self`` with respect to ``variable``. + + INPUT: + + - ``variable`` -- (optional) the variable to integrate + - ``integration_constants`` -- (optional) list of integration + constants for the integrals of ``self`` (the last constant + corresponds to the first integral) + + If the first argument is a list, then this method iterprets it as + integration constants. If it is a positive integer, the method + interprets it as the number of times to integrate the function. + If ``variable`` is not the variable of the Laurent series, then + the coefficients are integrated with respect to ``variable``. + + If the integration constants are not specified, they are considered + to be `0`. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = t^-3 + 2 + 3*t + t^5 + sage: f.integral() + -1/2*t^-2 + 2*t + 3/2*t^2 + 1/6*t^6 + sage: f.integral([-2, -2]) + 1/2*t^-1 - 2 - 2*t + t^2 + 1/2*t^3 + 1/42*t^7 + sage: f.integral(t) + -1/2*t^-2 + 2*t + 3/2*t^2 + 1/6*t^6 + sage: f.integral(2) + 1/2*t^-1 + t^2 + 1/2*t^3 + 1/42*t^7 + sage: L.zero().integral() + 0 + sage: L.zero().integral([0, 1, 2, 3]) + t + t^2 + 1/2*t^3 + + We solve the ODE `f' = a f` by integrating both sides and + the recursive definition:: + + sage: R. = QQ[] + sage: L. = LazyLaurentSeriesRing(R) + sage: f = L.undefined(0) + sage: f.define((a*f).integral([C])) + sage: f + C + a*C*x + 1/2*a^2*C*x^2 + 1/6*a^3*C*x^3 + 1/24*a^4*C*x^4 + + 1/120*a^5*C*x^5 + 1/720*a^6*C*x^6 + O(x^7) + sage: C * exp(a*x) + C + a*C*x + 1/2*a^2*C*x^2 + 1/6*a^3*C*x^3 + 1/24*a^4*C*x^4 + + 1/120*a^5*C*x^5 + 1/720*a^6*C*x^6 + O(x^7) + + We can integrate both the series and coefficients:: + + sage: R. = QQ[] + sage: L. = LazyLaurentSeriesRing(R) + sage: f = (x*t^2 + y*t^-2 + z)^2; f + y^2*t^-4 + 2*y*z*t^-2 + (2*x*y + z^2) + 2*x*z*t^2 + x^2*t^4 + sage: f.integral(x) + x*y^2*t^-4 + 2*x*y*z*t^-2 + (x^2*y + x*z^2) + x^2*z*t^2 + 1/3*x^3*t^4 + sage: f.integral(t) + -1/3*y^2*t^-3 - 2*y*z*t^-1 + (2*x*y + z^2)*t + 2/3*x*z*t^3 + 1/5*x^2*t^5 + sage: f.integral(y, [x*y*z]) + -1/9*y^3*t^-3 - y^2*z*t^-1 + x*y*z + (x*y^2 + y*z^2)*t + 2/3*x*y*z*t^3 + 1/5*x^2*y*t^5 + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = t^-2 + sage: f.integral(t, [0, 0, 0]) + Traceback (most recent call last): + ... + ValueError: cannot integrate 3 times the series t^-2 + sage: f = t^-5 + t^-2 + sage: f.integral(3) + Traceback (most recent call last): + ... + ValueError: cannot integrate 3 times the series t^-5 + t^-2 + sage: f.integral([0, 1], [0, 1]) + Traceback (most recent call last): + ... + ValueError: integration constants given twice + sage: f.integral(4, [0, 1]) + Traceback (most recent call last): + ... + ValueError: the number of integrations does not match the number of integration constants + """ + P = self.parent() + zero = P.base_ring().zero() + if variable is None: + if integration_constants is None: + integration_constants = [zero] + elif variable != P.gen(): + if isinstance(variable, (list, tuple)): + if integration_constants is not None: + raise ValueError("integration constants given twice") + integration_constants = tuple(variable) + variable = None + elif variable in ZZ and ZZ(variable) >= 0: + if integration_constants is None: + integration_constants = [zero] * ZZ(variable) + elif ZZ(variable) != len(integration_constants): + raise ValueError("the number of integrations does not match" + " the number of integration constants") + variable = None + if integration_constants is None: + integration_constants = [] + else: + if integration_constants is None: + integration_constants = [zero] + variable = None + + nints = len(integration_constants) + + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, Stream_zero): + if any(integration_constants): + coeff_stream = Stream_exact([c / ZZ.prod(k for k in range(1, i+1)) + for i, c in enumerate(integration_constants)], + order=0, + constant=zero) + return P.element_class(P, coeff_stream) + return self + + if (isinstance(coeff_stream, Stream_exact) and not coeff_stream._constant): + coeffs = [c / ZZ.prod(k for k in range(1, i+1)) + for i, c in enumerate(integration_constants)] + if coeff_stream._approximate_order < 0: + ic = coeff_stream._initial_coefficients + ao = coeff_stream._approximate_order + if nints > -ao or any(ic[-ao-nints:-ao]): + raise ValueError(f"cannot integrate {nints} times the series {self}") + if variable is not None: + coeffs = [c.integral(variable) / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic[:-ao-nints], ao)] + coeffs + else: + coeffs = [c / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic[:-ao-nints], ao)] + coeffs + + ic = ic[-ao:] + val = ao + nints + ao = 0 + else: + coeffs += [zero] * coeff_stream._approximate_order + ic = coeff_stream._initial_coefficients + val = 0 + ao = coeff_stream._approximate_order + if variable: + coeffs += [c.integral(variable) / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic, ao)] + else: + coeffs += [c / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic, ao)] + if not any(coeffs): + return P.zero() + coeff_stream = Stream_exact(coeffs, order=val, constant=zero) + return P.element_class(P, coeff_stream) + + if nints: + coeff_stream = Stream_integral(coeff_stream, integration_constants, P.is_sparse()) + + if variable is not None: + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: c.integral(variable), + P.is_sparse()) + return P.element_class(P, coeff_stream) + def approximate_series(self, prec, name=None): r""" Return the Laurent series with absolute precision ``prec`` approximated @@ -5301,14 +5468,18 @@ def derivative(self, *args): sage: T. = LazyPowerSeriesRing(ZZ) sage: z.derivative() 1 - sage: (1+z+z^2).derivative(3) + sage: (1 + z + z^2).derivative(3) 0 - sage: (1/(1-z)).derivative() + sage: (z^2 + z^4 + z^10).derivative(3) + 24*z + 720*z^7 + sage: (1 / (1-z)).derivative() 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) + sage: T([1, 1, 1], constant=4).derivative() + 1 + 2*z + 12*z^2 + 16*z^3 + 20*z^4 + 24*z^5 + 28*z^6 + O(z^7) sage: R. = QQ[] sage: L. = LazyPowerSeriesRing(R) - sage: f = 1/(1-q*x+y); f + sage: f = 1 / (1-q*x+y); f 1 + (q*x-y) + (q^2*x^2+(-2*q)*x*y+y^2) + (q^3*x^3+(-3*q^2)*x^2*y+3*q*x*y^2-y^3) + (q^4*x^4+(-4*q^3)*x^3*y+6*q^2*x^2*y^2+(-4*q)*x*y^3+y^4) @@ -5322,6 +5493,53 @@ def derivative(self, *args): + (6*q^5*x^6+(-30*q^4)*x^5*y+60*q^3*x^4*y^2+(-60*q^2)*x^3*y^3+30*q*x^2*y^4+(-6)*x*y^5) + O(x,y)^7 + Multivariate:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = (x + y^2 + z)^3; f + (x^3+3*x^2*z+3*x*z^2+z^3) + (3*x^2*y^2+6*x*y^2*z+3*y^2*z^2) + (3*x*y^4+3*y^4*z) + y^6 + sage: f.derivative(x) + (3*x^2+6*x*z+3*z^2) + (6*x*y^2+6*y^2*z) + 3*y^4 + sage: f.derivative(y, 5) + 720*y + sage: f.derivative(z, 5) + 0 + sage: f.derivative(x, y, z) + 12*y + + sage: f = (1 + x + y^2 + z)^-1 + sage: f.derivative(x) + -1 + (2*x+2*z) + (-3*x^2+2*y^2-6*x*z-3*z^2) + ... + O(x,y,z)^6 + sage: f.derivative(y, 2) + -2 + (4*x+4*z) + (-6*x^2+12*y^2-12*x*z-6*z^2) + ... + O(x,y,z)^5 + sage: f.derivative(x, y) + 4*y + (-12*x*y-12*y*z) + (24*x^2*y-12*y^3+48*x*y*z+24*y*z^2) + + (-40*x^3*y+48*x*y^3-120*x^2*y*z+48*y^3*z-120*x*y*z^2-40*y*z^3) + O(x,y,z)^5 + sage: f.derivative(x, y, z) + (-12*y) + (48*x*y+48*y*z) + (-120*x^2*y+48*y^3-240*x*y*z-120*y*z^2) + O(x,y,z)^4 + + sage: R. = QQ[] + sage: L. = LazyPowerSeriesRing(R) + sage: f = ((t^2-3)*x + t*y^2 - t*z)^2 + sage: f.derivative(t,x,t,y) + 24*t*y + sage: f.derivative(t, 2) + ((12*t^2-12)*x^2+(-12*t)*x*z+2*z^2) + (12*t*x*y^2+(-4)*y^2*z) + 2*y^4 + sage: f.derivative(z, t) + ((-6*t^2+6)*x+4*t*z) + ((-4*t)*y^2) + sage: f.derivative(t, 10) + 0 + + sage: f = (1 + t*(x + y + z))^-1 + sage: f.derivative(x, t, y) + 4*t + ((-18*t^2)*x+(-18*t^2)*y+(-18*t^2)*z) + + (48*t^3*x^2+96*t^3*x*y+48*t^3*y^2+96*t^3*x*z+96*t^3*y*z+48*t^3*z^2) + + ... + O(x,y,z)^5 + sage: f.derivative(t, 2) + (2*x^2+4*x*y+2*y^2+4*x*z+4*y*z+2*z^2) + ... + O(x,y,z)^7 + sage: f.derivative(x, y, z, t) + (-18*t^2) + (96*t^3*x+96*t^3*y+96*t^3*z) + ... + O(x,y,z)^4 + TESTS: Check that :issue:`36154` is fixed:: @@ -5341,7 +5559,7 @@ def derivative(self, *args): if x is None: order += 1 elif x in V: - gen_vars.append(x) + gen_vars.append(x._coeff_stream[1]) else: vars.append(x) @@ -5357,6 +5575,16 @@ def derivative(self, *args): if P._arity > 1: v = gen_vars + vars d = -len(gen_vars) + + if isinstance(coeff_stream, Stream_exact): # the constant should be 0 + ao = coeff_stream._approximate_order + val = max(ao + d, 0) + coeffs = [R(c).derivative(v) for c in coeff_stream._initial_coefficients[val-(ao+d):]] + if any(coeffs): + coeff_stream = Stream_exact(coeffs, order=val, constant=coeff_stream._constant) + return P.element_class(P, coeff_stream) + return P.zero() + coeff_stream = Stream_map_coefficients(coeff_stream, lambda c: R(c).derivative(v), P.is_sparse()) @@ -5390,6 +5618,248 @@ def derivative(self, *args): P.is_sparse()) return P.element_class(P, coeff_stream) + def integral(self, variable=None, integration_constants=None): + r""" + Return the integral of ``self`` with respect to ``variable``. + + INPUT: + + - ``variable`` -- (optional) the variable to integrate + - ``integration_constants`` -- (optional) list of integration + constants for the integrals of ``self`` (the last constant + corresponds to the first integral) + + For multivariable series, then only ``variable`` should be + specified; the integration constant is taken to be `0`. + + Now we assume the series is univariate. If the first argument is a + list, then this method iterprets it as integration constants. If it + is a positive integer, the method interprets it as the number of times + to integrate the function. If ``variable`` is not the variable of + the power series, then the coefficients are integrated with respect + to ``variable``. If the integration constants are not specified, + they are considered to be `0`. + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = 2 + 3*t + t^5 + sage: f.integral() + 2*t + 3/2*t^2 + 1/6*t^6 + sage: f.integral([-2, -2]) + -2 - 2*t + t^2 + 1/2*t^3 + 1/42*t^7 + sage: f.integral(t) + 2*t + 3/2*t^2 + 1/6*t^6 + sage: f.integral(2) + t^2 + 1/2*t^3 + 1/42*t^7 + sage: (t^3 + t^5).integral() + 1/4*t^4 + 1/6*t^6 + sage: L.zero().integral() + 0 + sage: L.zero().integral([0, 1, 2, 3]) + t + t^2 + 1/2*t^3 + sage: L([1, 2 ,3], constant=4).integral() + t + t^2 + t^3 + t^4 + 4/5*t^5 + 2/3*t^6 + O(t^7) + + We solve the ODE `f'' - f' - 2 f = 0` by solving for `f''`, then + integrating and applying a recursive definition:: + + sage: R. = QQ[] + sage: L. = LazyPowerSeriesRing(R) + sage: f = L.undefined() + sage: f.define((f.derivative() + 2*f).integral([C, D])) + sage: f + C + D*x + ((C+1/2*D)*x^2) + ((1/3*C+1/2*D)*x^3) + + ((1/4*C+5/24*D)*x^4) + ((1/12*C+11/120*D)*x^5) + + ((11/360*C+7/240*D)*x^6) + O(x^7) + sage: f.derivative(2) - f.derivative() - 2*f + O(x^7) + + We compare this with the answer we get from the + characteristic polynomial:: + + sage: g = C * exp(-x) + D * exp(2*x); g + (C+D) + ((-C+2*D)*x) + ((1/2*C+2*D)*x^2) + ((-1/6*C+4/3*D)*x^3) + + ((1/24*C+2/3*D)*x^4) + ((-1/120*C+4/15*D)*x^5) + + ((1/720*C+4/45*D)*x^6) + O(x^7) + sage: g.derivative(2) - g.derivative() - 2*g + O(x^7) + + Note that ``C`` and ``D`` are playing different roles, so we need + to perform a substitution to the coefficients of ``f`` to recover + the solution ``g``:: + + sage: fp = f.map_coefficients(lambda c: c(C=C+D, D=2*D-C)); fp + (C+D) + ((-C+2*D)*x) + ((1/2*C+2*D)*x^2) + ((-1/6*C+4/3*D)*x^3) + + ((1/24*C+2/3*D)*x^4) + ((-1/120*C+4/15*D)*x^5) + + ((1/720*C+4/45*D)*x^6) + O(x^7) + sage: fp - g + O(x^7) + + We can integrate both the series and coefficients:: + + sage: R. = QQ[] + sage: L. = LazyPowerSeriesRing(R) + sage: f = (x*t^2 + y*t + z)^2; f + z^2 + 2*y*z*t + ((y^2+2*x*z)*t^2) + 2*x*y*t^3 + x^2*t^4 + sage: f.integral(x) + x*z^2 + 2*x*y*z*t + ((x*y^2+x^2*z)*t^2) + x^2*y*t^3 + 1/3*x^3*t^4 + sage: f.integral(t) + z^2*t + y*z*t^2 + ((1/3*y^2+2/3*x*z)*t^3) + 1/2*x*y*t^4 + 1/5*x^2*t^5 + sage: f.integral(y, [x*y*z]) + x*y*z + y*z^2*t + 1/2*y^2*z*t^2 + ((1/9*y^3+2/3*x*y*z)*t^3) + 1/4*x*y^2*t^4 + 1/5*x^2*y*t^5 + + We can integrate multivariate power series:: + + sage: R. = QQ[] + sage: L. = LazyPowerSeriesRing(R) + sage: f = ((t^2 + t) - t * y^2 + t^2 * (y + z))^2; f + (t^4+2*t^3+t^2) + ((2*t^4+2*t^3)*y+(2*t^4+2*t^3)*z) + + ((t^4-2*t^3-2*t^2)*y^2+2*t^4*y*z+t^4*z^2) + + ((-2*t^3)*y^3+(-2*t^3)*y^2*z) + t^2*y^4 + sage: g = f.integral(x); g + ((t^4+2*t^3+t^2)*x) + ((2*t^4+2*t^3)*x*y+(2*t^4+2*t^3)*x*z) + + ((t^4-2*t^3-2*t^2)*x*y^2+2*t^4*x*y*z+t^4*x*z^2) + + ((-2*t^3)*x*y^3+(-2*t^3)*x*y^2*z) + t^2*x*y^4 + sage: g[0] + 0 + sage: g[1] + (t^4 + 2*t^3 + t^2)*x + sage: g[2] + (2*t^4 + 2*t^3)*x*y + (2*t^4 + 2*t^3)*x*z + sage: f.integral(z) + ((t^4+2*t^3+t^2)*z) + ((2*t^4+2*t^3)*y*z+(t^4+t^3)*z^2) + + ((t^4-2*t^3-2*t^2)*y^2*z+t^4*y*z^2+1/3*t^4*z^3) + + ((-2*t^3)*y^3*z+(-t^3)*y^2*z^2) + t^2*y^4*z + sage: f.integral(t) + (1/5*t^5+1/2*t^4+1/3*t^3) + ((2/5*t^5+1/2*t^4)*y+(2/5*t^5+1/2*t^4)*z) + + ((1/5*t^5-1/2*t^4-2/3*t^3)*y^2+2/5*t^5*y*z+1/5*t^5*z^2) + + ((-1/2*t^4)*y^3+(-1/2*t^4)*y^2*z) + 1/3*t^3*y^4 + + sage: L. = LazyPowerSeriesRing(QQ) + sage: (x + y - z^2).integral(z) + (x*z+y*z) + (-1/3*z^3) + + TESTS:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = t^2 + sage: f.integral([0, 1], [0, 1]) + Traceback (most recent call last): + ... + ValueError: integration constants given twice + sage: f.integral(4, [0, 1]) + Traceback (most recent call last): + ... + ValueError: the number of integrations does not match the number of integration constants + + sage: L. = LazyPowerSeriesRing(QQ) + sage: x.integral(y, [2]) + Traceback (most recent call last): + ... + ValueError: integration constants must not be given for multivariate series + sage: x.integral() + Traceback (most recent call last): + ... + ValueError: the integration variable must be specified + """ + P = self.parent() + coeff_stream = self._coeff_stream + R = P._laurent_poly_ring + + if P._arity > 1: + if integration_constants is not None: + raise ValueError("integration constants must not be given for multivariate series") + if variable is None: + raise ValueError("the integration variable must be specified") + + if isinstance(coeff_stream, Stream_zero): + return self + + if variable in P.gens(): + variable = variable._coeff_stream[1] + shift = 1 + else: + shift = 0 + + if isinstance(coeff_stream, Stream_exact): # the constant should be 0 + ao = coeff_stream._approximate_order + coeffs = [R(c).integral(variable) for c in coeff_stream._initial_coefficients] + coeff_stream = Stream_exact(coeffs, order=ao+shift, constant=coeff_stream._constant) + return P.element_class(P, coeff_stream) + + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: c.integral(variable), + P.is_sparse()) + if shift: + coeff_stream = Stream_shift(coeff_stream, 1) + return P.element_class(P, coeff_stream) + + # the univariate case + + zero = P.base_ring().zero() + # This is copied from the LazyLaurentSeries.integral + if variable is None: + if integration_constants is None: + integration_constants = [zero] + elif variable != P.gen(): + if isinstance(variable, (list, tuple)): + if integration_constants is not None: + raise ValueError("integration constants given twice") + integration_constants = tuple(variable) + variable = None + elif variable in ZZ and ZZ(variable) >= 0: + if integration_constants is None: + integration_constants = [zero] * ZZ(variable) + elif ZZ(variable) != len(integration_constants): + raise ValueError("the number of integrations does not match" + " the number of integration constants") + variable = None + if integration_constants is None: + integration_constants = [] + else: + if integration_constants is None: + integration_constants = [zero] + variable = None + + nints = len(integration_constants) + + if isinstance(coeff_stream, Stream_zero): + if any(integration_constants): + coeff_stream = Stream_exact([c / ZZ.prod(k for k in range(1, i+1)) + for i, c in enumerate(integration_constants)], + order=0, + constant=zero) + return P.element_class(P, coeff_stream) + + return self + + if (isinstance(coeff_stream, Stream_exact) and not coeff_stream._constant): + coeffs = [c / ZZ.prod(k for k in range(1, i+1)) + for i, c in enumerate(integration_constants)] + coeffs += [zero] * coeff_stream._approximate_order + ic = coeff_stream._initial_coefficients + ao = coeff_stream._approximate_order + if variable: + coeffs += [c.integral(variable) / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic, ao)] + else: + coeffs += [c / ZZ.prod(i+k for k in range(1, nints+1)) + for i, c in enumerate(ic, ao)] + if not any(coeffs): + return P.zero() + coeff_stream = Stream_exact(coeffs, order=0, constant=zero) + return P.element_class(P, coeff_stream) + + if nints: + coeff_stream = Stream_integral(coeff_stream, integration_constants, P.is_sparse()) + + if variable is not None: + coeff_stream = Stream_map_coefficients(coeff_stream, + lambda c: c.integral(variable), + P.is_sparse()) + return P.element_class(P, coeff_stream) + def _format_series(self, formatter, format_strings=False): """ Return nonzero ``self`` formatted by ``formatter``. From 419701c10f4c10c8bc072b3655b3d39c5501ae14 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 10 Oct 2023 12:20:10 +0900 Subject: [PATCH 002/507] Adding functional equation and taylor series constructions. --- src/sage/data_structures/stream.py | 338 ++++++++++++++++++++++++++++- src/sage/rings/lazy_series_ring.py | 99 ++++++++- 2 files changed, 434 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index b1ae06cbcee..16224579f4f 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -103,6 +103,7 @@ from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.misc.cachefunc import cached_method +from copy import copy lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -213,6 +214,14 @@ def is_uninitialized(self): """ return False + def replace(self, stream, sub): + """ + Return ``self`` except with ``stream`` replaced by ``sub``. + + The default is to return ``self``. + """ + return self + class Stream_inexact(Stream): """ @@ -596,7 +605,6 @@ class Stream_exact(Stream): The convention for ``order`` is different to the one in :class:`sage.rings.lazy_series_ring.LazySeriesRing`, where the input is shifted to have the prescribed order. - """ def __init__(self, initial_coefficients, constant=None, degree=None, order=None): """ @@ -1038,6 +1046,142 @@ def __eq__(self, other): return isinstance(other, type(self)) and self.get_coefficient == other.get_coefficient +class Stream_taylor(Stream_inexact): + r""" + Class that returns a stream for the Taylor series of a function. + + INPUT: + + - ``function`` -- a function that has a ``derivative`` method and takes + a single input + - ``is_sparse`` -- boolean; specifies whether the stream is sparse + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: g(x) = sin(x) + sage: f = Stream_taylor(g, False) + sage: f[3] + -1/6 + sage: [f[i] for i in range(10)] + [0, 1, 0, -1/6, 0, 1/120, 0, -1/5040, 0, 1/362880] + + sage: g(y) = cos(y) + sage: f = Stream_taylor(g, False) + sage: n = f.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [1, 0, -1/2, 0, 1/24, 0, -1/720, 0, 1/40320, 0] + + sage: g(z) = 1 / (1 - 2*z) + sage: f = Stream_taylor(g, True) + sage: [f[i] for i in range(4)] + [1, 2, 4, 8] + """ + def __init__(self, function, is_sparse): + """ + Initialize. + + TESTS:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: f = Stream_taylor(polygen(QQ, 'x')^3, False) + sage: TestSuite(f).run(skip="_test_pickling") + """ + self._func = function + super().__init__(is_sparse, False) + self._approximate_order = 0 + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: x = polygen(QQ, 'x') + sage: f = Stream_taylor(x^3 + x - 2, True) + sage: g = Stream_taylor(x^3 + x - 2, False) + sage: hash(f) == hash(g) + True + """ + # We don't hash the function as it might not be hashable. + return hash(type(self)) + + def __eq__(self, other): + r""" + Return whether ``self`` and ``other`` are known to be equal. + + INPUT: + + - ``other`` -- a stream + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: x = polygen(QQ, 'x') + sage: fun = x^2 + sage: f = Stream_taylor(x^2, True) + sage: g = Stream_taylor(x^2, False) + sage: h = Stream_taylor((x^3 + x^2) / (x + 1), False) + sage: f == g + True + sage: f == h + True + + sage: F(y) = cos(y)^2 + sin(y)^2 + y + sage: G(y) = y + 1 + sage: f = Stream_taylor(F, True) + sage: g = Stream_taylor(G, False) + sage: f == g + True + """ + # The bool call is needed when passing functions in SR + return isinstance(other, type(self)) and bool(self._func == other._func) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of ``self``. + + INPUT: + + - ``n`` -- integer; the degree for the coefficient + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: g(x) = exp(x) + sage: f = Stream_taylor(g, True) + sage: f.get_coefficient(5) + 1/120 + """ + if n == 0: + return self._func(ZZ.zero()) + from sage.functions.other import factorial + return self._func.derivative(n)(ZZ.zero()) / factorial(n) + + def iterate_coefficients(self): + """ + A generator for the coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_taylor + sage: x = polygen(QQ, 'x') + sage: f = Stream_taylor(x^3, False) + sage: it = f.iterate_coefficients() + sage: [next(it) for _ in range(10)] + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] + """ + cur = self._func + n = 0 + denom = 1 + while True: + yield cur(ZZ.zero()) / denom + cur = cur.derivative() + n += 1 + denom *= n + + class Stream_uninitialized(Stream_inexact): r""" Coefficient stream for an uninitialized series. @@ -1134,6 +1278,128 @@ def is_uninitialized(self): return result +class Stream_functional_equation(Stream_inexact): + r""" + Coefficient stream defined by a functional equation `F = 0`. + + INPUT: + + - ``approximate_order`` -- integer; a lower bound for the order + of the stream + - ``F`` -- the stream for the equation using ``uninitialized`` + - ``uninitialized`` -- the uninitialized stream + - ``initial_values`` -- the initial values + + Instances of this class are always dense. + + EXAMPLES:: + """ + def __init__(self, approximate_order, F, uninitialized, initial_values, true_order=False): + """ + Initialize ``self``. + + TESTS:: + + sage: from sage.data_structures.stream import Stream_uninitialized + sage: C = Stream_uninitialized(0) + sage: TestSuite(C).run(skip="_test_pickling") + """ + if approximate_order is None: + raise ValueError("the valuation must be specified for undefined series") + if initial_values is None: + initial_values = [] + self._start = approximate_order + for i, val in enumerate(initial_values): + if val: + approximate_order += i + true_order = True + break + else: + approximate_order += len(initial_values) + super().__init__(False, true_order) + self._F = F + self._initial_values = initial_values + self._approximate_order = approximate_order + self._uninitialized = uninitialized + self._uninitialized._approximate_order = approximate_order + self._uninitialized._target = self + + def iterate_coefficients(self): + """ + A generator for the coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized + sage: from sage.data_structures.stream import Stream_exact + sage: z = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(0) + sage: C._target + sage: C._target = z + sage: n = C.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] + """ + yield from self._initial_values + + from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing + P = InfinitePolynomialRing(ZZ, 'x') + x = P.gen() + PFF = P.fraction_field() + + def get_coeff(n): + # Check against the initial values first + i = n - self._start + if n < len(self._initial_values): + return P(self._initial_values[n]) + + # We are always a dense implementation + # Check to see if we have already computed the value + if n < self._approximate_order: + return P.zero() + if self._true_order: + i = n - self._approximate_order + if i < len(self._cache): + return P(self._cache[i]) + + return x[i] + + sf = Stream_function(get_coeff, is_sparse=False, approximate_order=self._start, true_order=True) + self._F = self._F.replace(self._uninitialized, sf) + + n = self._start + offset = len(self._initial_values) - self._start + while True: + coeff = self._F[n] + if coeff.parent() is PFF: + coeff = coeff.numerator() + + V = coeff.variables() + if not V: + if coeff: + raise ValueError(f"no solution from degree {n} as {coeff} == 0") + yield ZZ.zero() + n += 1 + continue + + if len(V) > 1: + # Substitute for known variables + coeff = coeff.subs({x[i]: val for i, val in enumerate(sf._cache)}) + V = coeff.variables() + if len(V) > 1: + raise ValueError(f"unable to determine a unique solution from degree {n}") + + # single variable to solve for + hc = coeff.homogeneous_components() + if not set(hc).issubset([0,1]): + raise ValueError(f"unable to determine a unique solution from degree {n}") + val = -hc.get(0, P.zero()).lc() / hc[1].lc() + # Update the cache + sf._cache[n + offset] = val + yield val + n += 1 + + class Stream_unary(Stream_inexact): r""" Base class for unary operators on coefficient streams. @@ -1225,6 +1491,26 @@ def is_uninitialized(self): """ return self._series.is_uninitialized() + def replace(self, stream, sub): + r""" + Return ``self`` except with ``stream`` replaced by ``sub``. + + .. WARNING:: + + This does not update the approximate order or the cache. + """ + if self._series == stream: + ret = copy(self) + ret._series = sub + else: + temp = self._series.replace(stream, sub) + if temp == self._series: + ret = self + else: + ret = copy(self) + ret._series = temp + return ret + class Stream_binary(Stream_inexact): """ @@ -1247,7 +1533,6 @@ class Stream_binary(Stream_inexact): sage: [h[i] for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] """ - def __init__(self, left, right, is_sparse): """ Initialize ``self``. @@ -1331,6 +1616,35 @@ def is_uninitialized(self): """ return self._left.is_uninitialized() or self._right.is_uninitialized() + def replace(self, stream, sub): + r""" + Return ``self`` except with ``stream`` replaced by ``sub``. + + .. WARNING:: + + This does not update the approximate order or the cache. + """ + if self._left == stream: + ret = copy(self) + ret._left = sub + else: + temp = self._left.replace(stream, sub) + if temp == self._left: + ret = self + else: + ret = copy(self) + ret._left = temp + # It is possible that both the left and right are the same stream + if self._right == stream: + ret = copy(ret) + ret._right = sub + else: + temp = ret._right.replace(stream, sub) + if not (temp == self._right): + ret = copy(ret) + ret._right = temp + return ret + class Stream_binaryCommutative(Stream_binary): r""" @@ -3056,6 +3370,26 @@ def is_uninitialized(self): """ return self._series.is_uninitialized() + def replace(self, stream, sub): + r""" + Return ``self`` except with ``stream`` replaced by ``sub``. + + .. WARNING:: + + This does not update the approximate order. + """ + if self._series == stream: + ret = copy(self) + ret._series = sub + else: + temp = self._series.replace(stream, sub) + if temp == self._series: + ret = self + else: + ret = copy(self) + ret._series = temp + return ret + class Stream_truncated(Stream_unary): """ diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 07e08938696..4a3e3afb1cb 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -87,7 +87,10 @@ Stream_function, Stream_iterator, Stream_exact, - Stream_uninitialized + Stream_uninitialized, + Stream_sub, + Stream_taylor, + Stream_functional_equation ) from types import GeneratorType @@ -1792,6 +1795,43 @@ def residue_field(self): raise TypeError("the base ring is not a field") return R + def taylor(self, f): + r""" + Return the Taylor expansion of the function ``f``. + + INPUT: + + - ``f`` -- a function such that one of the following works: + + * the substitution `f(z)`, where `z` is generator of ``self`` + * `f` is a function of a single variable with no poles and has a + ``derivative`` method + """ + try: + return f(self.gen()) + except (ValueError, TypeError): + pass + stream = Stream_taylor(f, self.is_sparse()) + return self.element_class(self, stream) + + def functional_equation(self, left, right, series, initial_values): + r""" + Define the lazy undefined ``series`` that solves the functional + equation ``left == right`` with ``initial_values``. + """ + if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: + raise ValueError("series already defined") + + left = self(left) + right = self(right) + cs = series._coeff_stream + ao = cs._approximate_order + R = self.base_ring() + initial_values = [R(val) for val in initial_values] + F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) + ret = Stream_functional_equation(ao, F, cs, initial_values) + series._coeff_stream = ret + # === special functions === def q_pochhammer(self, q=None): @@ -2503,6 +2543,63 @@ def some_elements(self): elts.extend([(z-3)*(2+z)**2, (1 - 2*z**3)/(1 - z + 3*z**2), self(lambda n: sum_gens**n)]) return elts + def taylor(self, f): + r""" + Return the Taylor expansion of the function ``f``. + + INPUT: + + - ``f`` -- a function such that one of the following works: + + * the substitution `f(z_1, \ldots, z_n)`, where `(z_1, \ldots, z_n)` + are the generators of ``self`` + * `f` is a function of a single variable with no poles and has a + ``derivative`` method + """ + try: + return f(*self.gens()) + except (ValueError, TypeError): + pass + if self._arity != 1: + raise NotImplementedError("only implemented generically for one variable") + stream = Stream_taylor(f, self.is_sparse()) + return self.element_class(self, stream) + + def functional_equation(self, left, right, series, initial_values): + r""" + Define the lazy undefined ``series`` that solves the functional + equation ``left == right`` with ``initial_values``. + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: F = diff(f, 2) + sage: L.functional_equation(-F, f, f, [1, 0]) + sage: f + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: cos(z) + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: F + -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) + """ + if self._arity != 1: + raise NotImplementedError("only implemented for one variable") + + if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: + raise ValueError("series already defined") + + left = self(left) + right = self(right) + cs = series._coeff_stream + ao = cs._approximate_order + R = self.base_ring() + initial_values = [R(val) for val in initial_values] + F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) + ret = Stream_functional_equation(ao, F, cs, initial_values) + series._coeff_stream = ret + + ###################################################################### From b9790074d73d1d89cf29df5fd7ff87713582b36b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 12 Oct 2023 18:11:31 +0900 Subject: [PATCH 003/507] Adding more examples, fixing bugs, streamlining the implementation. --- src/sage/data_structures/stream.py | 64 +++++++++++++++--------------- src/sage/rings/lazy_series_ring.py | 57 +++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 16224579f4f..e5de010fbad 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1289,12 +1289,13 @@ class Stream_functional_equation(Stream_inexact): - ``F`` -- the stream for the equation using ``uninitialized`` - ``uninitialized`` -- the uninitialized stream - ``initial_values`` -- the initial values + - ``R`` -- the base ring Instances of this class are always dense. EXAMPLES:: """ - def __init__(self, approximate_order, F, uninitialized, initial_values, true_order=False): + def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_order=False): """ Initialize ``self``. @@ -1313,11 +1314,14 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, true_ord if val: approximate_order += i true_order = True + initial_values = initial_values[i:] break else: approximate_order += len(initial_values) + initial_values = [] super().__init__(False, true_order) self._F = F + self._base = R self._initial_values = initial_values self._approximate_order = approximate_order self._uninitialized = uninitialized @@ -1343,59 +1347,55 @@ def iterate_coefficients(self): yield from self._initial_values from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - P = InfinitePolynomialRing(ZZ, 'x') + P = InfinitePolynomialRing(self._base, 'x') x = P.gen() PFF = P.fraction_field() + offset = self._approximate_order def get_coeff(n): - # Check against the initial values first - i = n - self._start - if n < len(self._initial_values): - return P(self._initial_values[n]) - - # We are always a dense implementation - # Check to see if we have already computed the value - if n < self._approximate_order: - return P.zero() - if self._true_order: - i = n - self._approximate_order - if i < len(self._cache): - return P(self._cache[i]) - - return x[i] - - sf = Stream_function(get_coeff, is_sparse=False, approximate_order=self._start, true_order=True) + return x[n-offset] + + sf = Stream_function(get_coeff, is_sparse=False, approximate_order=offset, true_order=True) self._F = self._F.replace(self._uninitialized, sf) - n = self._start - offset = len(self._initial_values) - self._start + n = self._F._approximate_order + data = list(self._initial_values) while True: coeff = self._F[n] if coeff.parent() is PFF: coeff = coeff.numerator() - + else: + coeff = P(coeff) V = coeff.variables() - if not V: - if coeff: - raise ValueError(f"no solution from degree {n} as {coeff} == 0") - yield ZZ.zero() - n += 1 - continue if len(V) > 1: # Substitute for known variables - coeff = coeff.subs({x[i]: val for i, val in enumerate(sf._cache)}) + coeff = coeff.subs({str(x[i]): val for i, val in enumerate(data)}) V = coeff.variables() if len(V) > 1: - raise ValueError(f"unable to determine a unique solution from degree {n}") + raise ValueError(f"unable to determine a unique solution in degree {n}") + + if not V: + if coeff: + raise ValueError(f"no solution in degree {n} as {coeff} == 0") + i = n - self._start + #print(i, len(data)) + if i < len(data): + # We already know this coefficient + yield data[n - self._start] + else: + yield self._base.zero() + data.append(self._base.zero()) + n += 1 + continue # single variable to solve for hc = coeff.homogeneous_components() if not set(hc).issubset([0,1]): - raise ValueError(f"unable to determine a unique solution from degree {n}") + raise ValueError(f"unable to determine a unique solution in degree {n}") val = -hc.get(0, P.zero()).lc() / hc[1].lc() # Update the cache - sf._cache[n + offset] = val + data.append(val) yield val n += 1 diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 4a3e3afb1cb..36f9f564b05 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1818,6 +1818,19 @@ def functional_equation(self, left, right, series, initial_values): r""" Define the lazy undefined ``series`` that solves the functional equation ``left == right`` with ``initial_values``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = L.undefined(-1) + sage: L.functional_equation(f, 2+z*f(z^2), f, [5]) + sage: f + 5*z^-1 + 5 + 2*z + 2*z^2 + 2*z^4 + O(z^6) + + sage: f = L.undefined(-2) + sage: L.functional_equation(f, 2+z*f(z^2), f, [5]) + sage: f + """ if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: raise ValueError("series already defined") @@ -1829,7 +1842,7 @@ def functional_equation(self, left, right, series, initial_values): R = self.base_ring() initial_values = [R(val) for val in initial_values] F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) - ret = Stream_functional_equation(ao, F, cs, initial_values) + ret = Stream_functional_equation(ao, F, cs, initial_values, R) series._coeff_stream = ret # === special functions === @@ -2582,6 +2595,46 @@ def functional_equation(self, left, right, series, initial_values): 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) sage: F -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: L.functional_equation(2*z*f(z^3) + z*f^3 - 3*f + 3, 0, f, []) + sage: f + 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(SR) + sage: G = L.undefined(0) + sage: L.functional_equation(diff(G) - exp(-G(-z)), 0, G, [ln(2)]) + sage: G + log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(RR) + sage: G = L.undefined(0) + sage: L.functional_equation(diff(G) - exp(-G(-z)), 0, G, [log(2)]) + sage: G + 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) + + We solve the recurrence relation in (3.12) of Prellberg and Brak + :doi:`10.1007/BF02183685`:: + + sage: q,y = QQ['q,y'].fraction_field().gens() + sage: L. = LazyPowerSeriesRing(q.parent()) + sage: R = L.undefined() + sage: L.functional_equation((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, 0, R, [0]) + sage: R[0] + 0 + sage: R[1] + q*y/(-q*y + 1) + sage: R[2] + (-q^3*y^2 - q^2*y)/(-q^3*y^2 + q^2*y + q*y - 1) + sage: R[3].factor() + (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 + * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) + + sage: Rp = L.undefined(1) + sage: L.functional_equation((1-q*x)*Rp, (y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q, Rp, []) + sage: all(R[n] == Rp[n] for n in range(10)) + True """ if self._arity != 1: raise NotImplementedError("only implemented for one variable") @@ -2596,7 +2649,7 @@ def functional_equation(self, left, right, series, initial_values): R = self.base_ring() initial_values = [R(val) for val in initial_values] F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) - ret = Stream_functional_equation(ao, F, cs, initial_values) + ret = Stream_functional_equation(ao, F, cs, initial_values, R) series._coeff_stream = ret From 9de8c8f7cc5bf9476f19ba9f0c4ee63935be2a9c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 13 Oct 2023 18:08:45 +0900 Subject: [PATCH 004/507] Fixing more bugs, updating the inputs, other touchups. --- src/sage/data_structures/stream.py | 52 ++++++++++++----- src/sage/rings/lazy_series_ring.py | 90 ++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index e5de010fbad..88c765569b9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1277,6 +1277,28 @@ def is_uninitialized(self): self._initializing = False return result + def replace(self, stream, sub): + r""" + Return ``self`` except with ``stream`` replaced by ``sub``. + + .. WARNING:: + + This does not update the approximate order or the cache. + """ + if self._target is None: + return self + if self._target == stream: + ret = copy(self) + ret._target = sub + else: + temp = self._target.replace(stream, sub) + if temp == self._target: + ret = self + else: + ret = copy(self) + ret._target = temp + return ret + class Stream_functional_equation(Stream_inexact): r""" @@ -1347,7 +1369,7 @@ def iterate_coefficients(self): yield from self._initial_values from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - P = InfinitePolynomialRing(self._base, 'x') + P = InfinitePolynomialRing(self._base, names=('FESDUMMY',)) x = P.gen() PFF = P.fraction_field() offset = self._approximate_order @@ -1368,24 +1390,22 @@ def get_coeff(n): coeff = P(coeff) V = coeff.variables() + # Substitute for known variables + if V: + # The infinite polynomial ring is very brittle with substitutions + # and variable comparisons + sub = {str(x[i]): val for i, val in enumerate(data) + if any(str(x[i]) == str(va) for va in V)} + if sub: + coeff = coeff.subs(sub) + V = P(coeff).variables() + if len(V) > 1: - # Substitute for known variables - coeff = coeff.subs({str(x[i]): val for i, val in enumerate(data)}) - V = coeff.variables() - if len(V) > 1: - raise ValueError(f"unable to determine a unique solution in degree {n}") + raise ValueError(f"unable to determine a unique solution in degree {n}") if not V: if coeff: - raise ValueError(f"no solution in degree {n} as {coeff} == 0") - i = n - self._start - #print(i, len(data)) - if i < len(data): - # We already know this coefficient - yield data[n - self._start] - else: - yield self._base.zero() - data.append(self._base.zero()) + raise ValueError(f"no solution in degree {n} as {coeff} != 0") n += 1 continue @@ -1393,6 +1413,8 @@ def get_coeff(n): hc = coeff.homogeneous_components() if not set(hc).issubset([0,1]): raise ValueError(f"unable to determine a unique solution in degree {n}") + if str(hc[1].lm()) != str(x[len(data)]): + raise ValueError(f"the solutions to the coefficients must be computed in order") val = -hc.get(0, P.zero()).lc() / hc[1].lc() # Update the cache data.append(val) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 36f9f564b05..e0405229961 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1814,35 +1814,38 @@ def taylor(self, f): stream = Stream_taylor(f, self.is_sparse()) return self.element_class(self, stream) - def functional_equation(self, left, right, series, initial_values): + def functional_equation(self, eqn, series, initial_values=None): r""" Define the lazy undefined ``series`` that solves the functional - equation ``left == right`` with ``initial_values``. + equation ``eqn == 0`` with ``initial_values``. EXAMPLES:: sage: L. = LazyLaurentSeriesRing(QQ) sage: f = L.undefined(-1) - sage: L.functional_equation(f, 2+z*f(z^2), f, [5]) + sage: L.functional_equation(2+z*f(z^2) - f, f, [5]) sage: f - 5*z^-1 + 5 + 2*z + 2*z^2 + 2*z^4 + O(z^6) + 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) + sage: 2 + z*f(z^2) - f + O(z^6) sage: f = L.undefined(-2) - sage: L.functional_equation(f, 2+z*f(z^2), f, [5]) + sage: L.functional_equation(2+z*f(z^2) - f, f, [5]) sage: f - + """ if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: raise ValueError("series already defined") - left = self(left) - right = self(right) + if initial_values is None: + initial_values = [] + + eqn = self(eqn) cs = series._coeff_stream ao = cs._approximate_order R = self.base_ring() initial_values = [R(val) for val in initial_values] - F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) - ret = Stream_functional_equation(ao, F, cs, initial_values, R) + ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) series._coeff_stream = ret # === special functions === @@ -2578,17 +2581,17 @@ def taylor(self, f): stream = Stream_taylor(f, self.is_sparse()) return self.element_class(self, stream) - def functional_equation(self, left, right, series, initial_values): + def functional_equation(self, eqn, series, initial_values=None): r""" Define the lazy undefined ``series`` that solves the functional - equation ``left == right`` with ``initial_values``. + equation ``eqn == 0`` with ``initial_values``. EXAMPLES:: sage: L. = LazyPowerSeriesRing(QQ) sage: f = L.undefined(0) sage: F = diff(f, 2) - sage: L.functional_equation(-F, f, f, [1, 0]) + sage: L.functional_equation(F + f, f, [1, 0]) sage: f 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) sage: cos(z) @@ -2598,19 +2601,38 @@ def functional_equation(self, left, right, series, initial_values): sage: L. = LazyPowerSeriesRing(QQ) sage: f = L.undefined(0) - sage: L.functional_equation(2*z*f(z^3) + z*f^3 - 3*f + 3, 0, f, []) + sage: L.functional_equation(2*z*f(z^3) + z*f^3 - 3*f + 3, f) sage: f 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) + From Exercise 6.63b in [ECII]_:: + + sage: g = L.undefined() + sage: z1 = z*diff(g, z) + sage: z2 = z1 + z^2 * diff(g, z, 2) + sage: z3 = z1 + 3 * z^2 * diff(g, z, 2) + z^3 * diff(g, z, 3) + sage: e1 = g^2 * z3 - 15*g*z1*z2 + 30*z1^3 + sage: e2 = g * z2 - 3 * z1^2 + sage: e3 = g * z2 - 3 * z1^2 + sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 + sage: L.functional_equation(e, g, [1, 2]) + + sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol + 1 + 2*z + 2*z^4 + O(z^7) + sage: all(g[i] == sol[i] for i in range(20)) + True + + Some more examples over different rings:: + sage: L. = LazyPowerSeriesRing(SR) sage: G = L.undefined(0) - sage: L.functional_equation(diff(G) - exp(-G(-z)), 0, G, [ln(2)]) + sage: L.functional_equation(diff(G) - exp(-G(-z)), G, [ln(2)]) sage: G log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) sage: L. = LazyPowerSeriesRing(RR) sage: G = L.undefined(0) - sage: L.functional_equation(diff(G) - exp(-G(-z)), 0, G, [log(2)]) + sage: L.functional_equation(diff(G) - exp(-G(-z)), G, [log(2)]) sage: G 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) @@ -2620,7 +2642,7 @@ def functional_equation(self, left, right, series, initial_values): sage: q,y = QQ['q,y'].fraction_field().gens() sage: L. = LazyPowerSeriesRing(q.parent()) sage: R = L.undefined() - sage: L.functional_equation((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, 0, R, [0]) + sage: L.functional_equation((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, R, [0]) sage: R[0] 0 sage: R[1] @@ -2632,9 +2654,32 @@ def functional_equation(self, left, right, series, initial_values): * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) sage: Rp = L.undefined(1) - sage: L.functional_equation((1-q*x)*Rp, (y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q, Rp, []) + sage: L.functional_equation((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp, Rp, []) sage: all(R[n] == Rp[n] for n in range(10)) True + + Another example:: + + sage: L. = LazyPowerSeriesRing(QQ["x,y,f1,f2"].fraction_field()) + sage: L.base_ring().inject_variables() + Defining x, y, f1, f2 + sage: F = L.undefined() + sage: L.functional_equation(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), F, [0, f1, f2]) + sage: F + f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) + + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) + + ... + O(z^8) + sage: sol = 1/(x-y)*((2*f2-y*f1)*(exp(x*z)-1)/x - (2*f2-x*f1)*(exp(y*z)-1)/y) + sage: F - sol + O(z^7) + + We need to specify the initial value for the degree 1 component to + get a unique solution in the previous example:: + + sage: F = L.undefined() + sage: L.functional_equation(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), F, []) + sage: F + """ if self._arity != 1: raise NotImplementedError("only implemented for one variable") @@ -2642,14 +2687,15 @@ def functional_equation(self, left, right, series, initial_values): if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: raise ValueError("series already defined") - left = self(left) - right = self(right) + if initial_values is None: + initial_values = [] + + eqn = self(eqn) cs = series._coeff_stream ao = cs._approximate_order R = self.base_ring() initial_values = [R(val) for val in initial_values] - F = Stream_sub(left._coeff_stream, right._coeff_stream, self.is_sparse()) - ret = Stream_functional_equation(ao, F, cs, initial_values, R) + ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) series._coeff_stream = ret From 071a10a3cc01028a80083bc0c4a23edb989f60ab Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 17 Oct 2023 13:12:10 +0900 Subject: [PATCH 005/507] Improving the functional defintion; changing the method name/location; moving towards full coverage. --- src/sage/data_structures/stream.py | 56 +++++++- src/sage/rings/lazy_series.py | 136 +++++++++++++++++++ src/sage/rings/lazy_series_ring.py | 205 +++++++---------------------- 3 files changed, 234 insertions(+), 163 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 88c765569b9..ea2c2825497 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -219,6 +219,13 @@ def replace(self, stream, sub): Return ``self`` except with ``stream`` replaced by ``sub``. The default is to return ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_zero + sage: zero = Stream_zero() + sage: zero.replace(zero, zero) is zero + True """ return self @@ -1087,6 +1094,15 @@ def __init__(self, function, is_sparse): sage: f = Stream_taylor(polygen(QQ, 'x')^3, False) sage: TestSuite(f).run(skip="_test_pickling") """ + from sage.symbolic.ring import SR + from sage.structure.element import parent + if parent(function) is SR: + self._is_symbolic = True + if function.number_of_arguments() != 1: + raise NotImplementedError("the function can only take a single input") + self._arg = function.arguments()[0] + else: + self._is_symbolic = False self._func = function super().__init__(is_sparse, False) self._approximate_order = 0 @@ -1135,7 +1151,7 @@ def __eq__(self, other): sage: f == g True """ - # The bool call is needed when passing functions in SR + # The bool call is needed when the functions are in SR return isinstance(other, type(self)) and bool(self._func == other._func) def get_coefficient(self, n): @@ -1153,11 +1169,24 @@ def get_coefficient(self, n): sage: f = Stream_taylor(g, True) sage: f.get_coefficient(5) 1/120 + + sage: from sage.data_structures.stream import Stream_taylor + sage: y = SR.var('y') + sage: f = Stream_taylor(sin(y), True) + sage: f.get_coefficient(5) + 1/120 """ if n == 0: + if self._is_symbolic: + return self._func.subs({self._arg: ZZ.zero()}) return self._func(ZZ.zero()) + from sage.functions.other import factorial - return self._func.derivative(n)(ZZ.zero()) / factorial(n) + if self._is_symbolic: + num = self._func.derivative(n).subs({self._arg: ZZ.zero()}) + else: + num = self._func.derivative(n)(ZZ.zero()) + return num / factorial(n) def iterate_coefficients(self): """ @@ -1171,12 +1200,21 @@ def iterate_coefficients(self): sage: it = f.iterate_coefficients() sage: [next(it) for _ in range(10)] [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] + + sage: y = SR.var('y') + sage: f = Stream_taylor(y^3, False) + sage: it = f.iterate_coefficients() + sage: [next(it) for _ in range(10)] + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] """ cur = self._func n = 0 denom = 1 while True: - yield cur(ZZ.zero()) / denom + if self._is_symbolic: + yield cur({self._arg: ZZ.zero()}) / denom + else: + yield cur(ZZ.zero()) / denom cur = cur.derivative() n += 1 denom *= n @@ -1375,7 +1413,10 @@ def iterate_coefficients(self): offset = self._approximate_order def get_coeff(n): - return x[n-offset] + n -= offset + if n < len(self._initial_values): + return self._initial_values[n] + return x[n] sf = Stream_function(get_coeff, is_sparse=False, approximate_order=offset, true_order=True) self._F = self._F.replace(self._uninitialized, sf) @@ -1417,6 +1458,7 @@ def get_coeff(n): raise ValueError(f"the solutions to the coefficients must be computed in order") val = -hc.get(0, P.zero()).lc() / hc[1].lc() # Update the cache + sf._cache[len(data)] = val data.append(val) yield val n += 1 @@ -3835,7 +3877,7 @@ def _approximate_order(self): return min(self._series._approximate_order + self._shift, 0) def get_coefficient(self, n): - """ + r""" Return the ``n``-th coefficient of ``self``. EXAMPLES:: @@ -3845,14 +3887,14 @@ def get_coefficient(self, n): sage: [f[i] for i in range(-3, 4)] [-2, -1, 0, 1, 2, 3, 4] sage: f2 = Stream_integral(f, [0], True) - sage: [f2[i] for i in range(-3, 5)] + sage: [f2.get_coefficient(i) for i in range(-3, 5)] [0, 1, 1, 0, 1, 1, 1, 1] sage: f = Stream_function(lambda n: (n + 1)*(n+2), True, 2) sage: [f[i] for i in range(-1, 4)] [0, 0, 0, 12, 20] sage: f2 = Stream_integral(f, [-1, -1, -1], True) - sage: [f2[i] for i in range(-1, 7)] + sage: [f2.get_coefficient(i) for i in range(-1, 7)] [0, -1, -1, -1/2, 0, 0, 1/5, 1/6] """ if 0 <= n < self._shift: diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 7124419c847..2024d295bd5 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -253,6 +253,7 @@ Stream_zero, Stream_exact, Stream_uninitialized, + Stream_functional_equation, Stream_shift, Stream_truncated, Stream_function, @@ -1549,6 +1550,141 @@ def define(self, s): # an alias for compatibility with padics set = define + def define_implicity(self, eqn, initial_values=None): + r""" + Define ``self`` as the series that solves the functional + equation ``eqn == 0`` with ``initial_values``. + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: F = diff(f, 2) + sage: f.define_implicity(F + f, [1, 0]) + sage: f + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: cos(z) + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: F + -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: f.define_implicity(2*z*f(z^3) + z*f^3 - 3*f + 3) + sage: f + 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) + + From Exercise 6.63b in [EnumComb2]_:: + + sage: g = L.undefined() + sage: z1 = z*diff(g, z) + sage: z2 = z1 + z^2 * diff(g, z, 2) + sage: z3 = z1 + 3 * z^2 * diff(g, z, 2) + z^3 * diff(g, z, 3) + sage: e1 = g^2 * z3 - 15*g*z1*z2 + 30*z1^3 + sage: e2 = g * z2 - 3 * z1^2 + sage: e3 = g * z2 - 3 * z1^2 + sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 + sage: g.define_implicity(e, [1, 2]) + + sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol + 1 + 2*z + 2*z^4 + O(z^7) + sage: all(g[i] == sol[i] for i in range(20)) + True + + Some more examples over different rings:: + + sage: L. = LazyPowerSeriesRing(SR) + sage: G = L.undefined(0) + sage: G.define_implicity(diff(G) - exp(-G(-z)), [ln(2)]) + sage: G + log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(RR) + sage: G = L.undefined(0) + sage: G.define_implicity(diff(G) - exp(-G(-z)), [log(2)]) + sage: G + 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) + + We solve the recurrence relation in (3.12) of Prellberg and Brak + :doi:`10.1007/BF02183685`:: + + sage: q,y = QQ['q,y'].fraction_field().gens() + sage: L. = LazyPowerSeriesRing(q.parent()) + sage: R = L.undefined() + sage: R.define_implicity((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, [0]) + sage: R[0] + 0 + sage: R[1] + q*y/(-q*y + 1) + sage: R[2] + (-q^3*y^2 - q^2*y)/(-q^3*y^2 + q^2*y + q*y - 1) + sage: R[3].factor() + (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 + * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) + + sage: Rp = L.undefined(1) + sage: Rp.define_implicity((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp) + sage: all(R[n] == Rp[n] for n in range(10)) + True + + Another example:: + + sage: L. = LazyPowerSeriesRing(QQ["x,y,f1,f2"].fraction_field()) + sage: L.base_ring().inject_variables() + Defining x, y, f1, f2 + sage: F = L.undefined() + sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1, f2]) + sage: F + f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) + + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) + + ... + O(z^8) + sage: sol = 1/(x-y)*((2*f2-y*f1)*(exp(x*z)-1)/x - (2*f2-x*f1)*(exp(y*z)-1)/y) + sage: F - sol + O(z^7) + + We need to specify the initial values for the degree 1 and 2 + components to get a unique solution in the previous example:: + + sage: F = L.undefined() + sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) + sage: F + + + sage: F = L.undefined() + sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) + sage: F + + + Laurent series examples:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = L.undefined(-1) + sage: f.define_implicity(2+z*f(z^2) - f, [5]) + sage: f + 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) + sage: 2 + z*f(z^2) - f + O(z^6) + + sage: g = L.undefined(-2) + sage: g.define_implicity(2+z*g(z^2) - g, [5]) + sage: g + + """ + if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: + raise ValueError("series already defined") + + if initial_values is None: + initial_values = [] + + P = self.parent() + eqn = P(eqn) + cs = self._coeff_stream + ao = cs._approximate_order + R = P.base_ring() + initial_values = [R(val) for val in initial_values] + ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) + self._coeff_stream = ret + def _repr_(self): r""" Return a string representation of ``self``. diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index e0405229961..c3059bf936a 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -88,9 +88,7 @@ Stream_iterator, Stream_exact, Stream_uninitialized, - Stream_sub, - Stream_taylor, - Stream_functional_equation + Stream_taylor ) from types import GeneratorType @@ -1797,7 +1795,7 @@ def residue_field(self): def taylor(self, f): r""" - Return the Taylor expansion of the function ``f``. + Return the Taylor expansion around `0` of the function ``f``. INPUT: @@ -1806,6 +1804,24 @@ def taylor(self, f): * the substitution `f(z)`, where `z` is generator of ``self`` * `f` is a function of a single variable with no poles and has a ``derivative`` method + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: x = SR.var('x') + sage: f(x) = (1 + x)/(1 - x^2) + sage: L.taylor(f) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + O(z^7) + + For inputs as symbolic functions, this uses the generic implementation, + and so the function cannot have any poles at `0`:: + + sage: f(x) = (1 + x^2) / sin(x^2) + sage: L.taylor(f) + + sage: def g(a): return (1 + a^2) / sin(a^2) + sage: L.taylor(g) + z^-2 + 1 + 1/6*z^2 + 1/6*z^4 + O(z^5) """ try: return f(self.gen()) @@ -1814,40 +1830,6 @@ def taylor(self, f): stream = Stream_taylor(f, self.is_sparse()) return self.element_class(self, stream) - def functional_equation(self, eqn, series, initial_values=None): - r""" - Define the lazy undefined ``series`` that solves the functional - equation ``eqn == 0`` with ``initial_values``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = L.undefined(-1) - sage: L.functional_equation(2+z*f(z^2) - f, f, [5]) - sage: f - 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) - sage: 2 + z*f(z^2) - f - O(z^6) - - sage: f = L.undefined(-2) - sage: L.functional_equation(2+z*f(z^2) - f, f, [5]) - sage: f - - """ - if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: - raise ValueError("series already defined") - - if initial_values is None: - initial_values = [] - - eqn = self(eqn) - cs = series._coeff_stream - ao = cs._approximate_order - R = self.base_ring() - initial_values = [R(val) for val in initial_values] - ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) - series._coeff_stream = ret - # === special functions === def q_pochhammer(self, q=None): @@ -2561,7 +2543,7 @@ def some_elements(self): def taylor(self, f): r""" - Return the Taylor expansion of the function ``f``. + Return the Taylor expansion around `0` of the function ``f``. INPUT: @@ -2571,6 +2553,34 @@ def taylor(self, f): are the generators of ``self`` * `f` is a function of a single variable with no poles and has a ``derivative`` method + + .. WARNING:: + + For inputs as symbolic functions/expressions + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: x = SR.var('x') + sage: f(x) = (1 + x) / (1 - x^3) + sage: L.taylor(f) + 1 + z + z^3 + z^4 + z^6 + O(z^7) + sage: (1 + z) / (1 - z^3) + 1 + z + z^3 + z^4 + z^6 + O(z^7) + sage: f(x) = cos(x + pi/2) + sage: L.taylor(f) + -z + 1/6*z^3 - 1/120*z^5 + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: def f(x, y): return (1 + x) / (1 + y) + sage: L.taylor(f) + 1 + (a-b) + (-a*b+b^2) + (a*b^2-b^3) + (-a*b^3+b^4) + (a*b^4-b^5) + (-a*b^5+b^6) + O(a,b)^7 + sage: y = SR.var('y') + sage: g(x, y) = (1 + x) / (1 + y) + sage: L.taylor(g) + Traceback (most recent call last): + ... + NotImplementedError: only implemented generically for one variable """ try: return f(*self.gens()) @@ -2581,123 +2591,6 @@ def taylor(self, f): stream = Stream_taylor(f, self.is_sparse()) return self.element_class(self, stream) - def functional_equation(self, eqn, series, initial_values=None): - r""" - Define the lazy undefined ``series`` that solves the functional - equation ``eqn == 0`` with ``initial_values``. - - EXAMPLES:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(0) - sage: F = diff(f, 2) - sage: L.functional_equation(F + f, f, [1, 0]) - sage: f - 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) - sage: cos(z) - 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) - sage: F - -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) - - sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(0) - sage: L.functional_equation(2*z*f(z^3) + z*f^3 - 3*f + 3, f) - sage: f - 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) - - From Exercise 6.63b in [ECII]_:: - - sage: g = L.undefined() - sage: z1 = z*diff(g, z) - sage: z2 = z1 + z^2 * diff(g, z, 2) - sage: z3 = z1 + 3 * z^2 * diff(g, z, 2) + z^3 * diff(g, z, 3) - sage: e1 = g^2 * z3 - 15*g*z1*z2 + 30*z1^3 - sage: e2 = g * z2 - 3 * z1^2 - sage: e3 = g * z2 - 3 * z1^2 - sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 - sage: L.functional_equation(e, g, [1, 2]) - - sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol - 1 + 2*z + 2*z^4 + O(z^7) - sage: all(g[i] == sol[i] for i in range(20)) - True - - Some more examples over different rings:: - - sage: L. = LazyPowerSeriesRing(SR) - sage: G = L.undefined(0) - sage: L.functional_equation(diff(G) - exp(-G(-z)), G, [ln(2)]) - sage: G - log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) - - sage: L. = LazyPowerSeriesRing(RR) - sage: G = L.undefined(0) - sage: L.functional_equation(diff(G) - exp(-G(-z)), G, [log(2)]) - sage: G - 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) - - We solve the recurrence relation in (3.12) of Prellberg and Brak - :doi:`10.1007/BF02183685`:: - - sage: q,y = QQ['q,y'].fraction_field().gens() - sage: L. = LazyPowerSeriesRing(q.parent()) - sage: R = L.undefined() - sage: L.functional_equation((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, R, [0]) - sage: R[0] - 0 - sage: R[1] - q*y/(-q*y + 1) - sage: R[2] - (-q^3*y^2 - q^2*y)/(-q^3*y^2 + q^2*y + q*y - 1) - sage: R[3].factor() - (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 - * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) - - sage: Rp = L.undefined(1) - sage: L.functional_equation((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp, Rp, []) - sage: all(R[n] == Rp[n] for n in range(10)) - True - - Another example:: - - sage: L. = LazyPowerSeriesRing(QQ["x,y,f1,f2"].fraction_field()) - sage: L.base_ring().inject_variables() - Defining x, y, f1, f2 - sage: F = L.undefined() - sage: L.functional_equation(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), F, [0, f1, f2]) - sage: F - f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) - + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) - + ... + O(z^8) - sage: sol = 1/(x-y)*((2*f2-y*f1)*(exp(x*z)-1)/x - (2*f2-x*f1)*(exp(y*z)-1)/y) - sage: F - sol - O(z^7) - - We need to specify the initial value for the degree 1 component to - get a unique solution in the previous example:: - - sage: F = L.undefined() - sage: L.functional_equation(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), F, []) - sage: F - - """ - if self._arity != 1: - raise NotImplementedError("only implemented for one variable") - - if not isinstance(series._coeff_stream, Stream_uninitialized) or series._coeff_stream._target is not None: - raise ValueError("series already defined") - - if initial_values is None: - initial_values = [] - - eqn = self(eqn) - cs = series._coeff_stream - ao = cs._approximate_order - R = self.base_ring() - initial_values = [R(val) for val in initial_values] - ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) - series._coeff_stream = ret - ###################################################################### From 743cad65f0690b32a0b48f5fde3f88e8d0c87565 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 17 Oct 2023 15:45:28 +0900 Subject: [PATCH 006/507] Full doctest coverage for the PR. --- src/sage/data_structures/stream.py | 114 +++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index ea2c2825497..d8ead2bd6b1 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1322,6 +1322,29 @@ def replace(self, stream, sub): .. WARNING:: This does not update the approximate order or the cache. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_shift, Stream_function + sage: U = Stream_uninitialized(0) + sage: F = Stream_function(lambda n: 1, False, 0) + sage: X = Stream_function(lambda n: n, False, 0) + sage: S = Stream_shift(F, -3) + sage: U.replace(X, F) is U + True + sage: U._target = S + sage: Up = U.replace(S, X) + sage: Up == U + False + sage: [Up[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: Upp = U.replace(F, X) + sage: Upp == U + False + sage: [Upp[i] for i in range(5)] + [3, 4, 5, 6, 7] + sage: [U[i] for i in range(5)] + [1, 1, 1, 1, 1] """ if self._target is None: return self @@ -1562,6 +1585,28 @@ def replace(self, stream, sub): .. WARNING:: This does not update the approximate order or the cache. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_shift, Stream_neg, Stream_function + sage: F = Stream_function(lambda n: 1, False, 0) + sage: X = Stream_function(lambda n: n, False, 0) + sage: S = Stream_shift(F, -3) + sage: N = Stream_neg(S, False) + sage: N.replace(X, F) is N + True + sage: Np = N.replace(F, X) + sage: Np == N + False + sage: [Np[i] for i in range(5)] + [-3, -4, -5, -6, -7] + sage: Npp = N.replace(S, X) + sage: Npp == N + False + sage: [Npp[i] for i in range(5)] + [0, -1, -2, -3, -4] + sage: [N[i] for i in range(5)] + [-1, -1, -1, -1, -1] """ if self._series == stream: ret = copy(self) @@ -1687,6 +1732,53 @@ def replace(self, stream, sub): .. WARNING:: This does not update the approximate order or the cache. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_neg, Stream_sub, Stream_function + sage: L = Stream_function(lambda n: 1, False, 0) + sage: R = Stream_function(lambda n: n, False, 0) + sage: NL = Stream_neg(L, False) + sage: NR = Stream_neg(R, False) + sage: S = Stream_sub(NL, NR, False) + sage: S.replace(Stream_function(lambda n: n^2, False, 0), R) is S + True + sage: Sp = S.replace(L, R) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [0, 0, 0, 0, 0] + + Because we have computed some values of the cache for ``NR`` (which + is copied), we get the following wrong result:: + + sage: Sp = S.replace(R, L) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [-1, 0, 0, 0, 0] + + With fresh caches:: + + sage: NL = Stream_neg(L, False) + sage: NR = Stream_neg(R, False) + sage: S = Stream_sub(NL, NR, False) + sage: Sp = S.replace(R, L) + sage: [Sp[i] for i in range(5)] + [0, 0, 0, 0, 0] + + The replacements here do not affect the relevant caches:: + + sage: Sp = S.replace(NL, L) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [1, 2, 3, 4, 5] + sage: Sp = S.replace(NR, R) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [-1, -2, -3, -4, -5] """ if self._left == stream: ret = copy(self) @@ -3441,6 +3533,28 @@ def replace(self, stream, sub): .. WARNING:: This does not update the approximate order. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_shift, Stream_function + sage: F = Stream_function(lambda n: 1, False, 0) + sage: X = Stream_function(lambda n: n, False, 0) + sage: S = Stream_shift(F, -3) + sage: S.replace(X, F) is S + True + sage: Sp = S.replace(F, X) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [3, 4, 5, 6, 7] + sage: U = Stream_uninitialized(0) + sage: U._target = F + sage: S = Stream_shift(U, -3) + sage: Sp = S.replace(F, X) + sage: Sp == S + False + sage: [Sp[i] for i in range(5)] + [3, 4, 5, 6, 7] """ if self._series == stream: ret = copy(self) From 971c44b754956ff1434004e5d431b8790a0599f7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 24 Oct 2023 15:56:02 +0900 Subject: [PATCH 007/507] Adding Martin's patch and updating a few doctests reflecting the speed increase. --- src/sage/data_structures/stream.py | 45 ++++++++++++++++++++++++++++-- src/sage/rings/lazy_series.py | 9 +++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index d8ead2bd6b1..7b75eb613fb 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -147,6 +147,9 @@ def __init__(self, true_order): """ self._true_order = true_order + def map_cache(self, function): + pass + @lazy_attribute def _approximate_order(self): """ @@ -454,6 +457,14 @@ def iterate_coefficients(self): yield self.get_coefficient(n) n += 1 + def map_cache(self, function): + if self._cache: + if self._is_sparse: + i = max(self._cache) + self._cache[i] = function(self._cache[i]) + else: + self._cache[-1] = function(self._cache[-1]) + def order(self): r""" Return the order of ``self``, which is the minimum index ``n`` such @@ -1360,6 +1371,11 @@ def replace(self, stream, sub): ret._target = temp return ret + def map_cache(self, function): + super().map_cache(function) + if self._target is not None: + self._target.map_cache(function) + class Stream_functional_equation(Stream_inexact): r""" @@ -1430,7 +1446,7 @@ def iterate_coefficients(self): yield from self._initial_values from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - P = InfinitePolynomialRing(self._base, names=('FESDUMMY',)) + P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), implementation='sparse') x = P.gen() PFF = P.fraction_field() offset = self._approximate_order @@ -1481,7 +1497,16 @@ def get_coeff(n): raise ValueError(f"the solutions to the coefficients must be computed in order") val = -hc.get(0, P.zero()).lc() / hc[1].lc() # Update the cache - sf._cache[len(data)] = val + def sub(c): + if c not in self._base: + # print(c.polynomial().parent()) + return self._base(c.subs({V[0]: val})) + return c + # print("sf._cache", sf._cache) + sf.map_cache(sub) + # print("F._cache", self._F._cache) + self._F.map_cache(sub) + # sf._cache[len(data)] = val data.append(val) yield val n += 1 @@ -1526,6 +1551,10 @@ def __init__(self, series, is_sparse, true_order=False): self._series = series super().__init__(is_sparse, true_order) + def map_cache(self, function): + super().map_cache(function) + self._series.map_cache(function) + def __hash__(self): """ Return the hash of ``self``. @@ -1663,6 +1692,11 @@ def __init__(self, left, right, is_sparse): self._right = right super().__init__(is_sparse, False) + def map_cache(self, function): + super().map_cache(function) + self._left.map_cache(function) + self._right.map_cache(function) + def __hash__(self): """ Return the hash of ``self``. @@ -2526,6 +2560,10 @@ def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): f = Stream_map_coefficients(f, lambda x: p(x), is_sparse) super().__init__(f, g, is_sparse) + def map_cache(self, function): + super().map_cache(function) + self._powers = [g.map_cache(function) for g in self._powers] + @lazy_attribute def _approximate_order(self): """ @@ -3407,6 +3445,9 @@ def __init__(self, series, shift): self._shift = shift super().__init__(series._true_order) + def map_cache(self, function): + self._series.map_cache(function) + @lazy_attribute def _approximate_order(self): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 2024d295bd5..6f8920ed63d 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1588,7 +1588,7 @@ def define_implicity(self, eqn, initial_values=None): sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol 1 + 2*z + 2*z^4 + O(z^7) - sage: all(g[i] == sol[i] for i in range(20)) + sage: all(g[i] == sol[i] for i in range(50)) True Some more examples over different rings:: @@ -1603,12 +1603,13 @@ def define_implicity(self, eqn, initial_values=None): sage: G = L.undefined(0) sage: G.define_implicity(diff(G) - exp(-G(-z)), [log(2)]) sage: G - 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) + 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 + - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) We solve the recurrence relation in (3.12) of Prellberg and Brak :doi:`10.1007/BF02183685`:: - sage: q,y = QQ['q,y'].fraction_field().gens() + sage: q, y = QQ['q,y'].fraction_field().gens() sage: L. = LazyPowerSeriesRing(q.parent()) sage: R = L.undefined() sage: R.define_implicity((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, [0]) @@ -1624,7 +1625,7 @@ def define_implicity(self, eqn, initial_values=None): sage: Rp = L.undefined(1) sage: Rp.define_implicity((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp) - sage: all(R[n] == Rp[n] for n in range(10)) + sage: all(R[n] == Rp[n] for n in range(7)) True Another example:: From c62ee41657d3ef6492becc458357015de3c9a6f5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 24 Oct 2023 16:31:14 +0900 Subject: [PATCH 008/507] Fixing some remaining details and polishing. --- src/sage/data_structures/stream.py | 262 ++++++++++++++++++++++++----- 1 file changed, 217 insertions(+), 45 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 7b75eb613fb..362495177c9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -147,9 +147,6 @@ def __init__(self, true_order): """ self._true_order = true_order - def map_cache(self, function): - pass - @lazy_attribute def _approximate_order(self): """ @@ -232,6 +229,21 @@ def replace(self, stream, sub): """ return self + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + The default is to do nothing (as there is no cache). + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_zero + sage: z = Stream_zero() + sage: z.recursive_map_largest_cached(lambda x: x + 10) + """ + pass + class Stream_inexact(Stream): """ @@ -457,14 +469,6 @@ def iterate_coefficients(self): yield self.get_coefficient(n) n += 1 - def map_cache(self, function): - if self._cache: - if self._is_sparse: - i = max(self._cache) - self._cache[i] = function(self._cache[i]) - else: - self._cache[-1] = function(self._cache[-1]) - def order(self): r""" Return the order of ``self``, which is the minimum index ``n`` such @@ -602,6 +606,39 @@ def __ne__(self, other): return False + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function + sage: f = Stream_function(lambda n: n, False, 1) + sage: [f[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: f._cache + [1, 2, 3, 4] + sage: f.recursive_map_largest_cached(lambda x: x + 10) + sage: f._cache + [1, 2, 3, 14] + + sage: f = Stream_function(lambda n: n, True, 1) + sage: [f[i] for i in range(0,10,3)] + [0, 3, 6, 9] + sage: f._cache + {3: 3, 6: 6, 9: 9} + sage: f.recursive_map_largest_cached(lambda x: x + 10) + sage: f._cache + {3: 3, 6: 6, 9: 19} + """ + if self._cache: + if self._is_sparse: + i = max(self._cache) + self._cache[i] = function(self._cache[i]) + else: + self._cache[-1] = function(self._cache[-1]) + class Stream_exact(Stream): r""" @@ -1244,7 +1281,7 @@ class Stream_uninitialized(Stream_inexact): .. TODO:: - shouldn't instances of this class share the cache with its + Should instances of this class share the cache with its ``_target``? EXAMPLES:: @@ -1371,10 +1408,28 @@ def replace(self, stream, sub): ret._target = temp return ret - def map_cache(self, function): - super().map_cache(function) + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function + sage: h = Stream_function(lambda n: n, False, 1) + sage: M = Stream_uninitialized(0) + sage: M._target = h + sage: [h[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: h._cache + [1, 2, 3, 4] + sage: M.recursive_map_largest_cached(lambda x: x + 10) + sage: h._cache + [1, 2, 3, 14] + """ + super().recursive_map_largest_cached(function) if self._target is not None: - self._target.map_cache(function) + self._target.recursive_map_largest_cached(function) class Stream_functional_equation(Stream_inexact): @@ -1393,6 +1448,16 @@ class Stream_functional_equation(Stream_inexact): Instances of this class are always dense. EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized + sage: from sage.data_structures.stream import Stream_functional_equation + sage: from sage.data_structures.stream import Stream_derivative, Stream_sub + sage: C = Stream_uninitialized(0) + sage: D = Stream_derivative(C, 1, False) + sage: F = Stream_sub(D, C, False) + sage: S = Stream_functional_equation(0, F, C, [1], QQ) + sage: [S[i] for i in range(10)] + [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] """ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_order=False): """ @@ -1401,8 +1466,13 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ TESTS:: sage: from sage.data_structures.stream import Stream_uninitialized + sage: from sage.data_structures.stream import Stream_functional_equation + sage: from sage.data_structures.stream import Stream_derivative, Stream_sub sage: C = Stream_uninitialized(0) - sage: TestSuite(C).run(skip="_test_pickling") + sage: D = Stream_derivative(C, 1, False) + sage: F = Stream_sub(D, C, False) + sage: S = Stream_functional_equation(0, F, C, [1], QQ) + sage: TestSuite(S).run(skip="_test_pickling") """ if approximate_order is None: raise ValueError("the valuation must be specified for undefined series") @@ -1434,14 +1504,15 @@ def iterate_coefficients(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_exact - sage: z = Stream_exact([1], order=1) + sage: from sage.data_structures.stream import Stream_functional_equation + sage: from sage.data_structures.stream import Stream_derivative, Stream_sub sage: C = Stream_uninitialized(0) - sage: C._target - sage: C._target = z - sage: n = C.iterate_coefficients() + sage: D = Stream_derivative(C, 1, False) + sage: F = Stream_sub(D, C, False) + sage: S = Stream_functional_equation(0, F, C, [1], QQ) + sage: n = S.iterate_coefficients() sage: [next(n) for _ in range(10)] - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] + [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] """ yield from self._initial_values @@ -1496,17 +1567,13 @@ def get_coeff(n): if str(hc[1].lm()) != str(x[len(data)]): raise ValueError(f"the solutions to the coefficients must be computed in order") val = -hc.get(0, P.zero()).lc() / hc[1].lc() - # Update the cache + # Update the caches def sub(c): if c not in self._base: - # print(c.polynomial().parent()) return self._base(c.subs({V[0]: val})) return c - # print("sf._cache", sf._cache) - sf.map_cache(sub) - # print("F._cache", self._F._cache) - self._F.map_cache(sub) - # sf._cache[len(data)] = val + sf.recursive_map_largest_cached(sub) + self._F.recursive_map_largest_cached(sub) data.append(val) yield val n += 1 @@ -1551,10 +1618,6 @@ def __init__(self, series, is_sparse, true_order=False): self._series = series super().__init__(is_sparse, true_order) - def map_cache(self, function): - super().map_cache(function) - self._series.map_cache(function) - def __hash__(self): """ Return the hash of ``self``. @@ -1649,6 +1712,35 @@ def replace(self, stream, sub): ret._series = temp return ret + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + .. WARNING:: + + This might make the output inconsistent. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_neg + sage: h = Stream_function(lambda n: n, False, 1) + sage: M = Stream_neg(h, False) + sage: [M[i] for i in range(5)] + [0, -1, -2, -3, -4] + sage: M._cache + [-1, -2, -3, -4] + sage: h._cache + [1, 2, 3, 4] + sage: M.recursive_map_largest_cached(lambda x: x + 10) + sage: M._cache + [-1, -2, -3, 6] + sage: h._cache + [1, 2, 3, 14] + """ + super().recursive_map_largest_cached(function) + self._series.recursive_map_largest_cached(function) + class Stream_binary(Stream_inexact): """ @@ -1692,11 +1784,6 @@ def __init__(self, left, right, is_sparse): self._right = right super().__init__(is_sparse, False) - def map_cache(self, function): - super().map_cache(function) - self._left.map_cache(function) - self._right.map_cache(function) - def __hash__(self): """ Return the hash of ``self``. @@ -1835,6 +1922,37 @@ def replace(self, stream, sub): ret._right = temp return ret + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_add + sage: l = Stream_function(lambda n: n, False, 1) + sage: r = Stream_function(lambda n: n^2, False, 1) + sage: M = Stream_add(l, r, False) + sage: [M[i] for i in range(5)] + [0, 2, 6, 12, 20] + sage: M._cache + [2, 6, 12, 20] + sage: l._cache + [1, 2, 3, 4] + sage: r._cache + [1, 4, 9, 16] + sage: M.recursive_map_largest_cached(lambda x: x + 10) + sage: M._cache + [2, 6, 12, 30] + sage: l._cache + [1, 2, 3, 14] + sage: r._cache + [1, 4, 9, 26] + """ + super().recursive_map_largest_cached(function) + self._left.recursive_map_largest_cached(function) + self._right.recursive_map_largest_cached(function) + class Stream_binaryCommutative(Stream_binary): r""" @@ -2560,10 +2678,6 @@ def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): f = Stream_map_coefficients(f, lambda x: p(x), is_sparse) super().__init__(f, g, is_sparse) - def map_cache(self, function): - super().map_cache(function) - self._powers = [g.map_cache(function) for g in self._powers] - @lazy_attribute def _approximate_order(self): """ @@ -2763,6 +2877,46 @@ def stretched_power_restrict_degree(self, i, m, d): return self._basis.zero() + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_plethysm + sage: s = SymmetricFunctions(QQ).s() + sage: p = SymmetricFunctions(QQ).p() + sage: f = Stream_function(lambda n: s[n], True, 1) + sage: g = Stream_function(lambda n: s[n-1,1], True, 2) + sage: h = Stream_plethysm(f, g, True, p) + sage: [h[i] for i in range(1, 5)] + [0, 1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3], + 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] - 1/2*p[4]] + sage: h._cache + {2: 1/2*p[1, 1] - 1/2*p[2], + 3: 1/3*p[1, 1, 1] - 1/3*p[3], + 4: 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] - 1/2*p[4]} + sage: [hp._cache for hp in h._powers] + [{2: 1/2*p[1, 1] - 1/2*p[2], + 3: 1/3*p[1, 1, 1] - 1/3*p[3], + 4: 1/8*p[1, 1, 1, 1] + 1/4*p[2, 1, 1] - 1/8*p[2, 2] - 1/4*p[4]}, + {4: 1/4*p[1, 1, 1, 1] - 1/2*p[2, 1, 1] + 1/4*p[2, 2]}] + sage: h.recursive_map_largest_cached(lambda x: x/2) + sage: h._cache + {2: 1/2*p[1, 1] - 1/2*p[2], + 3: 1/3*p[1, 1, 1] - 1/3*p[3], + 4: 1/8*p[1, 1, 1, 1] + 1/8*p[2, 2] - 1/4*p[4]} + sage: [hp._cache for hp in h._powers] + [{2: 1/2*p[1, 1] - 1/2*p[2], + 3: 1/3*p[1, 1, 1] - 1/3*p[3], + 4: 1/128*p[1, 1, 1, 1] + 1/64*p[2, 1, 1] - 1/128*p[2, 2] - 1/64*p[4]}, + {4: 1/8*p[1, 1, 1, 1] - 1/4*p[2, 1, 1] + 1/8*p[2, 2]}] + """ + super().recursive_map_largest_cached(function) + for g in self._powers: + g.recursive_map_largest_cached(function) + ##################################################################### # Unary operations @@ -3445,9 +3599,6 @@ def __init__(self, series, shift): self._shift = shift super().__init__(series._true_order) - def map_cache(self, function): - self._series.map_cache(function) - @lazy_attribute def _approximate_order(self): """ @@ -3609,6 +3760,27 @@ def replace(self, stream, sub): ret._series = temp return ret + def recursive_map_largest_cached(self, function): + r""" + Update the largest indexed entry in the cache by applying ``function`` + and proceed recursively through any dependent streams. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_shift + sage: from sage.data_structures.stream import Stream_function + sage: h = Stream_function(lambda n: n, False, -5) + sage: M = Stream_shift(h, 2) + sage: [M[i] for i in range(-5, 5)] + [0, 0, -5, -4, -3, -2, -1, 0, 1, 2] + sage: h._cache + [-5, -4, -3, -2, -1, 0, 1, 2] + sage: M.recursive_map_largest_cached(lambda x: x + 10) + sage: h._cache + [-5, -4, -3, -2, -1, 0, 1, 12] + """ + self._series.recursive_map_largest_cached(function) + class Stream_truncated(Stream_unary): """ From a4ae02ccf0c1aa23066f00c12b54b4a328742356 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 29 Oct 2023 14:02:46 +0100 Subject: [PATCH 009/507] provide method to access parent streams, trust that only last element of cache may contain variable, add failing test --- src/sage/data_structures/stream.py | 230 +++++++++-------------------- src/sage/rings/lazy_series.py | 8 + 2 files changed, 75 insertions(+), 163 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 362495177c9..d4de5aff3b3 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -229,20 +229,19 @@ def replace(self, stream, sub): """ return self - def recursive_map_largest_cached(self, function): + def parent_streams(self): r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. - - The default is to do nothing (as there is no cache). + Return the list of streams which are used to compute the + coefficients of ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero sage: z = Stream_zero() - sage: z.recursive_map_largest_cached(lambda x: x + 10) + sage: z.parent_streams() + [] """ - pass + return [] class Stream_inexact(Stream): @@ -606,39 +605,6 @@ def __ne__(self, other): return False - def recursive_map_largest_cached(self, function): - r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n, False, 1) - sage: [f[i] for i in range(5)] - [0, 1, 2, 3, 4] - sage: f._cache - [1, 2, 3, 4] - sage: f.recursive_map_largest_cached(lambda x: x + 10) - sage: f._cache - [1, 2, 3, 14] - - sage: f = Stream_function(lambda n: n, True, 1) - sage: [f[i] for i in range(0,10,3)] - [0, 3, 6, 9] - sage: f._cache - {3: 3, 6: 6, 9: 9} - sage: f.recursive_map_largest_cached(lambda x: x + 10) - sage: f._cache - {3: 3, 6: 6, 9: 19} - """ - if self._cache: - if self._is_sparse: - i = max(self._cache) - self._cache[i] = function(self._cache[i]) - else: - self._cache[-1] = function(self._cache[-1]) - class Stream_exact(Stream): r""" @@ -1408,28 +1374,27 @@ def replace(self, stream, sub): ret._target = temp return ret - def recursive_map_largest_cached(self, function): + def parent_streams(self): r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. + Return the list of streams which are used to compute the + coefficients of ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function sage: h = Stream_function(lambda n: n, False, 1) sage: M = Stream_uninitialized(0) + sage: M.parent_streams() + [] sage: M._target = h sage: [h[i] for i in range(5)] [0, 1, 2, 3, 4] - sage: h._cache - [1, 2, 3, 4] - sage: M.recursive_map_largest_cached(lambda x: x + 10) - sage: h._cache - [1, 2, 3, 14] + sage: M.parent_streams() + [] """ - super().recursive_map_largest_cached(function) if self._target is not None: - self._target.recursive_map_largest_cached(function) + return [self._target] + return [] class Stream_functional_equation(Stream_inexact): @@ -1497,6 +1462,19 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ self._uninitialized._approximate_order = approximate_order self._uninitialized._target = self + def _subs_in_caches(self, s, var, val): + if hasattr(s, "_cache"): + if s._cache: + if s._is_sparse: + i = max(s._cache) + else: + i = -1 + c = s._cache[i] + if c not in self._base: + s._cache[i] = self._base(c.subs({var: val})) + for t in s.parent_streams(): + self._subs_in_caches(t, var, val) + def iterate_coefficients(self): """ A generator for the coefficients of ``self``. @@ -1532,7 +1510,7 @@ def get_coeff(n): self._F = self._F.replace(self._uninitialized, sf) n = self._F._approximate_order - data = list(self._initial_values) + m = len(self._initial_values) while True: coeff = self._F[n] if coeff.parent() is PFF: @@ -1541,16 +1519,6 @@ def get_coeff(n): coeff = P(coeff) V = coeff.variables() - # Substitute for known variables - if V: - # The infinite polynomial ring is very brittle with substitutions - # and variable comparisons - sub = {str(x[i]): val for i, val in enumerate(data) - if any(str(x[i]) == str(va) for va in V)} - if sub: - coeff = coeff.subs(sub) - V = P(coeff).variables() - if len(V) > 1: raise ValueError(f"unable to determine a unique solution in degree {n}") @@ -1562,21 +1530,20 @@ def get_coeff(n): # single variable to solve for hc = coeff.homogeneous_components() - if not set(hc).issubset([0,1]): - raise ValueError(f"unable to determine a unique solution in degree {n}") - if str(hc[1].lm()) != str(x[len(data)]): - raise ValueError(f"the solutions to the coefficients must be computed in order") - val = -hc.get(0, P.zero()).lc() / hc[1].lc() + if len(hc) == 1: + val = self._base.zero() + else: + if set(hc) != set([0, 1]): + raise ValueError(f"unable to determine a unique solution in degree {n}") + if str(hc[1].lm()) != str(x[m]): + raise ValueError(f"the solutions to the coefficients must be computed in order") + val = self._base(-hc[0].lc() / hc[1].lc()) # Update the caches - def sub(c): - if c not in self._base: - return self._base(c.subs({V[0]: val})) - return c - sf.recursive_map_largest_cached(sub) - self._F.recursive_map_largest_cached(sub) - data.append(val) + self._subs_in_caches(sf, V[0], val) + self._subs_in_caches(self._F, V[0], val) yield val n += 1 + m += 1 class Stream_unary(Stream_inexact): @@ -1712,34 +1679,20 @@ def replace(self, stream, sub): ret._series = temp return ret - def recursive_map_largest_cached(self, function): + def parent_streams(self): r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. - - .. WARNING:: - - This might make the output inconsistent. + Return the list of streams which are used to compute the + coefficients of ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_function, Stream_neg sage: h = Stream_function(lambda n: n, False, 1) sage: M = Stream_neg(h, False) - sage: [M[i] for i in range(5)] - [0, -1, -2, -3, -4] - sage: M._cache - [-1, -2, -3, -4] - sage: h._cache - [1, 2, 3, 4] - sage: M.recursive_map_largest_cached(lambda x: x + 10) - sage: M._cache - [-1, -2, -3, 6] - sage: h._cache - [1, 2, 3, 14] + sage: M.parent_streams() + [] """ - super().recursive_map_largest_cached(function) - self._series.recursive_map_largest_cached(function) + return [self._series] class Stream_binary(Stream_inexact): @@ -1922,10 +1875,10 @@ def replace(self, stream, sub): ret._right = temp return ret - def recursive_map_largest_cached(self, function): + def parent_streams(self): r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. + Return the list of streams which are used to compute the + coefficients of ``self``. EXAMPLES:: @@ -1933,25 +1886,11 @@ def recursive_map_largest_cached(self, function): sage: l = Stream_function(lambda n: n, False, 1) sage: r = Stream_function(lambda n: n^2, False, 1) sage: M = Stream_add(l, r, False) - sage: [M[i] for i in range(5)] - [0, 2, 6, 12, 20] - sage: M._cache - [2, 6, 12, 20] - sage: l._cache - [1, 2, 3, 4] - sage: r._cache - [1, 4, 9, 16] - sage: M.recursive_map_largest_cached(lambda x: x + 10) - sage: M._cache - [2, 6, 12, 30] - sage: l._cache - [1, 2, 3, 14] - sage: r._cache - [1, 4, 9, 26] - """ - super().recursive_map_largest_cached(function) - self._left.recursive_map_largest_cached(function) - self._right.recursive_map_largest_cached(function) + sage: M.parent_streams() + [, + ] + """ + return [self._left, self._right] class Stream_binaryCommutative(Stream_binary): @@ -2877,10 +2816,10 @@ def stretched_power_restrict_degree(self, i, m, d): return self._basis.zero() - def recursive_map_largest_cached(self, function): + def parent_streams(self): r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. + Return the list of streams which are used to compute the + coefficients of ``self``. EXAMPLES:: @@ -2890,32 +2829,18 @@ def recursive_map_largest_cached(self, function): sage: f = Stream_function(lambda n: s[n], True, 1) sage: g = Stream_function(lambda n: s[n-1,1], True, 2) sage: h = Stream_plethysm(f, g, True, p) + sage: h.parent_streams() + [] sage: [h[i] for i in range(1, 5)] - [0, 1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3], + [0, + 1/2*p[1, 1] - 1/2*p[2], + 1/3*p[1, 1, 1] - 1/3*p[3], 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] - 1/2*p[4]] - sage: h._cache - {2: 1/2*p[1, 1] - 1/2*p[2], - 3: 1/3*p[1, 1, 1] - 1/3*p[3], - 4: 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] - 1/2*p[4]} - sage: [hp._cache for hp in h._powers] - [{2: 1/2*p[1, 1] - 1/2*p[2], - 3: 1/3*p[1, 1, 1] - 1/3*p[3], - 4: 1/8*p[1, 1, 1, 1] + 1/4*p[2, 1, 1] - 1/8*p[2, 2] - 1/4*p[4]}, - {4: 1/4*p[1, 1, 1, 1] - 1/2*p[2, 1, 1] + 1/4*p[2, 2]}] - sage: h.recursive_map_largest_cached(lambda x: x/2) - sage: h._cache - {2: 1/2*p[1, 1] - 1/2*p[2], - 3: 1/3*p[1, 1, 1] - 1/3*p[3], - 4: 1/8*p[1, 1, 1, 1] + 1/8*p[2, 2] - 1/4*p[4]} - sage: [hp._cache for hp in h._powers] - [{2: 1/2*p[1, 1] - 1/2*p[2], - 3: 1/3*p[1, 1, 1] - 1/3*p[3], - 4: 1/128*p[1, 1, 1, 1] + 1/64*p[2, 1, 1] - 1/128*p[2, 2] - 1/64*p[4]}, - {4: 1/8*p[1, 1, 1, 1] - 1/4*p[2, 1, 1] + 1/8*p[2, 2]}] - """ - super().recursive_map_largest_cached(function) - for g in self._powers: - g.recursive_map_largest_cached(function) + sage: h.parent_streams() + [, + ] + """ + return self._powers ##################################################################### @@ -3760,27 +3685,6 @@ def replace(self, stream, sub): ret._series = temp return ret - def recursive_map_largest_cached(self, function): - r""" - Update the largest indexed entry in the cache by applying ``function`` - and proceed recursively through any dependent streams. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_shift - sage: from sage.data_structures.stream import Stream_function - sage: h = Stream_function(lambda n: n, False, -5) - sage: M = Stream_shift(h, 2) - sage: [M[i] for i in range(-5, 5)] - [0, 0, -5, -4, -3, -2, -1, 0, 1, 2] - sage: h._cache - [-5, -4, -3, -2, -1, 0, 1, 2] - sage: M.recursive_map_largest_cached(lambda x: x + 10) - sage: h._cache - [-5, -4, -3, -2, -1, 0, 1, 12] - """ - self._series.recursive_map_largest_cached(function) - class Stream_truncated(Stream_unary): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 6f8920ed63d..6593a8bc260 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1670,6 +1670,14 @@ def define_implicity(self, eqn, initial_values=None): sage: g.define_implicity(2+z*g(z^2) - g, [5]) sage: g + + TESTS:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: f.define_implicity(log(1+f) - ~(1 + f) + 1, []) + sage: f + """ if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") From aaf978fb640f0dd535c3a02cb96177919b679492 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 29 Oct 2023 20:07:51 +0100 Subject: [PATCH 010/507] slightly improve substitution, test is failing elsewhere --- src/sage/data_structures/stream.py | 23 +++++++++++++---------- src/sage/rings/lazy_series.py | 3 ++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index d4de5aff3b3..0de0ba4a96f 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1456,6 +1456,9 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ super().__init__(False, true_order) self._F = F self._base = R + from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing + self._P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), implementation='sparse') + self._PFF = self._P.fraction_field() self._initial_values = initial_values self._approximate_order = approximate_order self._uninitialized = uninitialized @@ -1470,8 +1473,13 @@ def _subs_in_caches(self, s, var, val): else: i = -1 c = s._cache[i] - if c not in self._base: - s._cache[i] = self._base(c.subs({var: val})) + if hasattr(c, "parent"): + if c.parent() is self._PFF: + num = c.numerator().subs({var: val}) + den = c.denominator().subs({var: val}) + s._cache[i] = self._base(num/den) + elif c.parent() is self._P: + s._cache[i] = self._base(c.subs({var: val})) for t in s.parent_streams(): self._subs_in_caches(t, var, val) @@ -1493,13 +1501,8 @@ def iterate_coefficients(self): [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] """ yield from self._initial_values - - from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), implementation='sparse') - x = P.gen() - PFF = P.fraction_field() + x = self._P.gen() offset = self._approximate_order - def get_coeff(n): n -= offset if n < len(self._initial_values): @@ -1513,10 +1516,10 @@ def get_coeff(n): m = len(self._initial_values) while True: coeff = self._F[n] - if coeff.parent() is PFF: + if coeff.parent() is self._PFF: coeff = coeff.numerator() else: - coeff = P(coeff) + coeff = self._P(coeff) V = coeff.variables() if len(V) > 1: diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 6593a8bc260..22c90512fb4 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1674,9 +1674,10 @@ def define_implicity(self, eqn, initial_values=None): TESTS:: sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(0) + sage: f = L.undefined(1) sage: f.define_implicity(log(1+f) - ~(1 + f) + 1, []) sage: f + 0 """ if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: From 95f00b0af042c7827c920f795047fdd8ce7bc281 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 30 Oct 2023 10:20:04 +0100 Subject: [PATCH 011/507] fix a typo and make the linter happy --- src/sage/data_structures/stream.py | 1 + src/sage/rings/lazy_series.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 0de0ba4a96f..4b456fbb57f 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1503,6 +1503,7 @@ def iterate_coefficients(self): yield from self._initial_values x = self._P.gen() offset = self._approximate_order + def get_coeff(n): n -= offset if n < len(self._initial_values): diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 22c90512fb4..46e44d363f4 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1550,7 +1550,7 @@ def define(self, s): # an alias for compatibility with padics set = define - def define_implicity(self, eqn, initial_values=None): + def define_implicitly(self, eqn, initial_values=None): r""" Define ``self`` as the series that solves the functional equation ``eqn == 0`` with ``initial_values``. @@ -1560,7 +1560,7 @@ def define_implicity(self, eqn, initial_values=None): sage: L. = LazyPowerSeriesRing(QQ) sage: f = L.undefined(0) sage: F = diff(f, 2) - sage: f.define_implicity(F + f, [1, 0]) + sage: f.define_implicitly(F + f, [1, 0]) sage: f 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) sage: cos(z) @@ -1570,7 +1570,7 @@ def define_implicity(self, eqn, initial_values=None): sage: L. = LazyPowerSeriesRing(QQ) sage: f = L.undefined(0) - sage: f.define_implicity(2*z*f(z^3) + z*f^3 - 3*f + 3) + sage: f.define_implicitly(2*z*f(z^3) + z*f^3 - 3*f + 3) sage: f 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) @@ -1584,7 +1584,7 @@ def define_implicity(self, eqn, initial_values=None): sage: e2 = g * z2 - 3 * z1^2 sage: e3 = g * z2 - 3 * z1^2 sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 - sage: g.define_implicity(e, [1, 2]) + sage: g.define_implicitly(e, [1, 2]) sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol 1 + 2*z + 2*z^4 + O(z^7) @@ -1595,13 +1595,13 @@ def define_implicity(self, eqn, initial_values=None): sage: L. = LazyPowerSeriesRing(SR) sage: G = L.undefined(0) - sage: G.define_implicity(diff(G) - exp(-G(-z)), [ln(2)]) + sage: G.define_implicitly(diff(G) - exp(-G(-z)), [ln(2)]) sage: G log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) sage: L. = LazyPowerSeriesRing(RR) sage: G = L.undefined(0) - sage: G.define_implicity(diff(G) - exp(-G(-z)), [log(2)]) + sage: G.define_implicitly(diff(G) - exp(-G(-z)), [log(2)]) sage: G 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) @@ -1612,7 +1612,7 @@ def define_implicity(self, eqn, initial_values=None): sage: q, y = QQ['q,y'].fraction_field().gens() sage: L. = LazyPowerSeriesRing(q.parent()) sage: R = L.undefined() - sage: R.define_implicity((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, [0]) + sage: R.define_implicitly((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, [0]) sage: R[0] 0 sage: R[1] @@ -1624,7 +1624,7 @@ def define_implicity(self, eqn, initial_values=None): * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) sage: Rp = L.undefined(1) - sage: Rp.define_implicity((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp) + sage: Rp.define_implicitly((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp) sage: all(R[n] == Rp[n] for n in range(7)) True @@ -1634,7 +1634,7 @@ def define_implicity(self, eqn, initial_values=None): sage: L.base_ring().inject_variables() Defining x, y, f1, f2 sage: F = L.undefined() - sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1, f2]) + sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1, f2]) sage: F f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) @@ -1647,12 +1647,12 @@ def define_implicity(self, eqn, initial_values=None): components to get a unique solution in the previous example:: sage: F = L.undefined() - sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) + sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) sage: F sage: F = L.undefined() - sage: F.define_implicity(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) + sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) sage: F @@ -1660,14 +1660,14 @@ def define_implicity(self, eqn, initial_values=None): sage: L. = LazyLaurentSeriesRing(QQ) sage: f = L.undefined(-1) - sage: f.define_implicity(2+z*f(z^2) - f, [5]) + sage: f.define_implicitly(2+z*f(z^2) - f, [5]) sage: f 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) sage: 2 + z*f(z^2) - f O(z^6) sage: g = L.undefined(-2) - sage: g.define_implicity(2+z*g(z^2) - g, [5]) + sage: g.define_implicitly(2+z*g(z^2) - g, [5]) sage: g @@ -1675,7 +1675,7 @@ def define_implicity(self, eqn, initial_values=None): sage: L. = LazyPowerSeriesRing(QQ) sage: f = L.undefined(1) - sage: f.define_implicity(log(1+f) - ~(1 + f) + 1, []) + sage: f.define_implicitly(log(1+f) - ~(1 + f) + 1, []) sage: f 0 From 9f116487563c5f407f5c24c3f8493db5ab3ed347 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 2 Nov 2023 10:26:15 +0100 Subject: [PATCH 012/507] use a custom getitem method --- src/sage/data_structures/stream.py | 125 +++++++++++++++++------------ src/sage/rings/lazy_series.py | 11 +++ 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 4b456fbb57f..52575e9eddb 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -365,8 +365,8 @@ def __setstate__(self, d): """ self.__dict__ = d if not self._is_sparse: + self._cache = list() self._iter = self.iterate_coefficients() - self._cache = [] def __getitem__(self, n): """ @@ -1397,7 +1397,7 @@ def parent_streams(self): return [] -class Stream_functional_equation(Stream_inexact): +class Stream_functional_equation(Stream): r""" Coefficient stream defined by a functional equation `F = 0`. @@ -1443,7 +1443,7 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ raise ValueError("the valuation must be specified for undefined series") if initial_values is None: initial_values = [] - self._start = approximate_order + for i, val in enumerate(initial_values): if val: approximate_order += i @@ -1453,17 +1453,72 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ else: approximate_order += len(initial_values) initial_values = [] - super().__init__(False, true_order) + super().__init__(true_order) + self._is_sparse = False self._F = F self._base = R from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - self._P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), implementation='sparse') + self._P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), + implementation='sparse') + self._x = self._P.gen() self._PFF = self._P.fraction_field() - self._initial_values = initial_values + for i, v in enumerate(initial_values): + if v: + self._cache = initial_values[i:] + self._true_order = True + break + approximate_order += 1 + else: + self._cache = [] self._approximate_order = approximate_order - self._uninitialized = uninitialized - self._uninitialized._approximate_order = approximate_order - self._uninitialized._target = self + self._n = approximate_order + len(self._cache) - 1 # the largest index of a coefficient we know + self._uncomputed = True + self._last_eq_n = self._F._approximate_order - 1 + uninitialized._target = self + + def parent_streams(self): + r""" + Return the list of streams which are used to compute the + coefficients of ``self``. + """ + return [] + + def __getitem__(self, n): + if n < self._approximate_order: + return ZZ.zero() + + if self._n >= n: + return self._cache[n - self._approximate_order] + + if self._uncomputed: + self._uncomputed = False + while not self._true_order and n >= self._approximate_order: + for k in range(self._n+1, n+1): + v, val = self._compute() + if val: + self._true_order = True + self._cache[-1] = val + else: + self._approximate_order += 1 + del self._cache[-1] + self._subs_in_caches(self._F, v, val) + self._n += 1 + + if self._true_order: + for k in range(self._n+1, n+1): + v, val = self._compute() + self._cache[-1] = val + self._subs_in_caches(self._F, v, val) + self._n += 1 + self._uncomputed = True + + if len(self._cache) == n - self._approximate_order + 1: + if n >= self._approximate_order: + return self._cache[n - self._approximate_order] + return ZZ.zero() + + self._cache.append(self._x[n]) + return self._cache[-1] def _subs_in_caches(self, s, var, val): if hasattr(s, "_cache"): @@ -1483,40 +1538,10 @@ def _subs_in_caches(self, s, var, val): for t in s.parent_streams(): self._subs_in_caches(t, var, val) - def iterate_coefficients(self): - """ - A generator for the coefficients of ``self``. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_functional_equation - sage: from sage.data_structures.stream import Stream_derivative, Stream_sub - sage: C = Stream_uninitialized(0) - sage: D = Stream_derivative(C, 1, False) - sage: F = Stream_sub(D, C, False) - sage: S = Stream_functional_equation(0, F, C, [1], QQ) - sage: n = S.iterate_coefficients() - sage: [next(n) for _ in range(10)] - [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] - """ - yield from self._initial_values - x = self._P.gen() - offset = self._approximate_order - - def get_coeff(n): - n -= offset - if n < len(self._initial_values): - return self._initial_values[n] - return x[n] - - sf = Stream_function(get_coeff, is_sparse=False, approximate_order=offset, true_order=True) - self._F = self._F.replace(self._uninitialized, sf) - - n = self._F._approximate_order - m = len(self._initial_values) + def _compute(self): while True: - coeff = self._F[n] + self._last_eq_n += 1 + coeff = self._F[self._last_eq_n] if coeff.parent() is self._PFF: coeff = coeff.numerator() else: @@ -1524,12 +1549,11 @@ def get_coeff(n): V = coeff.variables() if len(V) > 1: - raise ValueError(f"unable to determine a unique solution in degree {n}") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}") if not V: if coeff: - raise ValueError(f"no solution in degree {n} as {coeff} != 0") - n += 1 + raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") continue # single variable to solve for @@ -1539,16 +1563,11 @@ def get_coeff(n): else: if set(hc) != set([0, 1]): raise ValueError(f"unable to determine a unique solution in degree {n}") - if str(hc[1].lm()) != str(x[m]): - raise ValueError(f"the solutions to the coefficients must be computed in order") +# if str(hc[1].lm()) != str(self._x[m]): +# raise ValueError(f"the solutions to the coefficients must be computed in order") val = self._base(-hc[0].lc() / hc[1].lc()) - # Update the caches - self._subs_in_caches(sf, V[0], val) - self._subs_in_caches(self._F, V[0], val) - yield val - n += 1 - m += 1 + return V[0], val class Stream_unary(Stream_inexact): r""" diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 46e44d363f4..75becfc25d5 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1679,6 +1679,17 @@ def define_implicitly(self, eqn, initial_values=None): sage: f 0 + We run into the same problem:: + + sage: f[1] + sage: f._coeff_stream._F._left._left.get_coefficient.__closure__[1].cell_contents.__dict__ + {'_left': , + '_right': , + '_true_order': True, + '_is_sparse': True, + '_cache': {0: FESDUMMY_1}, + '_approximate_order': 0} + """ if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") From 185788e03dc873aa036b8ddf1c449da19376301d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 9 Nov 2023 22:49:36 +0100 Subject: [PATCH 013/507] add optional argument input_streams to Stream_function --- src/sage/data_structures/stream.py | 266 +++-------------------------- src/sage/rings/lazy_series.py | 29 ++-- 2 files changed, 42 insertions(+), 253 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 52575e9eddb..8910a738c4f 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -214,22 +214,7 @@ def is_uninitialized(self): """ return False - def replace(self, stream, sub): - """ - Return ``self`` except with ``stream`` replaced by ``sub``. - - The default is to return ``self``. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_zero - sage: zero = Stream_zero() - sage: zero.replace(zero, zero) is zero - True - """ - return self - - def parent_streams(self): + def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``. @@ -238,7 +223,7 @@ def parent_streams(self): sage: from sage.data_structures.stream import Stream_zero sage: z = Stream_zero() - sage: z.parent_streams() + sage: z.input_streams() [] """ return [] @@ -991,6 +976,8 @@ class Stream_function(Stream_inexact): - ``is_sparse`` -- boolean; specifies whether the stream is sparse - ``approximate_order`` -- integer; a lower bound for the order of the stream + - ``input_streams`` -- optional, a list of streams that are + involved in the computation of the coefficients of ``self`` .. NOTE:: @@ -1014,8 +1001,9 @@ class Stream_function(Stream_inexact): sage: f = Stream_function(lambda n: n, True, 0) sage: f[4] 4 + """ - def __init__(self, function, is_sparse, approximate_order, true_order=False): + def __init__(self, function, is_sparse, approximate_order, true_order=False, input_streams=[]): """ Initialize. @@ -1028,6 +1016,14 @@ def __init__(self, function, is_sparse, approximate_order, true_order=False): self.get_coefficient = function super().__init__(is_sparse, true_order) self._approximate_order = approximate_order + self._input_streams = input_streams + + def input_streams(self): + r""" + Return the list of streams which are used to compute the + coefficients of ``self``, as provided. + """ + return self._input_streams def __hash__(self): """ @@ -1329,52 +1325,7 @@ def is_uninitialized(self): self._initializing = False return result - def replace(self, stream, sub): - r""" - Return ``self`` except with ``stream`` replaced by ``sub``. - - .. WARNING:: - - This does not update the approximate order or the cache. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized, Stream_shift, Stream_function - sage: U = Stream_uninitialized(0) - sage: F = Stream_function(lambda n: 1, False, 0) - sage: X = Stream_function(lambda n: n, False, 0) - sage: S = Stream_shift(F, -3) - sage: U.replace(X, F) is U - True - sage: U._target = S - sage: Up = U.replace(S, X) - sage: Up == U - False - sage: [Up[i] for i in range(5)] - [0, 1, 2, 3, 4] - sage: Upp = U.replace(F, X) - sage: Upp == U - False - sage: [Upp[i] for i in range(5)] - [3, 4, 5, 6, 7] - sage: [U[i] for i in range(5)] - [1, 1, 1, 1, 1] - """ - if self._target is None: - return self - if self._target == stream: - ret = copy(self) - ret._target = sub - else: - temp = self._target.replace(stream, sub) - if temp == self._target: - ret = self - else: - ret = copy(self) - ret._target = temp - return ret - - def parent_streams(self): + def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``. @@ -1384,12 +1335,12 @@ def parent_streams(self): sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function sage: h = Stream_function(lambda n: n, False, 1) sage: M = Stream_uninitialized(0) - sage: M.parent_streams() + sage: M.input_streams() [] sage: M._target = h sage: [h[i] for i in range(5)] [0, 1, 2, 3, 4] - sage: M.parent_streams() + sage: M.input_streams() [] """ if self._target is not None: @@ -1476,13 +1427,6 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ self._last_eq_n = self._F._approximate_order - 1 uninitialized._target = self - def parent_streams(self): - r""" - Return the list of streams which are used to compute the - coefficients of ``self``. - """ - return [] - def __getitem__(self, n): if n < self._approximate_order: return ZZ.zero() @@ -1535,7 +1479,7 @@ def _subs_in_caches(self, s, var, val): s._cache[i] = self._base(num/den) elif c.parent() is self._P: s._cache[i] = self._base(c.subs({var: val})) - for t in s.parent_streams(): + for t in s.input_streams(): self._subs_in_caches(t, var, val) def _compute(self): @@ -1660,49 +1604,7 @@ def is_uninitialized(self): """ return self._series.is_uninitialized() - def replace(self, stream, sub): - r""" - Return ``self`` except with ``stream`` replaced by ``sub``. - - .. WARNING:: - - This does not update the approximate order or the cache. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_shift, Stream_neg, Stream_function - sage: F = Stream_function(lambda n: 1, False, 0) - sage: X = Stream_function(lambda n: n, False, 0) - sage: S = Stream_shift(F, -3) - sage: N = Stream_neg(S, False) - sage: N.replace(X, F) is N - True - sage: Np = N.replace(F, X) - sage: Np == N - False - sage: [Np[i] for i in range(5)] - [-3, -4, -5, -6, -7] - sage: Npp = N.replace(S, X) - sage: Npp == N - False - sage: [Npp[i] for i in range(5)] - [0, -1, -2, -3, -4] - sage: [N[i] for i in range(5)] - [-1, -1, -1, -1, -1] - """ - if self._series == stream: - ret = copy(self) - ret._series = sub - else: - temp = self._series.replace(stream, sub) - if temp == self._series: - ret = self - else: - ret = copy(self) - ret._series = temp - return ret - - def parent_streams(self): + def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``. @@ -1712,7 +1614,7 @@ def parent_streams(self): sage: from sage.data_structures.stream import Stream_function, Stream_neg sage: h = Stream_function(lambda n: n, False, 1) sage: M = Stream_neg(h, False) - sage: M.parent_streams() + sage: M.input_streams() [] """ return [self._series] @@ -1822,83 +1724,7 @@ def is_uninitialized(self): """ return self._left.is_uninitialized() or self._right.is_uninitialized() - def replace(self, stream, sub): - r""" - Return ``self`` except with ``stream`` replaced by ``sub``. - - .. WARNING:: - - This does not update the approximate order or the cache. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_neg, Stream_sub, Stream_function - sage: L = Stream_function(lambda n: 1, False, 0) - sage: R = Stream_function(lambda n: n, False, 0) - sage: NL = Stream_neg(L, False) - sage: NR = Stream_neg(R, False) - sage: S = Stream_sub(NL, NR, False) - sage: S.replace(Stream_function(lambda n: n^2, False, 0), R) is S - True - sage: Sp = S.replace(L, R) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [0, 0, 0, 0, 0] - - Because we have computed some values of the cache for ``NR`` (which - is copied), we get the following wrong result:: - - sage: Sp = S.replace(R, L) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [-1, 0, 0, 0, 0] - - With fresh caches:: - - sage: NL = Stream_neg(L, False) - sage: NR = Stream_neg(R, False) - sage: S = Stream_sub(NL, NR, False) - sage: Sp = S.replace(R, L) - sage: [Sp[i] for i in range(5)] - [0, 0, 0, 0, 0] - - The replacements here do not affect the relevant caches:: - - sage: Sp = S.replace(NL, L) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [1, 2, 3, 4, 5] - sage: Sp = S.replace(NR, R) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [-1, -2, -3, -4, -5] - """ - if self._left == stream: - ret = copy(self) - ret._left = sub - else: - temp = self._left.replace(stream, sub) - if temp == self._left: - ret = self - else: - ret = copy(self) - ret._left = temp - # It is possible that both the left and right are the same stream - if self._right == stream: - ret = copy(ret) - ret._right = sub - else: - temp = ret._right.replace(stream, sub) - if not (temp == self._right): - ret = copy(ret) - ret._right = temp - return ret - - def parent_streams(self): + def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``. @@ -1909,7 +1735,7 @@ def parent_streams(self): sage: l = Stream_function(lambda n: n, False, 1) sage: r = Stream_function(lambda n: n^2, False, 1) sage: M = Stream_add(l, r, False) - sage: M.parent_streams() + sage: M.input_streams() [, ] """ @@ -2839,7 +2665,7 @@ def stretched_power_restrict_degree(self, i, m, d): return self._basis.zero() - def parent_streams(self): + def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``. @@ -2852,14 +2678,14 @@ def parent_streams(self): sage: f = Stream_function(lambda n: s[n], True, 1) sage: g = Stream_function(lambda n: s[n-1,1], True, 2) sage: h = Stream_plethysm(f, g, True, p) - sage: h.parent_streams() + sage: h.input_streams() [] sage: [h[i] for i in range(1, 5)] [0, 1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3], 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] - 1/2*p[4]] - sage: h.parent_streams() + sage: h.input_streams() [, ] """ @@ -3666,48 +3492,6 @@ def is_uninitialized(self): """ return self._series.is_uninitialized() - def replace(self, stream, sub): - r""" - Return ``self`` except with ``stream`` replaced by ``sub``. - - .. WARNING:: - - This does not update the approximate order. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized, Stream_shift, Stream_function - sage: F = Stream_function(lambda n: 1, False, 0) - sage: X = Stream_function(lambda n: n, False, 0) - sage: S = Stream_shift(F, -3) - sage: S.replace(X, F) is S - True - sage: Sp = S.replace(F, X) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [3, 4, 5, 6, 7] - sage: U = Stream_uninitialized(0) - sage: U._target = F - sage: S = Stream_shift(U, -3) - sage: Sp = S.replace(F, X) - sage: Sp == S - False - sage: [Sp[i] for i in range(5)] - [3, 4, 5, 6, 7] - """ - if self._series == stream: - ret = copy(self) - ret._series = sub - else: - temp = self._series.replace(stream, sub) - if temp == self._series: - ret = self - else: - ret = copy(self) - ret._series = temp - return ret - class Stream_truncated(Stream_unary): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 75becfc25d5..1c11089ebdc 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1679,17 +1679,20 @@ def define_implicitly(self, eqn, initial_values=None): sage: f 0 - We run into the same problem:: - + sage: f = L.undefined(0) + sage: fp = f.derivative() + sage: g = L(lambda n: 0 if n < 10 else 1, 0) + sage: f.define_implicitly(f.derivative() * g + f) + sage: f[0] + 0 + sage: fp[0] + 0 + sage: fp[1] + 0 + sage: fp[2] + 0 sage: f[1] - sage: f._coeff_stream._F._left._left.get_coefficient.__closure__[1].cell_contents.__dict__ - {'_left': , - '_right': , - '_true_order': True, - '_is_sparse': True, - '_cache': {0: FESDUMMY_1}, - '_approximate_order': 0} - + 0 """ if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") @@ -3805,7 +3808,8 @@ def exp(self): # of the product are of the form sum_{k=1}^n a_k a_{n+1-k}. d_self_f = Stream_cauchy_mul_commutative(d_self, f._coeff_stream, False) int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), - False, 0) + False, 0, + input_streams=[d_self_f]) f._coeff_stream._target = int_d_self_f return f @@ -3859,7 +3863,8 @@ def log(self): coeff_stream_inverse, P.is_sparse()) int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), - P.is_sparse(), 1) + P.is_sparse(), 1, + input_streams=[d_self_quo_self]) return P.element_class(P, int_d_self_quo_self) From caea280fd015ab621c8c645cd993341e16691e8a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 20 Nov 2023 12:13:02 +0100 Subject: [PATCH 014/507] WIP: merge Stream_functional_equation into Stream_uninitialized --- src/sage/data_structures/stream.py | 328 ++++++++++++++--------------- src/sage/rings/lazy_series.py | 20 +- 2 files changed, 166 insertions(+), 182 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 8910a738c4f..be1adaec5df 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -161,6 +161,39 @@ def _approximate_order(self): """ raise NotImplementedError + def order(self): + r""" + Return the order of ``self``, which is the minimum index ``n`` such + that ``self[n]`` is non-zero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function + sage: f = Stream_function(lambda n: n, True, 0) + sage: f.order() + 1 + + TESTS:: + + sage: f = Stream_function(lambda n: n*(n+1), False, -1) + sage: f.order() + 1 + sage: f._true_order + True + + sage: f = Stream_function(lambda n: n*(n+1), True, -1) + sage: f.order() + 1 + sage: f._true_order + True + """ + if self._true_order: + return self._approximate_order + n = self._approximate_order + while not self[n]: + n += 1 + return n + def __ne__(self, other): """ Return whether ``self`` and ``other`` are known to be different. @@ -453,39 +486,6 @@ def iterate_coefficients(self): yield self.get_coefficient(n) n += 1 - def order(self): - r""" - Return the order of ``self``, which is the minimum index ``n`` such - that ``self[n]`` is non-zero. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_function - sage: f = Stream_function(lambda n: n, True, 0) - sage: f.order() - 1 - - TESTS:: - - sage: f = Stream_function(lambda n: n*(n+1), False, -1) - sage: f.order() - 1 - sage: f._true_order - True - - sage: f = Stream_function(lambda n: n*(n+1), True, -1) - sage: f.order() - 1 - sage: f._true_order - True - """ - if self._true_order: - return self._approximate_order - n = self._approximate_order - while not self[n]: - n += 1 - return n - def __ne__(self, other): """ Return whether ``self`` and ``other`` are known to be different. @@ -1230,7 +1230,7 @@ def iterate_coefficients(self): denom *= n -class Stream_uninitialized(Stream_inexact): +class Stream_uninitialized(Stream): r""" Coefficient stream for an uninitialized series. @@ -1268,144 +1268,37 @@ def __init__(self, approximate_order, true_order=False): sage: TestSuite(C).run(skip="_test_pickling") """ self._target = None + self._F = None if approximate_order is None: raise ValueError("the valuation must be specified for undefined series") - super().__init__(False, true_order) + super().__init__(true_order) self._approximate_order = approximate_order self._initializing = False + self._is_sparse = False - def iterate_coefficients(self): - """ - A generator for the coefficients of ``self``. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_exact - sage: z = Stream_exact([1], order=1) - sage: C = Stream_uninitialized(0) - sage: C._target - sage: C._target = z - sage: n = C.iterate_coefficients() - sage: [next(n) for _ in range(10)] - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] - """ - n = self._approximate_order - while True: - yield self._target[n] - n += 1 - - def is_uninitialized(self): - """ - Return ``True`` if ``self`` is an uninitialized stream. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: C = Stream_uninitialized(0) - sage: C.is_uninitialized() - True - - A more subtle uninitialized series:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: T = L.undefined(1) - sage: D = L.undefined(0) - sage: T.define(z * exp(T) * D) - sage: T._coeff_stream.is_uninitialized() - True - """ - if self._target is None: - return True - if self._initializing: - return False - # We implement semaphore-like behavior for coupled (undefined) series - self._initializing = True - result = self._target.is_uninitialized() - self._initializing = False - return result - - def input_streams(self): - r""" - Return the list of streams which are used to compute the - coefficients of ``self``. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function - sage: h = Stream_function(lambda n: n, False, 1) - sage: M = Stream_uninitialized(0) - sage: M.input_streams() - [] - sage: M._target = h - sage: [h[i] for i in range(5)] - [0, 1, 2, 3, 4] - sage: M.input_streams() - [] - """ - if self._target is not None: - return [self._target] - return [] - - -class Stream_functional_equation(Stream): - r""" - Coefficient stream defined by a functional equation `F = 0`. - - INPUT: - - - ``approximate_order`` -- integer; a lower bound for the order - of the stream - - ``F`` -- the stream for the equation using ``uninitialized`` - - ``uninitialized`` -- the uninitialized stream - - ``initial_values`` -- the initial values - - ``R`` -- the base ring - - Instances of this class are always dense. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_functional_equation - sage: from sage.data_structures.stream import Stream_derivative, Stream_sub - sage: C = Stream_uninitialized(0) - sage: D = Stream_derivative(C, 1, False) - sage: F = Stream_sub(D, C, False) - sage: S = Stream_functional_equation(0, F, C, [1], QQ) - sage: [S[i] for i in range(10)] - [1, 1, 1/2, 1/6, 1/24, 1/120, 1/720, 1/5040, 1/40320, 1/362880] - """ - def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_order=False): - """ - Initialize ``self``. + def define(self, target): + self._target = target + self._n = self._approximate_order - 1 # the largest index of a coefficient we know + # we only need this if target is not dense + self._cache = list() + self._iter = self.iterate_coefficients() - TESTS:: + def define_implicitly(self, F, initial_values, R): + assert self._target is None - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_functional_equation - sage: from sage.data_structures.stream import Stream_derivative, Stream_sub - sage: C = Stream_uninitialized(0) - sage: D = Stream_derivative(C, 1, False) - sage: F = Stream_sub(D, C, False) - sage: S = Stream_functional_equation(0, F, C, [1], QQ) - sage: TestSuite(S).run(skip="_test_pickling") - """ - if approximate_order is None: - raise ValueError("the valuation must be specified for undefined series") if initial_values is None: initial_values = [] for i, val in enumerate(initial_values): if val: - approximate_order += i - true_order = True - initial_values = initial_values[i:] + self._approximate_order += i + self._true_order = True + self._cache = initial_values[i:] break else: - approximate_order += len(initial_values) - initial_values = [] - super().__init__(true_order) - self._is_sparse = False + self._approximate_order += len(initial_values) + self._cache = [] + self._F = F self._base = R from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing @@ -1413,24 +1306,42 @@ def __init__(self, approximate_order, F, uninitialized, initial_values, R, true_ implementation='sparse') self._x = self._P.gen() self._PFF = self._P.fraction_field() - for i, v in enumerate(initial_values): - if v: - self._cache = initial_values[i:] - self._true_order = True - break - approximate_order += 1 - else: - self._cache = [] - self._approximate_order = approximate_order - self._n = approximate_order + len(self._cache) - 1 # the largest index of a coefficient we know self._uncomputed = True - self._last_eq_n = self._F._approximate_order - 1 - uninitialized._target = self + self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used + self._n = self._approximate_order + len(self._cache) - 1 # the largest index of a coefficient we know def __getitem__(self, n): if n < self._approximate_order: return ZZ.zero() + # define + if self._target is not None: + while not self._true_order and n >= self._approximate_order: + c = next(self._iter) + if c: + self._true_order = True + self._cache.append(c) + else: + self._approximate_order += 1 + + if self._true_order: + # It is important to extend by generator: + # self._iter might recurse, and thereby extend the + # cache itself, too. + i = n - self._approximate_order + self._cache.extend(next(self._iter) + for _ in range(i - len(self._cache) + 1)) + return self._cache[i] + + return ZZ.zero() + +# if target is dense, we do not need to duplicate its cache +# for i in range(self._n+1, n): +# self._target[i] +# self._n = n +# return self._target[n] + + # define_implicitly if self._n >= n: return self._cache[n - self._approximate_order] @@ -1513,6 +1424,83 @@ def _compute(self): return V[0], val + def iterate_coefficients(self): + """ + A generator for the coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized + sage: from sage.data_structures.stream import Stream_exact + sage: z = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(0) + sage: C._target + sage: C._target = z + sage: n = C.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] + """ + n = self._approximate_order + while True: + yield self._target[n] + n += 1 + + def is_uninitialized(self): + """ + Return ``True`` if ``self`` is an uninitialized stream. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized + sage: C = Stream_uninitialized(0) + sage: C.is_uninitialized() + True + + A more subtle uninitialized series:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: T = L.undefined(1) + sage: D = L.undefined(0) + sage: T.define(z * exp(T) * D) + sage: T._coeff_stream.is_uninitialized() + True + """ + if self._target is None and self._F is None: + return True + if self._initializing: + return False + # We implement semaphore-like behavior for coupled (undefined) series + self._initializing = True + if self._target is None: + result = self._F.is_uninitialized() + else: + result = self._target.is_uninitialized() + self._initializing = False + return result + + def input_streams(self): + r""" + Return the list of streams which are used to compute the + coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function + sage: h = Stream_function(lambda n: n, False, 1) + sage: M = Stream_uninitialized(0) + sage: M.input_streams() + [] + sage: M._target = h + sage: [h[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: M.input_streams() + [] + """ + if self._target is not None: + return [self._target] + return [] + + class Stream_unary(Stream_inexact): r""" Base class for unary operators on coefficient streams. diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index f18424a991a..32cccb50fa4 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -253,7 +253,6 @@ Stream_zero, Stream_exact, Stream_uninitialized, - Stream_functional_equation, Stream_shift, Stream_truncated, Stream_function, @@ -1545,7 +1544,7 @@ def define(self, s): self._coeff_stream = coeff_stream return - self._coeff_stream._target = coeff_stream + self._coeff_stream.define(coeff_stream) # an alias for compatibility with padics set = define @@ -1697,17 +1696,14 @@ def define_implicitly(self, eqn, initial_values=None): if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") - if initial_values is None: - initial_values = [] - P = self.parent() - eqn = P(eqn) - cs = self._coeff_stream - ao = cs._approximate_order + F = P(eqn)._coeff_stream R = P.base_ring() - initial_values = [R(val) for val in initial_values] - ret = Stream_functional_equation(ao, eqn._coeff_stream, cs, initial_values, R) - self._coeff_stream = ret + if initial_values is None: + initial_values = [] + else: + initial_values = [R(val) for val in initial_values] + self._coeff_stream.define_implicitly(F, initial_values, R) def _repr_(self): r""" @@ -3810,7 +3806,7 @@ def exp(self): int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), False, 0, input_streams=[d_self_f]) - f._coeff_stream._target = int_d_self_f + f._coeff_stream.define(int_d_self_f) return f def log(self): From 4c6e0894f2d48d494efc8931e6c4adf36cf5c7ac Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 20 Nov 2023 18:06:55 +0100 Subject: [PATCH 015/507] WIP: find all input streams at setup - bug because there are equal streams which are different --- src/sage/data_structures/stream.py | 97 +++++++++++++++++------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index be1adaec5df..2ca79e54082 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -104,6 +104,7 @@ from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.misc.cachefunc import cached_method from copy import copy +from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1276,6 +1277,30 @@ def __init__(self, approximate_order, true_order=False): self._initializing = False self._is_sparse = False + def input_streams(self): + r""" + Return the list of streams which are used to compute the + coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function + sage: h = Stream_function(lambda n: n, False, 1) + sage: M = Stream_uninitialized(0) + sage: M.input_streams() + [] + sage: M._target = h + sage: [h[i] for i in range(5)] + [0, 1, 2, 3, 4] + sage: M.input_streams() + [] + """ + if self._target is not None: + return [self._target] + if self._F is not None: + return [self._F] + return [] + def define(self, target): self._target = target self._n = self._approximate_order - 1 # the largest index of a coefficient we know @@ -1308,7 +1333,13 @@ def define_implicitly(self, F, initial_values, R): self._PFF = self._P.fraction_field() self._uncomputed = True self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used - self._n = self._approximate_order + len(self._cache) - 1 # the largest index of a coefficient we know + self._n = self._approximate_order + len(self._cache) - 1 # the index of the last coefficient we know + + def children(c): + return [s for s in c.input_streams() if hasattr(s, "_cache")] + + self._input_streams = list(RecursivelyEnumeratedSet([self], children)) + self._good_cache = [0 for c in self._input_streams] # the number of coefficients that have been substituted already def __getitem__(self, n): if n < self._approximate_order: @@ -1356,14 +1387,14 @@ def __getitem__(self, n): else: self._approximate_order += 1 del self._cache[-1] - self._subs_in_caches(self._F, v, val) + self._subs_in_caches(v, val) self._n += 1 if self._true_order: for k in range(self._n+1, n+1): v, val = self._compute() self._cache[-1] = val - self._subs_in_caches(self._F, v, val) + self._subs_in_caches(v, val) self._n += 1 self._uncomputed = True @@ -1375,23 +1406,27 @@ def __getitem__(self, n): self._cache.append(self._x[n]) return self._cache[-1] - def _subs_in_caches(self, s, var, val): - if hasattr(s, "_cache"): - if s._cache: - if s._is_sparse: - i = max(s._cache) - else: - i = -1 - c = s._cache[i] - if hasattr(c, "parent"): - if c.parent() is self._PFF: - num = c.numerator().subs({var: val}) - den = c.denominator().subs({var: val}) - s._cache[i] = self._base(num/den) - elif c.parent() is self._P: - s._cache[i] = self._base(c.subs({var: val})) - for t in s.input_streams(): - self._subs_in_caches(t, var, val) + def _subs_in_caches(self, var, val): + + def subs(cache, k): + c = cache[k] + if hasattr(c, "parent"): + if c.parent() is self._PFF: + num = c.numerator().subs({var: val}) + den = c.denominator().subs({var: val}) + cache[k] = self._base(num/den) + elif c.parent() is self._P: + cache[k] = self._base(c.subs({var: val})) + + for j, s in enumerate(self._input_streams): + m = len(s._cache) - self._good_cache[j] + if s._is_sparse: + for _, i in zip(range(m), reversed(s._cache)): + subs(s._cache, i) + else: + for i in range(-m, -1): + subs(s._cache, i) + self._good_cache[j] += m def _compute(self): while True: @@ -1478,28 +1513,6 @@ def is_uninitialized(self): self._initializing = False return result - def input_streams(self): - r""" - Return the list of streams which are used to compute the - coefficients of ``self``. - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized, Stream_function - sage: h = Stream_function(lambda n: n, False, 1) - sage: M = Stream_uninitialized(0) - sage: M.input_streams() - [] - sage: M._target = h - sage: [h[i] for i in range(5)] - [0, 1, 2, 3, 4] - sage: M.input_streams() - [] - """ - if self._target is not None: - return [self._target] - return [] - class Stream_unary(Stream_inexact): r""" From 4c4b9f3689ed7ac249770e2e5aabb06cac846e05 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 20 Nov 2023 19:41:15 +0100 Subject: [PATCH 016/507] walk through the closure in Stream_function to determine input streams, use 'is' for equality when finding input streams in Stream_uninitialized --- src/sage/data_structures/stream.py | 34 ++++++++++++++++++++---------- src/sage/rings/lazy_series.py | 8 +++---- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 2ca79e54082..05b7e88d6a2 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -104,7 +104,6 @@ from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.misc.cachefunc import cached_method from copy import copy -from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -977,8 +976,6 @@ class Stream_function(Stream_inexact): - ``is_sparse`` -- boolean; specifies whether the stream is sparse - ``approximate_order`` -- integer; a lower bound for the order of the stream - - ``input_streams`` -- optional, a list of streams that are - involved in the computation of the coefficients of ``self`` .. NOTE:: @@ -1004,7 +1001,7 @@ class Stream_function(Stream_inexact): 4 """ - def __init__(self, function, is_sparse, approximate_order, true_order=False, input_streams=[]): + def __init__(self, function, is_sparse, approximate_order, true_order=False): """ Initialize. @@ -1017,14 +1014,21 @@ def __init__(self, function, is_sparse, approximate_order, true_order=False, inp self.get_coefficient = function super().__init__(is_sparse, true_order) self._approximate_order = approximate_order - self._input_streams = input_streams def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``, as provided. """ - return self._input_streams + closure = self.get_coefficient.__closure__ + if closure is None: + return [] + l = [] + for cell in closure: + content = cell.cell_contents + if isinstance(content, Stream): + l.append(content) + return l def __hash__(self): """ @@ -1254,7 +1258,7 @@ class Stream_uninitialized(Stream): sage: one = Stream_exact([1]) sage: C = Stream_uninitialized(0) sage: C._target - sage: C._target = one + sage: C.define(one) sage: C[4] 0 """ @@ -1335,12 +1339,20 @@ def define_implicitly(self, F, initial_values, R): self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used self._n = self._approximate_order + len(self._cache) - 1 # the index of the last coefficient we know - def children(c): - return [s for s in c.input_streams() if hasattr(s, "_cache")] - - self._input_streams = list(RecursivelyEnumeratedSet([self], children)) + self._input_streams = list(self.input_streams_iterator()) self._good_cache = [0 for c in self._input_streams] # the number of coefficients that have been substituted already + def input_streams_iterator(self): + known = [self] + todo = [self] + while todo: + x = todo.pop() + yield x + for y in x.input_streams(): + if hasattr(y, "_cache") and not any(y is z for z in known): + todo.append(y) + known.append(y) + def __getitem__(self, n): if n < self._approximate_order: return ZZ.zero() diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 32cccb50fa4..ff3e4f5ccc8 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1676,7 +1676,7 @@ def define_implicitly(self, eqn, initial_values=None): sage: f = L.undefined(1) sage: f.define_implicitly(log(1+f) - ~(1 + f) + 1, []) sage: f - 0 + O(z^8) sage: f = L.undefined(0) sage: fp = f.derivative() @@ -3804,8 +3804,7 @@ def exp(self): # of the product are of the form sum_{k=1}^n a_k a_{n+1-k}. d_self_f = Stream_cauchy_mul_commutative(d_self, f._coeff_stream, False) int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), - False, 0, - input_streams=[d_self_f]) + False, 0) f._coeff_stream.define(int_d_self_f) return f @@ -3859,8 +3858,7 @@ def log(self): coeff_stream_inverse, P.is_sparse()) int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), - P.is_sparse(), 1, - input_streams=[d_self_quo_self]) + P.is_sparse(), 1) return P.element_class(P, int_d_self_quo_self) From 036f11fbd84fad2b742f4ac8d179ccb53c12301d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 20 Nov 2023 22:40:09 +0100 Subject: [PATCH 017/507] fix bad error message --- src/sage/data_structures/stream.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 05b7e88d6a2..0a6124286d4 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -103,8 +103,6 @@ from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.misc.cachefunc import cached_method -from copy import copy - lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1464,7 +1462,7 @@ def _compute(self): val = self._base.zero() else: if set(hc) != set([0, 1]): - raise ValueError(f"unable to determine a unique solution in degree {n}") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}") # if str(hc[1].lm()) != str(self._x[m]): # raise ValueError(f"the solutions to the coefficients must be computed in order") val = self._base(-hc[0].lc() / hc[1].lc()) From 1954a2754bc7e74f69379e4c97a4481efe92a0f5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 21 Nov 2023 10:05:05 +0100 Subject: [PATCH 018/507] store the list of variables belonging to each implicitly defined stream separately --- src/sage/data_structures/stream.py | 73 ++++++++++++++++++++++++++---- src/sage/rings/lazy_series.py | 4 +- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 0a6124286d4..ed4f6b3ce78 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1232,7 +1232,6 @@ def iterate_coefficients(self): n += 1 denom *= n - class Stream_uninitialized(Stream): r""" Coefficient stream for an uninitialized series. @@ -1304,13 +1303,34 @@ def input_streams(self): return [] def define(self, target): + r""" + Define ``self`` via ``self = target``. + + INPUT: + + - ``target`` -- a stream + + EXAMPLES:: + + """ self._target = target self._n = self._approximate_order - 1 # the largest index of a coefficient we know - # we only need this if target is not dense + # we only need this if target does not have a dense cache self._cache = list() self._iter = self.iterate_coefficients() def define_implicitly(self, F, initial_values, R): + r""" + Define ``self`` via ``F == 0``. + + INPUT: + + - ``F`` -- a stream + - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` + - ``R`` -- the coefficient ring + + EXAMPLES:: + """ assert self._target is None if initial_values is None: @@ -1329,9 +1349,11 @@ def define_implicitly(self, F, initial_values, R): self._F = F self._base = R from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing - self._P = InfinitePolynomialRing(self._base, names=('FESDUMMY',), - implementation='sparse') + # we use a silly variable name, because InfinitePolynomialRing is cached + self._P = InfinitePolynomialRing(self._base, names=("FESDUMMY",), + implementation='dense') self._x = self._P.gen() + self._variables = set() self._PFF = self._P.fraction_field() self._uncomputed = True self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used @@ -1341,6 +1363,12 @@ def define_implicitly(self, F, initial_values, R): self._good_cache = [0 for c in self._input_streams] # the number of coefficients that have been substituted already def input_streams_iterator(self): + r""" + Return the list of streams which have a cache and ``self`` + depends on. + + EXAMPLES:: + """ known = [self] todo = [self] while todo: @@ -1352,6 +1380,17 @@ def input_streams_iterator(self): known.append(y) def __getitem__(self, n): + """ + Return the ``n``-th coefficient of ``self``. + + INPUT: + + - ``n`` -- integer; the index + + EXAMPLES:: + + sage: + """ if n < self._approximate_order: return ZZ.zero() @@ -1413,11 +1452,24 @@ def __getitem__(self, n): return self._cache[n - self._approximate_order] return ZZ.zero() - self._cache.append(self._x[n]) - return self._cache[-1] + x = self._x[self._P._max + 1] + self._variables.add(x) + self._cache.append(x) + return x def _subs_in_caches(self, var, val): + r""" + Substitute ``val`` for ``var`` in the caches of the input + streams. + + INPUT: + - ``var``, a variable + - ``val``, the value that should replace the variable + + EXAMPLES:: + + """ def subs(cache, k): c = cache[k] if hasattr(c, "parent"): @@ -1439,6 +1491,9 @@ def subs(cache, k): self._good_cache[j] += m def _compute(self): + """ + Solve the next equations, until the next variable is determined. + """ while True: self._last_eq_n += 1 coeff = self._F[self._last_eq_n] @@ -1446,10 +1501,10 @@ def _compute(self): coeff = coeff.numerator() else: coeff = self._P(coeff) - V = coeff.variables() + V = list(self._variables.intersection([self._P(v) for v in coeff.variables()])) if len(V) > 1: - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") if not V: if coeff: @@ -1462,7 +1517,7 @@ def _compute(self): val = self._base.zero() else: if set(hc) != set([0, 1]): - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") # if str(hc[1].lm()) != str(self._x[m]): # raise ValueError(f"the solutions to the coefficients must be computed in order") val = self._base(-hc[0].lc() / hc[1].lc()) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index ff3e4f5ccc8..c70f3c7caf5 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1648,12 +1648,12 @@ def define_implicitly(self, eqn, initial_values=None): sage: F = L.undefined() sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) sage: F - + sage: F = L.undefined() sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) sage: F - + Laurent series examples:: From 616d3b3735a1b93b741caadfae4d8e56bda2a30a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 21 Nov 2023 17:34:45 +0100 Subject: [PATCH 019/507] solve systems of functional equations --- src/sage/data_structures/stream.py | 43 +++++++++++++++++++----------- src/sage/rings/lazy_series.py | 39 ++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index ed4f6b3ce78..4df9bded83b 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1359,25 +1359,33 @@ def define_implicitly(self, F, initial_values, R): self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used self._n = self._approximate_order + len(self._cache) - 1 # the index of the last coefficient we know - self._input_streams = list(self.input_streams_iterator()) - self._good_cache = [0 for c in self._input_streams] # the number of coefficients that have been substituted already - def input_streams_iterator(self): + @lazy_attribute + def _input_streams(self): r""" Return the list of streams which have a cache and ``self`` depends on. + All caches must have been created before this is called. + EXAMPLES:: """ known = [self] todo = [self] while todo: x = todo.pop() - yield x for y in x.input_streams(): if hasattr(y, "_cache") and not any(y is z for z in known): todo.append(y) known.append(y) + return known + + @lazy_attribute + def _good_cache(self): + r""" + The number of coefficients that have been substituted already. + """ + return [0 for c in self._input_streams] def __getitem__(self, n): """ @@ -1476,9 +1484,15 @@ def subs(cache, k): if c.parent() is self._PFF: num = c.numerator().subs({var: val}) den = c.denominator().subs({var: val}) - cache[k] = self._base(num/den) + new = num/den elif c.parent() is self._P: - cache[k] = self._base(c.subs({var: val})) + new = c.subs({var: val}) + else: + return + if new in self._base: + cache[k] = self._base(new) + else: + cache[k] = new for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] @@ -1486,7 +1500,7 @@ def subs(cache, k): for _, i in zip(range(m), reversed(s._cache)): subs(s._cache, i) else: - for i in range(-m, -1): + for i in range(-m, 0): subs(s._cache, i) self._good_cache[j] += m @@ -1511,16 +1525,15 @@ def _compute(self): raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") continue - # single variable to solve for - hc = coeff.homogeneous_components() - if len(hc) == 1: + c = coeff.polynomial() + v = c.parent()(V[0].polynomial()) + d = c.degree(v) + if d == 1: + val = self._PFF(- c.coefficient({v: 0}) / c.coefficient({v: 1})) + elif c.is_monomial() and c.coefficient({v: d}) in self._base: val = self._base.zero() else: - if set(hc) != set([0, 1]): - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") -# if str(hc[1].lm()) != str(self._x[m]): -# raise ValueError(f"the solutions to the coefficients must be computed in order") - val = self._base(-hc[0].lc() / hc[1].lc()) + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") return V[0], val diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index c70f3c7caf5..60733e8c495 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1532,7 +1532,9 @@ def define(self, s): sage: f 1 + 3*x + 16*x^2 + 87*x^3 + 607*x^4 + 4518*x^5 + 30549*x^6 + O(x^7) """ - if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: + if (not isinstance(self._coeff_stream, Stream_uninitialized) + or self._coeff_stream._target is not None + or self._coeff_stream._F is not None): raise ValueError("series already defined") if not isinstance(s, LazyModuleElement): @@ -1615,9 +1617,9 @@ def define_implicitly(self, eqn, initial_values=None): sage: R[0] 0 sage: R[1] - q*y/(-q*y + 1) + (-q*y)/(q*y - 1) sage: R[2] - (-q^3*y^2 - q^2*y)/(-q^3*y^2 + q^2*y + q*y - 1) + (q^3*y^2 + q^2*y)/(q^3*y^2 - q^2*y - q*y + 1) sage: R[3].factor() (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) @@ -1692,8 +1694,37 @@ def define_implicitly(self, eqn, initial_values=None): 0 sage: f[1] 0 + + Some systems of two coupled functional equations:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: FA = A^2 + B - 2 - z*B + sage: FB = B^2 - A + sage: A.define_implicitly(FA, [1]) + sage: B.define_implicitly(FB, [1]) + sage: A^2 + B - 2 - z*B + O(z^7) + sage: B^2 - A + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: FA = A^2 + B^2 - 2 - z*B + sage: FB = B^3 + 2*A^3 - 3 - z*(A + B) + sage: A.define_implicitly(FA, [1]) + sage: B.define_implicitly(FB, [1]) + sage: A^2 + B^2 - 2 - z*B + O(z^7) + sage: B^3 + 2*A^3 - 3 - z*(A + B) + O(z^7) + """ - if not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None: + if (not isinstance(self._coeff_stream, Stream_uninitialized) + or self._coeff_stream._target is not None + or self._coeff_stream._F is not None): raise ValueError("series already defined") P = self.parent() From 5d0c3a601ffa9957c2a41b6371c87472205b865d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 22 Nov 2023 07:34:02 +0100 Subject: [PATCH 020/507] WIP: use no more new variables than necessary --- src/sage/data_structures/stream.py | 22 +++++++++++++++++++--- src/sage/rings/lazy_series.py | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 4df9bded83b..6a3fdae27bc 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1232,6 +1232,21 @@ def iterate_coefficients(self): n += 1 denom *= n +from collections import defaultdict +STREAM_UNINITIALIZED_VARIABLES = defaultdict(set) +def get_variable(P): + r""" + Return the first variable with index not in + ``STREAM_UNINITIALIZED_VARIABLES``. + + """ + vars = STREAM_UNINITIALIZED_VARIABLES[P] + for i in range(P._max+2): + v = P.gen()[i] + if v not in vars: + vars.add(v) + return v + class Stream_uninitialized(Stream): r""" Coefficient stream for an uninitialized series. @@ -1352,9 +1367,9 @@ def define_implicitly(self, F, initial_values, R): # we use a silly variable name, because InfinitePolynomialRing is cached self._P = InfinitePolynomialRing(self._base, names=("FESDUMMY",), implementation='dense') - self._x = self._P.gen() - self._variables = set() self._PFF = self._P.fraction_field() + self._variables = set() # variables used for this stream + self._uncomputed = True self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used self._n = self._approximate_order + len(self._cache) - 1 # the index of the last coefficient we know @@ -1460,7 +1475,7 @@ def __getitem__(self, n): return self._cache[n - self._approximate_order] return ZZ.zero() - x = self._x[self._P._max + 1] + x = get_variable(self._P) self._variables.add(x) self._cache.append(x) return x @@ -1503,6 +1518,7 @@ def subs(cache, k): for i in range(-m, 0): subs(s._cache, i) self._good_cache[j] += m + STREAM_UNINITIALIZED_VARIABLES[self._P].remove(var) def _compute(self): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 60733e8c495..ac4553dce2c 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1650,12 +1650,12 @@ def define_implicitly(self, eqn, initial_values=None): sage: F = L.undefined() sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) sage: F - + sage: F = L.undefined() sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) sage: F - + Laurent series examples:: From fcb3bf9829ac19856df7b0130721934b07827867 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 22 Nov 2023 12:17:26 +0100 Subject: [PATCH 021/507] fix conversion issue --- src/sage/data_structures/stream.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 6a3fdae27bc..1c75521bf54 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -103,6 +103,9 @@ from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.misc.cachefunc import cached_method +from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense +from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing +from collections import defaultdict lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1232,7 +1235,6 @@ def iterate_coefficients(self): n += 1 denom *= n -from collections import defaultdict STREAM_UNINITIALIZED_VARIABLES = defaultdict(set) def get_variable(P): r""" @@ -1363,7 +1365,6 @@ def define_implicitly(self, F, initial_values, R): self._F = F self._base = R - from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing # we use a silly variable name, because InfinitePolynomialRing is cached self._P = InfinitePolynomialRing(self._base, names=("FESDUMMY",), implementation='dense') @@ -1531,7 +1532,8 @@ def _compute(self): coeff = coeff.numerator() else: coeff = self._P(coeff) - V = list(self._variables.intersection([self._P(v) for v in coeff.variables()])) + V = self._variables.intersection(InfinitePolynomial_dense(self._P, v) + for v in coeff.variables()) if len(V) > 1: raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") @@ -1541,8 +1543,9 @@ def _compute(self): raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") continue + var = V.pop() c = coeff.polynomial() - v = c.parent()(V[0].polynomial()) + v = c.parent()(var.polynomial()) d = c.degree(v) if d == 1: val = self._PFF(- c.coefficient({v: 0}) / c.coefficient({v: 1})) @@ -1551,7 +1554,7 @@ def _compute(self): else: raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") - return V[0], val + return var, val def iterate_coefficients(self): """ From 378e28d4d93cc27bea109794224853843bc1d55c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 23 Nov 2023 14:15:56 +0100 Subject: [PATCH 022/507] better substitution, better error messages --- src/sage/data_structures/stream.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 1c75521bf54..370c0ea9987 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1494,14 +1494,19 @@ def _subs_in_caches(self, var, val): EXAMPLES:: """ + var_p = var.polynomial() def subs(cache, k): c = cache[k] if hasattr(c, "parent"): if c.parent() is self._PFF: - num = c.numerator().subs({var: val}) - den = c.denominator().subs({var: val}) + num = c.numerator() + if var_p in num.variables(): + num = num.subs({var: val}) + den = c.denominator() + if var_p in den.variables(): + num = den.subs({var: val}) new = num/den - elif c.parent() is self._P: + elif c.parent() is self._P and var_p in c.variables(): new = c.subs({var: val}) else: return @@ -1536,10 +1541,10 @@ def _compute(self): for v in coeff.variables()) if len(V) > 1: - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the variables are {V}, the equation is {coeff} == 0") if not V: - if coeff: + if coeff in self._base and coeff: raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") continue @@ -1552,7 +1557,7 @@ def _compute(self): elif c.is_monomial() and c.coefficient({v: d}) in self._base: val = self._base.zero() else: - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the equation is {coeff} == 0") + raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the variable is {var}, the equation is {coeff} == 0") return var, val From e91fd918db0a922e465cbef6ef077c33cee46e18 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 24 Nov 2023 08:45:57 +0100 Subject: [PATCH 023/507] switch to ZZ.sum, add more failing doctests --- src/sage/data_structures/stream.py | 36 +++++++++++++++--------------- src/sage/rings/lazy_series.py | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 370c0ea9987..fb51931a6ee 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -2209,10 +2209,10 @@ def get_coefficient(self, n): sage: [h.get_coefficient(i) for i in range(10)] [0, 0, 1, 6, 20, 50, 105, 196, 336, 540] """ - return sum(l * self._right[n - k] - for k in range(self._left._approximate_order, - n - self._right._approximate_order + 1) - if (l := self._left[k])) + return ZZ.sum(l * self._right[n - k] + for k in range(self._left._approximate_order, + n - self._right._approximate_order + 1) + if (l := self._left[k])) def is_nonzero(self): r""" @@ -2313,10 +2313,10 @@ def get_coefficient(self, n): sage: [h[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] """ - return sum(l * self._right[n//k] for k in divisors(n) - if (k >= self._left._approximate_order - and n // k >= self._right._approximate_order - and (l := self._left[k]))) + return ZZ.sum(l * self._right[n//k] for k in divisors(n) + if (k >= self._left._approximate_order + and n // k >= self._right._approximate_order + and (l := self._left[k]))) class Stream_cauchy_compose(Stream_binary): @@ -2416,23 +2416,23 @@ def get_coefficient(self, n): fv = self._left._approximate_order gv = self._right._approximate_order if n < 0: - return sum(l * self._neg_powers[-k][n] - for k in range(fv, n // gv + 1) - if (l := self._left[k])) + return ZZ.sum(l * self._neg_powers[-k][n] + for k in range(fv, n // gv + 1) + if (l := self._left[k])) # n > 0 while len(self._pos_powers) <= n // gv: # TODO: possibly we always want a dense cache here? self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right, self._is_sparse)) - ret = sum(l * self._neg_powers[-k][n] for k in range(fv, 0) - if (l := self._left[k])) + ret = ZZ.sum(l * self._neg_powers[-k][n] for k in range(fv, 0) + if (l := self._left[k])) if not n: ret += self._left[0] - return ret + sum(l * self._pos_powers[k][n] for k in range(1, n // gv + 1) - if (l := self._left[k])) + return ret + ZZ.sum(l * self._pos_powers[k][n] for k in range(1, n // gv + 1) + if (l := self._left[k])) class Stream_plethysm(Stream_binary): @@ -3318,9 +3318,9 @@ def get_coefficient(self, n): if n == 1: return self._ainv # TODO: isn't self[k] * l and l * self[k] the same here? - c = sum(self[k] * l for k in divisors(n) - if (k < n - and (l := self._series[n // k]))) + c = ZZ.sum(self[k] * l for k in divisors(n) + if (k < n + and (l := self._series[n // k]))) return -c * self._ainv diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index ac4553dce2c..1269b9ca729 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1721,6 +1721,29 @@ def define_implicitly(self, eqn, initial_values=None): sage: B^3 + 2*A^3 - 3 - z*(A + B) O(z^7) + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: FA = (A^2 + B^2)*z^4 + sage: FB = A*B*z^3 + sage: FC = (A + B + C)*z^4 + sage: A.define_implicitly(FA, [0,0,0]) + sage: B.define_implicitly(FB, [0,0]) + sage: C.define_implicitly(FC, [0,0]) + sage: B[2] + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: D = L.undefined() + sage: A.define_implicitly(C^2 + D^2, [0,0,0]) + sage: B.define_implicitly(A + B + C + D, [0,0]) + sage: C.define_implicitly(A*D, [0,0]) + sage: D.define_implicitly(A + B + C + D, [0,0]) + sage: B[2] + """ if (not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None From 0f449ff5b7255bea88546b047673f85684bfa660 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 25 Nov 2023 21:15:43 +0100 Subject: [PATCH 024/507] more failing doctests --- src/sage/data_structures/stream.py | 6 ++++-- src/sage/rings/lazy_series.py | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index fb51931a6ee..2c31d86b3ef 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1544,8 +1544,10 @@ def _compute(self): raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the variables are {V}, the equation is {coeff} == 0") if not V: - if coeff in self._base and coeff: - raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") + if coeff: + if coeff in self._base: + raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") + # should do some substitution coeff = 0, but unclear which variable to extract continue var = V.pop() diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 1269b9ca729..8be6e02613a 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1725,15 +1725,15 @@ def define_implicitly(self, eqn, initial_values=None): sage: A = L.undefined() sage: B = L.undefined() sage: C = L.undefined() - sage: FA = (A^2 + B^2)*z^4 - sage: FB = A*B*z^3 - sage: FC = (A + B + C)*z^4 + sage: FA = (A^2 + B^2)*z^2 + sage: FB = A*B*z + sage: FC = (A + B + C)*z^2 sage: A.define_implicitly(FA, [0,0,0]) sage: B.define_implicitly(FB, [0,0]) sage: C.define_implicitly(FC, [0,0]) sage: B[2] - sage: L. = LazyPowerSeriesRing(QQ) + sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() sage: B = L.undefined() sage: C = L.undefined() @@ -1744,6 +1744,15 @@ def define_implicitly(self, eqn, initial_values=None): sage: D.define_implicitly(A + B + C + D, [0,0]) sage: B[2] + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: A.define_implicitly(B - C - 1) + sage: B.define_implicitly(B*z + 2*C + 1) + sage: C.define_implicitly(A + 2*C + 1) + sage: A[0] + """ if (not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None From a046fd0cd10099fd62cef398d4e4ec0acc8116b0 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Jan 2024 12:10:53 +0100 Subject: [PATCH 025/507] initial commit to solve also systems of functional equations --- src/sage/data_structures/stream.py | 191 +++++++++++++------------ src/sage/rings/lazy_series.py | 219 +--------------------------- src/sage/rings/lazy_series_ring.py | 222 +++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+), 313 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 2c31d86b3ef..502a3395aa8 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1241,6 +1241,7 @@ def get_variable(P): Return the first variable with index not in ``STREAM_UNINITIALIZED_VARIABLES``. + We need a different dictionary for each base ring. """ vars = STREAM_UNINITIALIZED_VARIABLES[P] for i in range(P._max+2): @@ -1287,7 +1288,7 @@ def __init__(self, approximate_order, true_order=False): sage: TestSuite(C).run(skip="_test_pickling") """ self._target = None - self._F = None + self._eqs = None if approximate_order is None: raise ValueError("the valuation must be specified for undefined series") super().__init__(true_order) @@ -1315,8 +1316,8 @@ def input_streams(self): """ if self._target is not None: return [self._target] - if self._F is not None: - return [self._F] + if self._eqs is not None: + return self._eqs return [] def define(self, target): @@ -1336,23 +1337,20 @@ def define(self, target): self._cache = list() self._iter = self.iterate_coefficients() - def define_implicitly(self, F, initial_values, R): + def define_implicitly(self, series, initial_values, equations, last_equation_used, R): r""" - Define ``self`` via ``F == 0``. + Define ``self`` via ``equations == 0``. INPUT: - - ``F`` -- a stream + - ``series`` -- a list of series + - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - ``R`` -- the coefficient ring - EXAMPLES:: """ assert self._target is None - if initial_values is None: - initial_values = [] - for i, val in enumerate(initial_values): if val: self._approximate_order += i @@ -1363,18 +1361,15 @@ def define_implicitly(self, F, initial_values, R): self._approximate_order += len(initial_values) self._cache = [] - self._F = F self._base = R # we use a silly variable name, because InfinitePolynomialRing is cached self._P = InfinitePolynomialRing(self._base, names=("FESDUMMY",), implementation='dense') self._PFF = self._P.fraction_field() - self._variables = set() # variables used for this stream - self._uncomputed = True - self._last_eq_n = self._F._approximate_order - 1 # the index of the last equation we used - self._n = self._approximate_order + len(self._cache) - 1 # the index of the last coefficient we know - + self._eqs = equations + self._last_eqs_n = last_equation_used # the indices of the last equations we used + self._series = series @lazy_attribute def _input_streams(self): @@ -1382,9 +1377,11 @@ def _input_streams(self): Return the list of streams which have a cache and ``self`` depends on. - All caches must have been created before this is called. + ``self`` is the first stream in this list. - EXAMPLES:: + All caches must have been created before this is called. + Does this mean that this should only be called after the + first invocation of `_compute`? """ known = [self] todo = [self] @@ -1399,7 +1396,11 @@ def _input_streams(self): @lazy_attribute def _good_cache(self): r""" - The number of coefficients that have been substituted already. + The number of coefficients in each input stream - in the same + order - that are free of undetermined coefficients. + + This is used in :meth:`_subs_in_caches` to only substitute + items that may contain undetermined coefficients. """ return [0 for c in self._input_streams] @@ -1439,64 +1440,40 @@ def __getitem__(self, n): return ZZ.zero() -# if target is dense, we do not need to duplicate its cache -# for i in range(self._n+1, n): -# self._target[i] -# self._n = n -# return self._target[n] - # define_implicitly - if self._n >= n: + if self._good_cache[0] >= n - self._approximate_order + 1: return self._cache[n - self._approximate_order] if self._uncomputed: - self._uncomputed = False - while not self._true_order and n >= self._approximate_order: - for k in range(self._n+1, n+1): - v, val = self._compute() - if val: - self._true_order = True - self._cache[-1] = val - else: - self._approximate_order += 1 - del self._cache[-1] - self._subs_in_caches(v, val) - self._n += 1 - - if self._true_order: - for k in range(self._n+1, n+1): - v, val = self._compute() - self._cache[-1] = val - self._subs_in_caches(v, val) - self._n += 1 - self._uncomputed = True - - if len(self._cache) == n - self._approximate_order + 1: - if n >= self._approximate_order: - return self._cache[n - self._approximate_order] - return ZZ.zero() + for f in self._series: + f._uncomputed = False + while self._good_cache[0] < n - self._approximate_order + 1: + self._compute() + for f in self._series: + f._uncomputed = True + + if len(self._cache) >= n - self._approximate_order + 1: + return self._cache[n - self._approximate_order] x = get_variable(self._P) - self._variables.add(x) self._cache.append(x) return x def _subs_in_caches(self, var, val): r""" Substitute ``val`` for ``var`` in the caches of the input - streams. + streams and update ``self._good_cache``. INPUT: - ``var``, a variable - ``val``, the value that should replace the variable - - EXAMPLES:: - """ var_p = var.polynomial() def subs(cache, k): c = cache[k] + # TODO: we may want to insist that all coefficients of a + # stream have a parent if hasattr(c, "parent"): if c.parent() is self._PFF: num = c.numerator() @@ -1509,59 +1486,81 @@ def subs(cache, k): elif c.parent() is self._P and var_p in c.variables(): new = c.subs({var: val}) else: - return + return c in self._base if new in self._base: cache[k] = self._base(new) + return True else: cache[k] = new + return False for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] + good = m # last index of cache element still containing variables if s._is_sparse: - for _, i in zip(range(m), reversed(s._cache)): - subs(s._cache, i) + for idx, i in zip(range(m), reversed(s._cache)): + if not subs(s._cache, i): + good = m - idx - 1 else: for i in range(-m, 0): - subs(s._cache, i) - self._good_cache[j] += m + if not subs(s._cache, i): + good = -i - 1 + self._good_cache[j] += good STREAM_UNINITIALIZED_VARIABLES[self._P].remove(var) def _compute(self): """ Solve the next equations, until the next variable is determined. """ - while True: - self._last_eq_n += 1 - coeff = self._F[self._last_eq_n] - if coeff.parent() is self._PFF: - coeff = coeff.numerator() - else: - coeff = self._P(coeff) - V = self._variables.intersection(InfinitePolynomial_dense(self._P, v) - for v in coeff.variables()) - - if len(V) > 1: - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the variables are {V}, the equation is {coeff} == 0") - - if not V: - if coeff: - if coeff in self._base: - raise ValueError(f"no solution in degree {self._last_eq_n} as {coeff} != 0") - # should do some substitution coeff = 0, but unclear which variable to extract - continue - - var = V.pop() - c = coeff.polynomial() - v = c.parent()(var.polynomial()) - d = c.degree(v) - if d == 1: - val = self._PFF(- c.coefficient({v: 0}) / c.coefficient({v: 1})) - elif c.is_monomial() and c.coefficient({v: d}) in self._base: - val = self._base.zero() - else: - raise ValueError(f"unable to determine a unique solution in degree {self._last_eq_n}, the variable is {var}, the equation is {coeff} == 0") - - return var, val + # determine the next linear equations + coeffs = [] + for i, eq in enumerate(self._eqs): + while True: + self._last_eqs_n[i] += 1 + coeff = eq[self._last_eqs_n[i]] + if coeff.parent() is self._PFF: + coeff = coeff.numerator() + else: + coeff = self._P(coeff) + V = coeff.variables() + if not V: + if coeff: + raise ValueError(f"no solution in degree {self._last_eqs_n} as {coeff} != 0") + else: + continue + if coeff.degree() <= 1: + coeffs.append(coeff) + else: + self._last_eqs_n[i] -= 1 + break + if not coeffs: + raise ValueError("No linear equations") + + # solve + from sage.structure.sequence import Sequence + coeffs = Sequence([coeff.polynomial() for coeff in coeffs]) + m1, v1 = coeffs.coefficient_matrix() + m = m1.matrix_from_columns([i for i, (c,) in enumerate(v1) + if c.degree() == 1]) + b = -m1.matrix_from_columns([i for i, (c,) in enumerate(v1) + if c.degree() == 0]) + + if not b: + from sage.modules.free_module_element import zero_vector + b = zero_vector(m.nrows()) + x = m.solve_right(b) + k = m.right_kernel_matrix() + + # substitute + bad = True + for i, ((c,), (y,)) in enumerate(zip(v1, x)): + if k.column(i).is_zero(): + var = self._P(c) + val = self._base(y) + self._subs_in_caches(var, val) + bad = False + if bad: + raise ValueError("Could not determine any coefficients") def iterate_coefficients(self): """ @@ -1604,14 +1603,14 @@ def is_uninitialized(self): sage: T._coeff_stream.is_uninitialized() True """ - if self._target is None and self._F is None: + if self._target is None and self._eqs is None: return True if self._initializing: return False # We implement semaphore-like behavior for coupled (undefined) series self._initializing = True if self._target is None: - result = self._F.is_uninitialized() + result = False else: result = self._target.is_uninitialized() self._initializing = False @@ -3850,6 +3849,8 @@ class Stream_derivative(Stream_unary): """ Operator for taking derivatives of a non-exact stream. + Instances of this class share the cache with its input stream. + INPUT: - ``series`` -- a :class:`Stream` diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 8be6e02613a..fd8de2ac50f 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1534,7 +1534,7 @@ def define(self, s): """ if (not isinstance(self._coeff_stream, Stream_uninitialized) or self._coeff_stream._target is not None - or self._coeff_stream._F is not None): + or self._coeff_stream._eqs is not None): raise ValueError("series already defined") if not isinstance(s, LazyModuleElement): @@ -1551,223 +1551,6 @@ def define(self, s): # an alias for compatibility with padics set = define - def define_implicitly(self, eqn, initial_values=None): - r""" - Define ``self`` as the series that solves the functional - equation ``eqn == 0`` with ``initial_values``. - - EXAMPLES:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(0) - sage: F = diff(f, 2) - sage: f.define_implicitly(F + f, [1, 0]) - sage: f - 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) - sage: cos(z) - 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) - sage: F - -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) - - sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(0) - sage: f.define_implicitly(2*z*f(z^3) + z*f^3 - 3*f + 3) - sage: f - 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) - - From Exercise 6.63b in [EnumComb2]_:: - - sage: g = L.undefined() - sage: z1 = z*diff(g, z) - sage: z2 = z1 + z^2 * diff(g, z, 2) - sage: z3 = z1 + 3 * z^2 * diff(g, z, 2) + z^3 * diff(g, z, 3) - sage: e1 = g^2 * z3 - 15*g*z1*z2 + 30*z1^3 - sage: e2 = g * z2 - 3 * z1^2 - sage: e3 = g * z2 - 3 * z1^2 - sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 - sage: g.define_implicitly(e, [1, 2]) - - sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol - 1 + 2*z + 2*z^4 + O(z^7) - sage: all(g[i] == sol[i] for i in range(50)) - True - - Some more examples over different rings:: - - sage: L. = LazyPowerSeriesRing(SR) - sage: G = L.undefined(0) - sage: G.define_implicitly(diff(G) - exp(-G(-z)), [ln(2)]) - sage: G - log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) - - sage: L. = LazyPowerSeriesRing(RR) - sage: G = L.undefined(0) - sage: G.define_implicitly(diff(G) - exp(-G(-z)), [log(2)]) - sage: G - 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 - - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) - - We solve the recurrence relation in (3.12) of Prellberg and Brak - :doi:`10.1007/BF02183685`:: - - sage: q, y = QQ['q,y'].fraction_field().gens() - sage: L. = LazyPowerSeriesRing(q.parent()) - sage: R = L.undefined() - sage: R.define_implicitly((1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q, [0]) - sage: R[0] - 0 - sage: R[1] - (-q*y)/(q*y - 1) - sage: R[2] - (q^3*y^2 + q^2*y)/(q^3*y^2 - q^2*y - q*y + 1) - sage: R[3].factor() - (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 - * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) - - sage: Rp = L.undefined(1) - sage: Rp.define_implicitly((y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp) - sage: all(R[n] == Rp[n] for n in range(7)) - True - - Another example:: - - sage: L. = LazyPowerSeriesRing(QQ["x,y,f1,f2"].fraction_field()) - sage: L.base_ring().inject_variables() - Defining x, y, f1, f2 - sage: F = L.undefined() - sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1, f2]) - sage: F - f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) - + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) - + ... + O(z^8) - sage: sol = 1/(x-y)*((2*f2-y*f1)*(exp(x*z)-1)/x - (2*f2-x*f1)*(exp(y*z)-1)/y) - sage: F - sol - O(z^7) - - We need to specify the initial values for the degree 1 and 2 - components to get a unique solution in the previous example:: - - sage: F = L.undefined() - sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)) - sage: F - - - sage: F = L.undefined() - sage: F.define_implicitly(F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z), [0, f1]) - sage: F - - - Laurent series examples:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = L.undefined(-1) - sage: f.define_implicitly(2+z*f(z^2) - f, [5]) - sage: f - 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) - sage: 2 + z*f(z^2) - f - O(z^6) - - sage: g = L.undefined(-2) - sage: g.define_implicitly(2+z*g(z^2) - g, [5]) - sage: g - - - TESTS:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: f = L.undefined(1) - sage: f.define_implicitly(log(1+f) - ~(1 + f) + 1, []) - sage: f - O(z^8) - - sage: f = L.undefined(0) - sage: fp = f.derivative() - sage: g = L(lambda n: 0 if n < 10 else 1, 0) - sage: f.define_implicitly(f.derivative() * g + f) - sage: f[0] - 0 - sage: fp[0] - 0 - sage: fp[1] - 0 - sage: fp[2] - 0 - sage: f[1] - 0 - - Some systems of two coupled functional equations:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: FA = A^2 + B - 2 - z*B - sage: FB = B^2 - A - sage: A.define_implicitly(FA, [1]) - sage: B.define_implicitly(FB, [1]) - sage: A^2 + B - 2 - z*B - O(z^7) - sage: B^2 - A - O(z^7) - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: FA = A^2 + B^2 - 2 - z*B - sage: FB = B^3 + 2*A^3 - 3 - z*(A + B) - sage: A.define_implicitly(FA, [1]) - sage: B.define_implicitly(FB, [1]) - sage: A^2 + B^2 - 2 - z*B - O(z^7) - sage: B^3 + 2*A^3 - 3 - z*(A + B) - O(z^7) - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: C = L.undefined() - sage: FA = (A^2 + B^2)*z^2 - sage: FB = A*B*z - sage: FC = (A + B + C)*z^2 - sage: A.define_implicitly(FA, [0,0,0]) - sage: B.define_implicitly(FB, [0,0]) - sage: C.define_implicitly(FC, [0,0]) - sage: B[2] - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: C = L.undefined() - sage: D = L.undefined() - sage: A.define_implicitly(C^2 + D^2, [0,0,0]) - sage: B.define_implicitly(A + B + C + D, [0,0]) - sage: C.define_implicitly(A*D, [0,0]) - sage: D.define_implicitly(A + B + C + D, [0,0]) - sage: B[2] - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: C = L.undefined() - sage: A.define_implicitly(B - C - 1) - sage: B.define_implicitly(B*z + 2*C + 1) - sage: C.define_implicitly(A + 2*C + 1) - sage: A[0] - - """ - if (not isinstance(self._coeff_stream, Stream_uninitialized) - or self._coeff_stream._target is not None - or self._coeff_stream._F is not None): - raise ValueError("series already defined") - - P = self.parent() - F = P(eqn)._coeff_stream - R = P.base_ring() - if initial_values is None: - initial_values = [] - else: - initial_values = [R(val) for val in initial_values] - self._coeff_stream.define_implicitly(F, initial_values, R) - def _repr_(self): r""" Return a string representation of ``self``. diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index c3059bf936a..1e574c8f374 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -659,6 +659,228 @@ def undefined(self, valuation=None): unknown = undefined + def define_implicitly(self, series, equations): + r""" + Define series by solving functional equations. + + INPUT: + + - ``series`` -- list of undefined series or pairs each + consisting of a series and its initial values + + - ``equations`` -- list of equations defining the series + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: F = diff(f, 2) + sage: L.define_implicitly([(f, [1, 0])], [F + f]) + sage: f + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: cos(z) + 1 - 1/2*z^2 + 1/24*z^4 - 1/720*z^6 + O(z^7) + sage: F + -1 + 1/2*z^2 - 1/24*z^4 + 1/720*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(0) + sage: L.define_implicitly([f], [2*z*f(z^3) + z*f^3 - 3*f + 3]) + sage: f + 1 + z + z^2 + 2*z^3 + 5*z^4 + 11*z^5 + 28*z^6 + O(z^7) + + From Exercise 6.63b in [EnumComb2]_:: + + sage: g = L.undefined() + sage: z1 = z*diff(g, z) + sage: z2 = z1 + z^2 * diff(g, z, 2) + sage: z3 = z1 + 3 * z^2 * diff(g, z, 2) + z^3 * diff(g, z, 3) + sage: e1 = g^2 * z3 - 15*g*z1*z2 + 30*z1^3 + sage: e2 = g * z2 - 3 * z1^2 + sage: e3 = g * z2 - 3 * z1^2 + sage: e = e1^2 + 32 * e2^3 - g^10 * e3^2 + sage: L.define_implicitly([(g, [1, 2])], [e]) + + sage: sol = L(lambda n: 1 if not n else (2 if is_square(n) else 0)); sol + 1 + 2*z + 2*z^4 + O(z^7) + sage: all(g[i] == sol[i] for i in range(50)) + True + + Some more examples over different rings:: + + sage: L. = LazyPowerSeriesRing(SR) + sage: G = L.undefined(0) + sage: L.define_implicitly([(G, [ln(2)])], [diff(G) - exp(-G(-z))]) + sage: G + log(2) + z + 1/2*z^2 + (-1/12*z^4) + 1/45*z^6 + O(z^7) + + sage: L. = LazyPowerSeriesRing(RR) + sage: G = L.undefined(0) + sage: L.define_implicitly([(G, [log(2)])], [diff(G) - exp(-G(-z))]) + sage: G + 0.693147180559945 + 1.00000000000000*z + 0.500000000000000*z^2 + - 0.0833333333333333*z^4 + 0.0222222222222222*z^6 + O(1.00000000000000*z^7) + + We solve the recurrence relation in (3.12) of Prellberg and Brak + :doi:`10.1007/BF02183685`:: + + sage: q, y = QQ['q,y'].fraction_field().gens() + sage: L. = LazyPowerSeriesRing(q.parent()) + sage: R = L.undefined() + sage: L.define_implicitly([(R, [0])], [(1-q*x)*R - (y*q*x+y)*R(q*x) - q*x*R*R(q*x) - x*y*q]) + sage: R[0] + 0 + sage: R[1] + (-q*y)/(q*y - 1) + sage: R[2] + (q^3*y^2 + q^2*y)/(q^3*y^2 - q^2*y - q*y + 1) + sage: R[3].factor() + (-1) * y * q^3 * (q*y - 1)^-2 * (q^2*y - 1)^-1 * (q^3*y - 1)^-1 + * (q^4*y^3 + q^3*y^2 + q^2*y^2 - q^2*y - q*y - 1) + + sage: Rp = L.undefined(1) + sage: L.define_implicitly([Rp], [(y*q*x+y)*Rp(q*x) + q*x*Rp*Rp(q*x) + x*y*q - (1-q*x)*Rp]) + sage: all(R[n] == Rp[n] for n in range(7)) + True + + Another example:: + + sage: L. = LazyPowerSeriesRing(QQ["x,y,f1,f2"].fraction_field()) + sage: L.base_ring().inject_variables() + Defining x, y, f1, f2 + sage: F = L.undefined() + sage: L.define_implicitly([(F, [0, f1, f2])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) + sage: F + f1*z + f2*z^2 + ((-1/6*x*y*f1+1/3*x*f2+1/3*y*f2)*z^3) + + ((-1/24*x^2*y*f1-1/24*x*y^2*f1+1/12*x^2*f2+1/12*x*y*f2+1/12*y^2*f2)*z^4) + + ... + O(z^8) + sage: sol = 1/(x-y)*((2*f2-y*f1)*(exp(x*z)-1)/x - (2*f2-x*f1)*(exp(y*z)-1)/y) + sage: F - sol + O(z^7) + + We need to specify the initial values for the degree 1 and 2 + components to get a unique solution in the previous example:: + + sage: F = L.undefined() + sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) + sage: F + + + sage: F = L.undefined() + sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) + sage: F + + + Laurent series examples:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = L.undefined(-1) + sage: L.define_implicitly([(f, [5])], [2+z*f(z^2) - f]) + sage: f + 5*z^-1 + 2 + 2*z + 2*z^3 + O(z^6) + sage: 2 + z*f(z^2) - f + O(z^6) + + sage: g = L.undefined(-2) + sage: L.define_implicitly([(g, [5])], [2+z*g(z^2) - g]) + sage: g + + + TESTS:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: f = L.undefined(1) + sage: L.define_implicitly([f], [log(1+f) - ~(1 + f) + 1]) + sage: f + O(z^8) + + sage: f = L.undefined(0) + sage: fp = f.derivative() + sage: g = L(lambda n: 0 if n < 10 else 1, 0) + sage: L.define_implicitly([f], [f.derivative() * g + f]) + sage: f[0] + 0 + sage: fp[0] + 0 + sage: fp[1] + 0 + sage: fp[2] + 0 + sage: f[1] + 0 + + Some systems of two coupled functional equations:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: L.define_implicitly([A, B], [A - B, A + B + 2]) + sage: A + -1 + O(z^7) + sage: B + -1 + O(z^7) + + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: FA = A^2 + B - 2 - z*B + sage: FB = B^2 - A + sage: L.define_implicitly([(A, [1]), (B, [1])], [FA, FB]) + sage: A^2 + B - 2 - z*B + O(z^7) + sage: B^2 - A + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: FA = A^2 + B^2 - 2 - z*B + sage: FB = B^3 + 2*A^3 - 3 - z*(A + B) + sage: L.define_implicitly([(A, [1]), (B, [1])], [FA, FB]) + sage: A^2 + B^2 - 2 - z*B + O(z^7) + sage: B^3 + 2*A^3 - 3 - z*(A + B) + O(z^7) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: FA = (A^2 + B^2)*z^2 + sage: FB = A*B*z + sage: FC = (A + B + C)*z^2 + sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0])], [FA, FB, FC]) + sage: B[2] + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: D = L.undefined() + sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) + sage: B[2] + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: C = L.undefined() + sage: L.define_implicitly([A, B, C], [B - C - 1, B*z + 2*C + 1, A + 2*C + 1]) + sage: A + 2*C + 1 + O(z^7) + """ + s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) + else a._coeff_stream + for a in series] + ics = [a[1] if isinstance(a, (tuple, list)) + else [] + for a in series] + # common state for all series + eqs = [eq._coeff_stream for eq in equations] + last_eq_used = [eq._approximate_order - 1 for eq in eqs] + for f, ic in zip(s, ics): + f.define_implicitly(s, ic, eqs, last_eq_used, self.base_ring()) + class options(GlobalOptions): r""" Set and display the options for lazy series. From a464a615d11116a67c0432642b345a7b640a9e0a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Jan 2024 19:01:38 +0100 Subject: [PATCH 026/507] work around bug in InfinitePolynomialRing, better error messages --- src/sage/data_structures/stream.py | 47 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 502a3395aa8..8968f1a4dc1 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1514,6 +1514,7 @@ def _compute(self): """ # determine the next linear equations coeffs = [] + non_linear_coeffs = [] for i, eq in enumerate(self._eqs): while True: self._last_eqs_n[i] += 1 @@ -1525,42 +1526,56 @@ def _compute(self): V = coeff.variables() if not V: if coeff: - raise ValueError(f"no solution in degree {self._last_eqs_n} as {coeff} != 0") + if len(self._last_eqs_n) == 1: + raise ValueError(f"no solution in degree {self._last_eqs_n[0]} as {coeff} != 0") + raise ValueError(f"no solution in degrees {self._last_eqs_n} as {coeff} != 0") else: continue if coeff.degree() <= 1: coeffs.append(coeff) else: + # nonlinear equations must not be discarded, we + # keep them to improve any error messages + non_linear_coeffs.append(coeff) self._last_eqs_n[i] -= 1 break if not coeffs: - raise ValueError("No linear equations") + if len(self._last_eqs_n) == 1: + raise ValueError(f"no linear equations in degree {self._last_eqs_n[0]}: {non_linear_coeffs}") + raise ValueError(f"no linear equations in degrees {self._last_eqs_n}: {non_linear_coeffs}") # solve - from sage.structure.sequence import Sequence - coeffs = Sequence([coeff.polynomial() for coeff in coeffs]) - m1, v1 = coeffs.coefficient_matrix() - m = m1.matrix_from_columns([i for i, (c,) in enumerate(v1) - if c.degree() == 1]) - b = -m1.matrix_from_columns([i for i, (c,) in enumerate(v1) - if c.degree() == 0]) - - if not b: + from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence + eqs = PolynomialSequence([coeff.polynomial() for coeff in coeffs]) + m1, v1 = eqs.coefficient_matrix() + # there should be at most one entry in v1 of degree 0 + # v1 is a matrix, not a vector + for j, (c,) in enumerate(v1): + if c.degree() == 0: + b = -m1.column(j) + m = m1.matrix_from_columns([i for i in range(v1.nrows()) if i != j]) + v = [c for i, (c,) in enumerate(v1) if i != j] + break + else: from sage.modules.free_module_element import zero_vector - b = zero_vector(m.nrows()) + b = zero_vector(m1.nrows()) + m = m1 + v = v1.list() x = m.solve_right(b) k = m.right_kernel_matrix() - # substitute + from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense bad = True - for i, ((c,), (y,)) in enumerate(zip(v1, x)): + for i, (c, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): - var = self._P(c) + var = InfinitePolynomial_dense(self._P, c) val = self._base(y) self._subs_in_caches(var, val) bad = False if bad: - raise ValueError("Could not determine any coefficients") + if len(self._last_eqs_n) == 1: + raise ValueError(f"could not determine any coefficients in degree {self._last_eqs_n[0]} - equations are {coeffs + non_linear_coeffs}") + raise ValueError(f"could not determine any coefficients in degrees {self._last_eqs_n} - equations are {coeffs + non_linear_coeffs}") def iterate_coefficients(self): """ From b9f29ad5e7bcd4f2a50b0437ff13a4f7a803d76f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 8 Jan 2024 11:02:23 +0100 Subject: [PATCH 027/507] fix bad counting in _subs_in_caches --- src/sage/data_structures/stream.py | 44 +++++++++++++++++++++++++----- src/sage/rings/lazy_series_ring.py | 17 ++++++++---- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 8968f1a4dc1..92524c86519 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1368,7 +1368,7 @@ def define_implicitly(self, series, initial_values, equations, last_equation_use self._PFF = self._P.fraction_field() self._uncomputed = True self._eqs = equations - self._last_eqs_n = last_equation_used # the indices of the last equations we used + self._last_eqs_n = last_equation_used # the indices of the last equations we used self._series = series @lazy_attribute @@ -1401,8 +1401,23 @@ def _good_cache(self): This is used in :meth:`_subs_in_caches` to only substitute items that may contain undetermined coefficients. + + It might be better to share this across all uninitialized + series in one system. """ - return [0 for c in self._input_streams] + g = [] + for c in self._input_streams: + if c._is_sparse: + vals = c._cache.values() + else: + vals = c._cache + i = 0 + for v in vals: + if v not in self._base: + break + i += 1 + g.append(i) + return g def __getitem__(self, n): """ @@ -1496,15 +1511,20 @@ def subs(cache, k): for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] - good = m # last index of cache element still containing variables + good = m # last index of cache element still containing + # variables + + # TODO: document why we first substitute in the last + # element of the cache in the sparse case, but not in the + # dense case if s._is_sparse: for idx, i in zip(range(m), reversed(s._cache)): if not subs(s._cache, i): good = m - idx - 1 else: - for i in range(-m, 0): + for i in range(-1, -m-1, -1): if not subs(s._cache, i): - good = -i - 1 + good = m + i self._good_cache[j] += good STREAM_UNINITIALIZED_VARIABLES[self._P].remove(var) @@ -1512,6 +1532,10 @@ def _compute(self): """ Solve the next equations, until the next variable is determined. """ + # this is needed to work around a bug prohibiting conversion + # of variables into the InfinitePolynomialRing over the + # SymbolicRing + from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense # determine the next linear equations coeffs = [] non_linear_coeffs = [] @@ -1533,6 +1557,14 @@ def _compute(self): continue if coeff.degree() <= 1: coeffs.append(coeff) + elif coeff.is_monomial() and sum(1 for d in coeff.degrees() if d): + # if we have a single variable, we can remove the + # exponent - maybe we could also remove the + # coefficient - are we computing in an integral + # domain? + c = coeff.coefficients()[0] + v = InfinitePolynomial_dense(self._P, coeff.variables()[0]) + coeffs.append(c * v) else: # nonlinear equations must not be discarded, we # keep them to improve any error messages @@ -1543,7 +1575,6 @@ def _compute(self): if len(self._last_eqs_n) == 1: raise ValueError(f"no linear equations in degree {self._last_eqs_n[0]}: {non_linear_coeffs}") raise ValueError(f"no linear equations in degrees {self._last_eqs_n}: {non_linear_coeffs}") - # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence eqs = PolynomialSequence([coeff.polynomial() for coeff in coeffs]) @@ -1564,7 +1595,6 @@ def _compute(self): x = m.solve_right(b) k = m.right_kernel_matrix() # substitute - from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense bad = True for i, (c, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 1e574c8f374..127b26184b6 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -844,14 +844,19 @@ def define_implicitly(self, series, equations): O(z^7) sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: C = L.undefined() + sage: A = L.undefined(valuation=3) + sage: B = L.undefined(valuation=2) + sage: C = L.undefined(valuation=2) sage: FA = (A^2 + B^2)*z^2 sage: FB = A*B*z sage: FC = (A + B + C)*z^2 - sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0])], [FA, FB, FC]) - sage: B[2] + sage: L.define_implicitly([A, B, C], [FA, FB, FC]) + sage: A + O(z^10) + sage: B + O(z^9) + sage: C + O(z^9) sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() @@ -859,7 +864,7 @@ def define_implicitly(self, series, equations): sage: C = L.undefined() sage: D = L.undefined() sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) - sage: B[2] + sage: B[2] # known bug, not tested sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() From 9ab26c220b89ed342ef5a8b5c58d3d48bb31fe53 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 8 Jan 2024 14:56:30 +0100 Subject: [PATCH 028/507] fix to erroneous doctests - the bug was in the old version --- src/sage/rings/lazy_series_ring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 127b26184b6..f89a435dac2 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -764,12 +764,12 @@ def define_implicitly(self, series, equations): sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + Laurent series examples:: From 409c21e151bd06779c5bfad0fa9335579b9cdfb8 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 11 Jan 2024 16:50:37 +0100 Subject: [PATCH 029/507] fix bookkeeping --- src/sage/data_structures/stream.py | 97 +++++++++++++++++++----------- src/sage/rings/lazy_series_ring.py | 13 ++-- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 92524c86519..a959fbb4e00 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1337,7 +1337,7 @@ def define(self, target): self._cache = list() self._iter = self.iterate_coefficients() - def define_implicitly(self, series, initial_values, equations, last_equation_used, R): + def define_implicitly(self, series, initial_values, equations, R): r""" Define ``self`` via ``equations == 0``. @@ -1346,7 +1346,7 @@ def define_implicitly(self, series, initial_values, equations, last_equation_use - ``series`` -- a list of series - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - - ``R`` -- the coefficient ring + - ``R`` -- the ring containing the coefficients (after substitution) """ assert self._target is None @@ -1368,7 +1368,6 @@ def define_implicitly(self, series, initial_values, equations, last_equation_use self._PFF = self._P.fraction_field() self._uncomputed = True self._eqs = equations - self._last_eqs_n = last_equation_used # the indices of the last equations we used self._series = series @lazy_attribute @@ -1456,7 +1455,7 @@ def __getitem__(self, n): return ZZ.zero() # define_implicitly - if self._good_cache[0] >= n - self._approximate_order + 1: + if self._good_cache[0] > n - self._approximate_order: return self._cache[n - self._approximate_order] if self._uncomputed: @@ -1467,7 +1466,9 @@ def __getitem__(self, n): for f in self._series: f._uncomputed = True - if len(self._cache) >= n - self._approximate_order + 1: + if n < self._approximate_order: + return ZZ.zero() + if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] x = get_variable(self._P) @@ -1497,35 +1498,54 @@ def subs(cache, k): den = c.denominator() if var_p in den.variables(): num = den.subs({var: val}) - new = num/den + new = num / den elif c.parent() is self._P and var_p in c.variables(): new = c.subs({var: val}) else: - return c in self._base + return if new in self._base: cache[k] = self._base(new) - return True else: cache[k] = new - return False for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] - good = m # last index of cache element still containing - # variables - - # TODO: document why we first substitute in the last - # element of the cache in the sparse case, but not in the - # dense case if s._is_sparse: - for idx, i in zip(range(m), reversed(s._cache)): - if not subs(s._cache, i): - good = m - idx - 1 + # we traverse the cache beginning with the last + # element added, because only the last m elements + # added can contain variables + indices = reversed(s._cache) else: - for i in range(-1, -m-1, -1): - if not subs(s._cache, i): - good = m + i + indices = range(-1, -m-1, -1) + # determine last good element + good = m + for i0, i in enumerate(indices): + subs(s._cache, i) + if s._cache[i] not in self._base: + good = m - i0 - 1 self._good_cache[j] += good + # fix approximate_order and true_order + ao = s._approximate_order + if s._is_sparse: + while ao in s._cache: + if s._cache[ao]: + if s._cache[ao] in self._base: + s._true_order = True + break + del s._cache[ao] + self._good_cache[j] -= 1 + ao += 1 + else: + while s._cache: + if s._cache[0]: + if s._cache[0] in self._base: + s._true_order = True + break + del s._cache[0] + self._good_cache[j] -= 1 + ao += 1 + s._approximate_order = ao + STREAM_UNINITIALIZED_VARIABLES[self._P].remove(var) def _compute(self): @@ -1541,20 +1561,23 @@ def _compute(self): non_linear_coeffs = [] for i, eq in enumerate(self._eqs): while True: - self._last_eqs_n[i] += 1 - coeff = eq[self._last_eqs_n[i]] + ao = eq._approximate_order + coeff = eq[ao] + if not coeff: + # it may or may not be the case that the + # _approximate_order is advanced by __getitem__ + if eq._approximate_order == ao: + eq._approximate_order += 1 + continue if coeff.parent() is self._PFF: coeff = coeff.numerator() else: coeff = self._P(coeff) V = coeff.variables() if not V: - if coeff: - if len(self._last_eqs_n) == 1: - raise ValueError(f"no solution in degree {self._last_eqs_n[0]} as {coeff} != 0") - raise ValueError(f"no solution in degrees {self._last_eqs_n} as {coeff} != 0") - else: - continue + if len(self._eqs) == 1: + raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") + raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") if coeff.degree() <= 1: coeffs.append(coeff) elif coeff.is_monomial() and sum(1 for d in coeff.degrees() if d): @@ -1569,12 +1592,12 @@ def _compute(self): # nonlinear equations must not be discarded, we # keep them to improve any error messages non_linear_coeffs.append(coeff) - self._last_eqs_n[i] -= 1 break if not coeffs: - if len(self._last_eqs_n) == 1: - raise ValueError(f"no linear equations in degree {self._last_eqs_n[0]}: {non_linear_coeffs}") - raise ValueError(f"no linear equations in degrees {self._last_eqs_n}: {non_linear_coeffs}") + if len(self._eqs) == 1: + raise ValueError(f"there are no linear equations in degree {self._approximate_order}: {non_linear_coeffs}") + degrees = [eq._approximate_order for eq in self._eqs] + raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence eqs = PolynomialSequence([coeff.polynomial() for coeff in coeffs]) @@ -1603,9 +1626,11 @@ def _compute(self): self._subs_in_caches(var, val) bad = False if bad: - if len(self._last_eqs_n) == 1: - raise ValueError(f"could not determine any coefficients in degree {self._last_eqs_n[0]} - equations are {coeffs + non_linear_coeffs}") - raise ValueError(f"could not determine any coefficients in degrees {self._last_eqs_n} - equations are {coeffs + non_linear_coeffs}") + if len(self._eqs) == 1: + assert len(coeffs) + len(non_linear_coeffs) == 1 + raise ValueError(f"could not determine any coefficients using the equation in degree {self._eqs[0]._approximate_order}: {(coeffs + non_linear_coeffs)[0]}") + degrees = [eq._approximate_order for eq in self._eqs] + raise ValueError(f"could not determine any coefficients using the equations in degrees {degrees}: {coeffs + non_linear_coeffs}") def iterate_coefficients(self): """ diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index f89a435dac2..f35a2cd7d34 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -764,12 +764,12 @@ def define_implicitly(self, series, equations): sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + Laurent series examples:: @@ -784,7 +784,7 @@ def define_implicitly(self, series, equations): sage: g = L.undefined(-2) sage: L.define_implicitly([(g, [5])], [2+z*g(z^2) - g]) sage: g - + TESTS:: @@ -854,9 +854,9 @@ def define_implicitly(self, series, equations): sage: A O(z^10) sage: B - O(z^9) + O(z^16) sage: C - O(z^9) + O(z^23) sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() @@ -882,9 +882,8 @@ def define_implicitly(self, series, equations): for a in series] # common state for all series eqs = [eq._coeff_stream for eq in equations] - last_eq_used = [eq._approximate_order - 1 for eq in eqs] for f, ic in zip(s, ics): - f.define_implicitly(s, ic, eqs, last_eq_used, self.base_ring()) + f.define_implicitly(s, ic, eqs, self._internal_poly_ring.base_ring()) class options(GlobalOptions): r""" From 92d0abdcd1815d32ffeda5b96e91279f8dfe323d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 11 Jan 2024 20:21:37 +0100 Subject: [PATCH 030/507] slightly simplify logic --- src/sage/data_structures/stream.py | 61 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index a959fbb4e00..6383cb6e628 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1563,36 +1563,37 @@ def _compute(self): while True: ao = eq._approximate_order coeff = eq[ao] - if not coeff: - # it may or may not be the case that the - # _approximate_order is advanced by __getitem__ - if eq._approximate_order == ao: - eq._approximate_order += 1 - continue - if coeff.parent() is self._PFF: - coeff = coeff.numerator() - else: - coeff = self._P(coeff) - V = coeff.variables() - if not V: - if len(self._eqs) == 1: - raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") - raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") - if coeff.degree() <= 1: - coeffs.append(coeff) - elif coeff.is_monomial() and sum(1 for d in coeff.degrees() if d): - # if we have a single variable, we can remove the - # exponent - maybe we could also remove the - # coefficient - are we computing in an integral - # domain? - c = coeff.coefficients()[0] - v = InfinitePolynomial_dense(self._P, coeff.variables()[0]) - coeffs.append(c * v) - else: - # nonlinear equations must not be discarded, we - # keep them to improve any error messages - non_linear_coeffs.append(coeff) - break + if coeff: + break + # it may or may not be the case that the + # _approximate_order is advanced by __getitem__ + if eq._approximate_order == ao: + eq._approximate_order += 1 + + if coeff.parent() is self._PFF: + coeff = coeff.numerator() + else: + coeff = self._P(coeff) + V = coeff.variables() + if not V: + if len(self._eqs) == 1: + raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") + raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") + if coeff.degree() <= 1: + coeffs.append(coeff) + elif coeff.is_monomial() and sum(1 for d in coeff.degrees() if d): + # if we have a single variable, we can remove the + # exponent - maybe we could also remove the + # coefficient - are we computing in an integral + # domain? + c = coeff.coefficients()[0] + v = InfinitePolynomial_dense(self._P, coeff.variables()[0]) + coeffs.append(c * v) + else: + # nonlinear equations must not be discarded, we + # collect them to improve any error messages + non_linear_coeffs.append(coeff) + if not coeffs: if len(self._eqs) == 1: raise ValueError(f"there are no linear equations in degree {self._approximate_order}: {non_linear_coeffs}") From bf700ab91c00aa3eb7d80d37b7febdfee39edca5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 12 Jan 2024 09:52:51 +0100 Subject: [PATCH 031/507] fix typo --- src/sage/data_structures/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 6383cb6e628..05258c09a31 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1497,7 +1497,7 @@ def subs(cache, k): num = num.subs({var: val}) den = c.denominator() if var_p in den.variables(): - num = den.subs({var: val}) + den = den.subs({var: val}) new = num / den elif c.parent() is self._P and var_p in c.variables(): new = c.subs({var: val}) From 62574fe4f4a93a0e50fbb75a1e529f13be83b7de Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 12 Jan 2024 11:07:55 +0100 Subject: [PATCH 032/507] bypass buggy subs in InfinitePolynomialRing --- src/sage/data_structures/stream.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 05258c09a31..31f0fd27e01 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1485,6 +1485,7 @@ def _subs_in_caches(self, var, val): - ``var``, a variable - ``val``, the value that should replace the variable """ + from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense var_p = var.polynomial() def subs(cache, k): c = cache[k] @@ -1492,15 +1493,23 @@ def subs(cache, k): # stream have a parent if hasattr(c, "parent"): if c.parent() is self._PFF: + R = self._P.polynomial_ring() num = c.numerator() if var_p in num.variables(): - num = num.subs({var: val}) + d = {R(v): InfinitePolynomial_dense(self._P, v) for v in num.variables()} + d[R(var_p)] = val + num = R(num.polynomial()).subs(d) den = c.denominator() if var_p in den.variables(): - den = den.subs({var: val}) + d = {R(v): InfinitePolynomial_dense(self._P, v) for v in den.variables()} + d[R(var_p)] = val + den = R(den.polynomial()).subs(d) new = num / den elif c.parent() is self._P and var_p in c.variables(): - new = c.subs({var: val}) + R = self._P.polynomial_ring() + d = {R(v): InfinitePolynomial_dense(self._P, v) for v in c.variables()} + d[R(var_p)] = val + new = R(c.polynomial()).subs(d) else: return if new in self._base: From 80769fb50b1679452f4441f45acb5504505be8d3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 12 Jan 2024 19:37:41 +0100 Subject: [PATCH 033/507] more work on the multivariate case --- src/sage/data_structures/stream.py | 129 +++++++++--------- src/sage/rings/lazy_series.py | 4 + src/sage/rings/lazy_series_ring.py | 79 ++++++++++- .../polynomial/multi_polynomial_element.py | 5 +- 4 files changed, 152 insertions(+), 65 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 31f0fd27e01..7366d330e33 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1328,8 +1328,6 @@ def define(self, target): - ``target`` -- a stream - EXAMPLES:: - """ self._target = target self._n = self._approximate_order - 1 # the largest index of a coefficient we know @@ -1337,7 +1335,8 @@ def define(self, target): self._cache = list() self._iter = self.iterate_coefficients() - def define_implicitly(self, series, initial_values, equations, R): + def define_implicitly(self, series, initial_values, equations, + base_ring, coefficient_ring, terms_of_degree): r""" Define ``self`` via ``equations == 0``. @@ -1347,6 +1346,7 @@ def define_implicitly(self, series, initial_values, equations, R): - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - ``R`` -- the ring containing the coefficients (after substitution) + - ``terms_of_degree`` -- a function returning the list of terms of a given degree """ assert self._target is None @@ -1361,14 +1361,16 @@ def define_implicitly(self, series, initial_values, equations, R): self._approximate_order += len(initial_values) self._cache = [] - self._base = R + self._coefficient_ring = coefficient_ring + self._base_ring = base_ring # we use a silly variable name, because InfinitePolynomialRing is cached - self._P = InfinitePolynomialRing(self._base, names=("FESDUMMY",), + self._P = InfinitePolynomialRing(self._base_ring, names=("FESDUMMY",), implementation='dense') self._PFF = self._P.fraction_field() self._uncomputed = True self._eqs = equations self._series = series + self._terms_of_degree = terms_of_degree @lazy_attribute def _input_streams(self): @@ -1412,7 +1414,7 @@ def _good_cache(self): vals = c._cache i = 0 for v in vals: - if v not in self._base: + if v not in self._coefficient_ring: break i += 1 g.append(i) @@ -1471,7 +1473,8 @@ def __getitem__(self, n): if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] - x = get_variable(self._P) + x = sum(get_variable(self._P) * m + for m in self._terms_of_degree(n, self._P)) self._cache.append(x) return x @@ -1485,37 +1488,30 @@ def _subs_in_caches(self, var, val): - ``var``, a variable - ``val``, the value that should replace the variable """ - from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense var_p = var.polynomial() - def subs(cache, k): - c = cache[k] + def subs(poly): + R = self._P.polynomial_ring() + if var_p in poly.variables(): + d = {R(v): InfinitePolynomial_dense(self._P, v) + for v in poly.variables()} + d[R(var_p)] = val + return R(poly.polynomial()).subs(d) + return poly + + def subs_frac(c): # TODO: we may want to insist that all coefficients of a # stream have a parent if hasattr(c, "parent"): if c.parent() is self._PFF: - R = self._P.polynomial_ring() - num = c.numerator() - if var_p in num.variables(): - d = {R(v): InfinitePolynomial_dense(self._P, v) for v in num.variables()} - d[R(var_p)] = val - num = R(num.polynomial()).subs(d) - den = c.denominator() - if var_p in den.variables(): - d = {R(v): InfinitePolynomial_dense(self._P, v) for v in den.variables()} - d[R(var_p)] = val - den = R(den.polynomial()).subs(d) - new = num / den + new = subs(c.numerator()) / subs(c.denominator()) elif c.parent() is self._P and var_p in c.variables(): - R = self._P.polynomial_ring() - d = {R(v): InfinitePolynomial_dense(self._P, v) for v in c.variables()} - d[R(var_p)] = val - new = R(c.polynomial()).subs(d) + new = subs(c) else: - return - if new in self._base: - cache[k] = self._base(new) + return c + if new in self._coefficient_ring: + return self._coefficient_ring(new) else: - cache[k] = new + return new for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] @@ -1529,8 +1525,12 @@ def subs(cache, k): # determine last good element good = m for i0, i in enumerate(indices): - subs(s._cache, i) - if s._cache[i] not in self._base: + if self._base_ring == self._coefficient_ring: + s._cache[i] = subs_frac(s._cache[i]) + else: + s._cache[i] = s._cache[i].map_coefficients(subs_frac) + + if s._cache[i] not in self._coefficient_ring: good = m - i0 - 1 self._good_cache[j] += good # fix approximate_order and true_order @@ -1538,7 +1538,7 @@ def subs(cache, k): if s._is_sparse: while ao in s._cache: if s._cache[ao]: - if s._cache[ao] in self._base: + if s._cache[ao] in self._coefficient_ring: s._true_order = True break del s._cache[ao] @@ -1547,7 +1547,7 @@ def subs(cache, k): else: while s._cache: if s._cache[0]: - if s._cache[0] in self._base: + if s._cache[0] in self._coefficient_ring: s._true_order = True break del s._cache[0] @@ -1561,10 +1561,6 @@ def _compute(self): """ Solve the next equations, until the next variable is determined. """ - # this is needed to work around a bug prohibiting conversion - # of variables into the InfinitePolynomialRing over the - # SymbolicRing - from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense # determine the next linear equations coeffs = [] non_linear_coeffs = [] @@ -1579,29 +1575,37 @@ def _compute(self): if eq._approximate_order == ao: eq._approximate_order += 1 - if coeff.parent() is self._PFF: - coeff = coeff.numerator() - else: - coeff = self._P(coeff) - V = coeff.variables() - if not V: - if len(self._eqs) == 1: - raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") - raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") - if coeff.degree() <= 1: - coeffs.append(coeff) - elif coeff.is_monomial() and sum(1 for d in coeff.degrees() if d): - # if we have a single variable, we can remove the - # exponent - maybe we could also remove the - # coefficient - are we computing in an integral - # domain? - c = coeff.coefficients()[0] - v = InfinitePolynomial_dense(self._P, coeff.variables()[0]) - coeffs.append(c * v) + if self._base_ring == self._coefficient_ring: + lcoeff = [coeff] else: - # nonlinear equations must not be discarded, we - # collect them to improve any error messages - non_linear_coeffs.append(coeff) + # TODO: it is a coincidence that this currently + # exists in all examples + lcoeff = coeff.coefficients() + + for c in lcoeff: + if c.parent() is self._PFF: + c = c.numerator() + else: + c = self._P(c) + V = c.variables() + if not V: + if len(self._eqs) == 1: + raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") + raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") + if c.degree() <= 1: + coeffs.append(c) + elif c.is_monomial() and sum(1 for d in c.degrees() if d): + # if we have a single variable, we can remove the + # exponent - maybe we could also remove the + # coefficient - are we computing in an integral + # domain? + c1 = c.coefficients()[0] + v = InfinitePolynomial_dense(self._P, c.variables()[0]) + coeffs.append(c1 * v) + else: + # nonlinear equations must not be discarded, we + # collect them to improve any error messages + non_linear_coeffs.append(c) if not coeffs: if len(self._eqs) == 1: @@ -1631,8 +1635,11 @@ def _compute(self): bad = True for i, (c, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): + # work around a bug prohibiting conversion of + # variables into the InfinitePolynomialRing over the + # SymbolicRing var = InfinitePolynomial_dense(self._P, c) - val = self._base(y) + val = self._base_ring(y) self._subs_in_caches(var, val) bad = False if bad: diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index fd8de2ac50f..06639e817e4 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5271,9 +5271,13 @@ def coefficient(n): def coefficient(n): r = R.zero() + P = self._coeff_stream[0].parent().base_ring() + g1 = [a.change_ring(P) for a in g] for i in range(n // gv + 1): # Make sure the element returned from the composition is in P + # NO, we must not do this, because of define_implicitly r += P(self[i](g))[n] +# r += (self._coeff_stream[i](g1))[n] return r coeff_stream = Stream_function(coefficient, P._sparse, sorder * gv) return P.element_class(P, coeff_stream) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index f35a2cd7d34..cf1d0786151 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -659,6 +659,22 @@ def undefined(self, valuation=None): unknown = undefined + def _terms_of_degree(self, n, R): + r""" + Return the list of terms occurring in a coefficient of degree + ``n`` such that coefficients are in the ring ``R``. + + If ``self`` is a univariate Laurent, power, or Dirichlet + series, this is the list containing the one of the base ring. + + If ``self`` is a multivariate power series, this is the list + of monomials of total degree ``n``. + + If ``self`` is a lazy symmetric function, this is the list + of basis elements of total degree ``n``. + """ + raise NotImplementedError + def define_implicitly(self, series, equations): r""" Define series by solving functional equations. @@ -873,6 +889,14 @@ def define_implicitly(self, series, equations): sage: L.define_implicitly([A, B, C], [B - C - 1, B*z + 2*C + 1, A + 2*C + 1]) sage: A + 2*C + 1 O(z^7) + + A bivariate example:: + + sage: R. = LazyPowerSeriesRing(QQ) + sage: g = R.undefined() + sage: R.define_implicitly([g], [g - (z*q + z*g*~(1-g))]) + sage: g + """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -880,10 +904,12 @@ def define_implicitly(self, series, equations): ics = [a[1] if isinstance(a, (tuple, list)) else [] for a in series] - # common state for all series eqs = [eq._coeff_stream for eq in equations] for f, ic in zip(s, ics): - f.define_implicitly(s, ic, eqs, self._internal_poly_ring.base_ring()) + f.define_implicitly(s, ic, eqs, + self.base_ring(), + self._internal_poly_ring.base_ring(), + self._terms_of_degree) class options(GlobalOptions): r""" @@ -1989,6 +2015,14 @@ def _monomial(self, c, n): """ return self._laurent_poly_ring(c).shift(n) + def _terms_of_degree(self, n, R): + r""" + Return the list consisting of a single element ``1`` in the given + ring. + + """ + return [R.one()] + def uniformizer(self): """ Return a uniformizer of ``self``.. @@ -2343,6 +2377,26 @@ def _monomial(self, c, n): return L(c) * L.gen() ** n return L(c) + def _terms_of_degree(self, n, R): + r""" + Return the list of monomials of degree ``n`` in the polynomial + ring with base ring ``R``. + + EXAMPLES:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: L._terms_of_degree(3, QQ) + [1] + sage: L. = LazyPowerSeriesRing(QQ) + sage: L._terms_of_degree(3, QQ) + [y^3, x*y^2, x^2*y, x^3] + + """ + if self._arity == 1: + return [R.one()] + return [m.change_ring(R) + for m in self._internal_poly_ring.base_ring().monomials_of_degree(n)] + @cached_method def gen(self, n=0): """ @@ -2980,6 +3034,20 @@ def _monomial(self, c, n): L = self._laurent_poly_ring return L(c) + def _terms_of_degree(self, n, R): + r""" + Return the list of basis elements of degree ``n``. + + EXAMPLES:: + + sage: # needs sage.modules + sage: s = SymmetricFunctions(ZZ).s() + sage: L = LazySymmetricFunctions(s) + sage: L._terms_of_degree(3, ZZ) + [s[3], s[2, 1], s[1, 1, 1]] + """ + return list(self._internal_poly_ring.base_ring().homogeneous_component_basis(n)) + def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, check=True): r""" Construct a lazy element in ``self`` from ``x``. @@ -3603,6 +3671,13 @@ def _monomial(self, c, n): except (ValueError, TypeError): return '({})/{}^{}'.format(self.base_ring()(c), n, self.variable_name()) + def _terms_of_degree(self, n, R): + r""" + Return the list consisting of a single element 1 in the base ring. + """ + return [R.one()] + + def _skip_leading_zeros(iterator): """ Return an iterator which discards all leading zeros. diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index f5c1b0e480c..d1e187582f0 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -178,8 +178,9 @@ def __call__(self, *x, **kwds): except AttributeError: K = self.parent().base_ring() y = K(0) - for (m,c) in self.element().dict().items(): - y += c*prod([ x[i]**m[i] for i in range(n) if m[i] != 0]) + for m, c in self.element().dict().items(): + mon = prod((x[i]**m[i] for i in range(n) if m[i]), K(1)) + y += c * mon return y def _richcmp_(self, right, op): From 2285de910d99d3a16283e2b9645714f5e20d2819 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 16 Jan 2024 11:15:23 +0100 Subject: [PATCH 034/507] replace InfinitePolynomialRing, make simple bivariate example work --- src/sage/data_structures/stream.py | 262 ++++++++++++++++++++++------- src/sage/rings/lazy_series_ring.py | 8 +- 2 files changed, 205 insertions(+), 65 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 7366d330e33..1925d1f7622 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1235,20 +1235,180 @@ def iterate_coefficients(self): n += 1 denom *= n -STREAM_UNINITIALIZED_VARIABLES = defaultdict(set) -def get_variable(P): - r""" - Return the first variable with index not in - ``STREAM_UNINITIALIZED_VARIABLES``. - We need a different dictionary for each base ring. +from sage.structure.parent import Parent +from sage.structure.element import Element, parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.fields import Fields +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +class UndeterminedCoefficientsRingElement(Element): + def __init__(self, parent, v): + Element.__init__(self, parent) + self._p = v + + def _repr_(self): + return repr(self._p) + + def _richcmp_(self, other, op): + r""" + Compare ``self`` with ``other`` with respect to the comparison + operator ``op``. + """ + return self._p._richcmp_(other._p, op) + + def _add_(self, other): + """ + + EXAMPLES:: + + sage: from sage.data_structures.stream import UndeterminedCoefficientsRing + sage: R = UndeterminedCoefficientsRing(QQ) + sage: R(None) + 1 + FESDUMMY_... + 1 + """ + P = self.parent() + return P.element_class(P, self._p + other._p) + + def _sub_(self, other): + """ + EXAMPLES:: + + sage: from sage.data_structures.stream import UndeterminedCoefficientsRing + sage: R = UndeterminedCoefficientsRing(QQ) + sage: 1 - R(None) + -FESDUMMY_... + 1 + """ + P = self.parent() + return P.element_class(P, self._p - other._p) + + def _neg_(self): + """ + Return the negative of ``self``. + """ + P = self.parent() + return P.element_class(P, -self._p) + + def _mul_(self, other): + """ + EXAMPLES:: + + sage: from sage.data_structures.stream import UndeterminedCoefficientsRing + sage: R = UndeterminedCoefficientsRing(QQ) + sage: R(None) * R(None) + FESDUMMY_...*FESDUMMY_... + """ + P = self.parent() + return P.element_class(P, self._p * other._p) + + def _div_(self, other): + P = self.parent() + return P.element_class(P, self._p / other._p) + + def numerator(self): + return self._p.numerator() + + def variables(self): + """ + EXAMPLES:: + + sage: from sage.data_structures.stream import UndeterminedCoefficientsRing + sage: R = UndeterminedCoefficientsRing(QQ) + sage: R(None) / (R(None) + R(None)) + FESDUMMY_.../(FESDUMMY_... + FESDUMMY_...) + """ + return self._p.numerator().variables() + self._p.denominator().variables() + + def rational_function(self): + return self._p + + def subs(self, d): + """ + EXAMPLES:: + + sage: from sage.data_structures.stream import UndeterminedCoefficientsRing + sage: R = UndeterminedCoefficientsRing(QQ) + sage: p = R(None) + 1 + sage: v = p.variables()[0] + sage: q = R(None) + 1 + sage: (p/q).subs({v: 3}) + 4/(FESDUMMY_... + 1) + """ + P = self.parent() + V = self.variables() + d1 = {v: c for v, c in d.items() + if v in V} + return P.element_class(P, self._p.subs(d1)) + +class UndeterminedCoefficientsRing(UniqueRepresentation, Parent): + """ + Rational functions in unknowns over a base ring. """ - vars = STREAM_UNINITIALIZED_VARIABLES[P] - for i in range(P._max+2): - v = P.gen()[i] - if v not in vars: - vars.add(v) - return v + # must not inherit from UniqueRepresentation, because we want a + # new set of variables for each system of equations + + _PREFIX = "FESDUMMY_" + @staticmethod + def __classcall_private__(cls, base_ring, *args, **kwds): + return super().__classcall__(cls, base_ring, *args, **kwds) + + def __init__(self, base_ring): + self._pool = dict() # dict from variables actually used to indices of gens + # we must start with at least two variables, to make PolynomialSequence work + self._P = PolynomialRing(base_ring, names=[self._PREFIX+str(i) for i in range(2)]) + self._PF = self._P.fraction_field() + Parent.__init__(self, base=base_ring, category=Fields()) + + def polynomial_ring(self): + """ + .. WARNING:: + + This ring changes when new variables are requested. + """ + return self._P + + def _element_constructor_(self, x): + if x is None: + n = self._P.ngens() + for i in range(n): + if i not in self._pool.values(): + break + else: + names = self._P.variable_names() + (self._PREFIX+str(n),) + self._P = PolynomialRing(self._P.base_ring(), names) + self._PF = self._P.fraction_field() + i = n + v = self._P.gen(i) + self._pool[v] = i + return self.element_class(self, self._PF(v)) + + if x in self._PF: + return self.element_class(self, self._PF(x)) + + raise ValueError(f"{x} is not in {self}") + + def delete_variable(self, v): + del self._pool[v] + + def _coerce_map_from_(self, S): + """ + Return ``True`` if a coercion from ``S`` exists. + """ + if self._P.base_ring().has_coerce_map_from(S): + return True + return None + + def _coerce_map_from_base_ring(self): + """ + Return a coercion map from the base ring of ``self``. + """ + return self._generic_coerce_map(self._P.base_ring()) + + def _repr_(self): + return f"Undetermined coefficient ring over {self._P.base_ring()}" + + Element = UndeterminedCoefficientsRingElement + class Stream_uninitialized(Stream): r""" @@ -1363,10 +1523,8 @@ def define_implicitly(self, series, initial_values, equations, self._coefficient_ring = coefficient_ring self._base_ring = base_ring - # we use a silly variable name, because InfinitePolynomialRing is cached - self._P = InfinitePolynomialRing(self._base_ring, names=("FESDUMMY",), - implementation='dense') - self._PFF = self._P.fraction_field() + self._P = UndeterminedCoefficientsRing(self._base_ring) + # elements of the stream have self._P as base ring self._uncomputed = True self._eqs = equations self._series = series @@ -1473,7 +1631,7 @@ def __getitem__(self, n): if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] - x = sum(get_variable(self._P) * m + x = sum(self._P(None) * m for m in self._terms_of_degree(n, self._P)) self._cache.append(x) return x @@ -1488,31 +1646,6 @@ def _subs_in_caches(self, var, val): - ``var``, a variable - ``val``, the value that should replace the variable """ - var_p = var.polynomial() - def subs(poly): - R = self._P.polynomial_ring() - if var_p in poly.variables(): - d = {R(v): InfinitePolynomial_dense(self._P, v) - for v in poly.variables()} - d[R(var_p)] = val - return R(poly.polynomial()).subs(d) - return poly - - def subs_frac(c): - # TODO: we may want to insist that all coefficients of a - # stream have a parent - if hasattr(c, "parent"): - if c.parent() is self._PFF: - new = subs(c.numerator()) / subs(c.denominator()) - elif c.parent() is self._P and var_p in c.variables(): - new = subs(c) - else: - return c - if new in self._coefficient_ring: - return self._coefficient_ring(new) - else: - return new - for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] if s._is_sparse: @@ -1522,15 +1655,29 @@ def subs_frac(c): indices = reversed(s._cache) else: indices = range(-1, -m-1, -1) - # determine last good element + # substitute variable and determine last good element good = m for i0, i in enumerate(indices): - if self._base_ring == self._coefficient_ring: - s._cache[i] = subs_frac(s._cache[i]) - else: - s._cache[i] = s._cache[i].map_coefficients(subs_frac) - - if s._cache[i] not in self._coefficient_ring: + # the following looks dangerous - could there be a + # ring that contains the UndeterminedCoefficientRing? + # it is not enough to look at the parent of + # s._cache[i] + c = s._cache[i] + if c not in self._coefficient_ring: + if self._base_ring == self._coefficient_ring: + c = c.subs({var: val}) + f = c.rational_function() + if f in self._coefficient_ring: + c = self._coefficient_ring(f) + else: + c = c.map_coefficients(lambda e: e.subs({var: val})) + try: + c = c.map_coefficients(lambda e: self._base_ring(e.rational_function()), + self._base_ring) + except TypeError: + pass + s._cache[i] = c + if c not in self._coefficient_ring: good = m - i0 - 1 self._good_cache[j] += good # fix approximate_order and true_order @@ -1555,7 +1702,7 @@ def subs_frac(c): ao += 1 s._approximate_order = ao - STREAM_UNINITIALIZED_VARIABLES[self._P].remove(var) + self._P.delete_variable(var) def _compute(self): """ @@ -1583,10 +1730,7 @@ def _compute(self): lcoeff = coeff.coefficients() for c in lcoeff: - if c.parent() is self._PFF: - c = c.numerator() - else: - c = self._P(c) + c = self._P(c).numerator() V = c.variables() if not V: if len(self._eqs) == 1: @@ -1600,7 +1744,7 @@ def _compute(self): # coefficient - are we computing in an integral # domain? c1 = c.coefficients()[0] - v = InfinitePolynomial_dense(self._P, c.variables()[0]) + v = self._P(c.variables()[0]) coeffs.append(c1 * v) else: # nonlinear equations must not be discarded, we @@ -1614,7 +1758,7 @@ def _compute(self): raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - eqs = PolynomialSequence([coeff.polynomial() for coeff in coeffs]) + eqs = PolynomialSequence(self._P.polynomial_ring(), coeffs) m1, v1 = eqs.coefficient_matrix() # there should be at most one entry in v1 of degree 0 # v1 is a matrix, not a vector @@ -1633,12 +1777,8 @@ def _compute(self): k = m.right_kernel_matrix() # substitute bad = True - for i, (c, y) in enumerate(zip(v, x)): + for i, (var, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): - # work around a bug prohibiting conversion of - # variables into the InfinitePolynomialRing over the - # SymbolicRing - var = InfinitePolynomial_dense(self._P, c) val = self._base_ring(y) self._subs_in_caches(var, val) bad = False diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index cf1d0786151..3a47facd933 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -780,12 +780,12 @@ def define_implicitly(self, series, equations): sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + Laurent series examples:: @@ -872,7 +872,7 @@ def define_implicitly(self, series, equations): sage: B O(z^16) sage: C - O(z^23) + O(z^22) sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() @@ -896,7 +896,7 @@ def define_implicitly(self, series, equations): sage: g = R.undefined() sage: R.define_implicitly([g], [g - (z*q + z*g*~(1-g))]) sage: g - + z*q + z^2*q + z^3*q + (z^4*q+z^3*q^2) + (z^5*q+3*z^4*q^2) + O(z,q)^7 """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream From 07f3c079e75cbfbc840018b7883c16f582382de0 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 17 Jan 2024 18:27:50 +0100 Subject: [PATCH 035/507] stay in the coefficient ring if possible in Stream_cauchy_invert and Stream_dirichlet_invert, add (currently failing) doctest for implicitly defined series involving composition --- src/sage/data_structures/stream.py | 8 ++++---- src/sage/rings/lazy_series.py | 8 ++------ src/sage/rings/lazy_series_ring.py | 11 +++++++++++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 1925d1f7622..a3b7c448b0c 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -3389,9 +3389,9 @@ def _ainv(self): """ v = self._series.order() try: - return ~self._series[v] - except TypeError: return self._series[v].inverse_of_unit() + except ArithmeticError: + return ~self._series[v] def iterate_coefficients(self): """ @@ -3521,9 +3521,9 @@ def _ainv(self): 5 """ try: - return ~self._series[1] - except TypeError: return self._series[1].inverse_of_unit() + except ArithmeticError: + return ~self._series[1] def get_coefficient(self, n): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 06639e817e4..5d538cc862d 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5271,14 +5271,10 @@ def coefficient(n): def coefficient(n): r = R.zero() - P = self._coeff_stream[0].parent().base_ring() - g1 = [a.change_ring(P) for a in g] for i in range(n // gv + 1): - # Make sure the element returned from the composition is in P - # NO, we must not do this, because of define_implicitly - r += P(self[i](g))[n] -# r += (self._coeff_stream[i](g1))[n] + r += (self._coeff_stream[i](g))[n] return r + coeff_stream = Stream_function(coefficient, P._sparse, sorder * gv) return P.element_class(P, coeff_stream) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 3a47facd933..31b8f893c9f 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -897,6 +897,17 @@ def define_implicitly(self, series, equations): sage: R.define_implicitly([g], [g - (z*q + z*g*~(1-g))]) sage: g z*q + z^2*q + z^3*q + (z^4*q+z^3*q^2) + (z^5*q+3*z^4*q^2) + O(z,q)^7 + + A bivariate example involving composition of series:: + + sage: R. = LazyPowerSeriesRing(QQ) + sage: M1 = R.undefined() + sage: M2 = R.undefined() + sage: eq1 = -t*(x - y)*M1(0, 0, t)*x + t*(x - 1)*(x + 1)*(y^2 + 1)*M1(0, y, t) + (t*x^2*y^2 + t*x*y + t*y^2 + t - x*y)*M1(x, y, t) + t*M2(0, 0, t)*x*y + x*y + sage: eq2 = -t*M1(0, 0, t)*x + t*(x - 1)*(y + 1)*M1(0, y, t) + t*(x*y + y + 1)*M1(x, y, t) - t*M2(0, 0, t)*x + t*(x - 1)*(y^2 + y^2 + y + 1)*M2(0, y, t) + (t*x^2*y^2 + t*x*y^2 + t*x*y + t*y^2 + t*y^2 + t*y + t - x*y)*M2(x, y, t) + sage: R.define_implicitly([M1, M2], [eq1, eq2]) + sage: M1 + """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream From 41afaa2ede652460b9e9841aabef46609b9138c3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 18 Jan 2024 16:36:10 +0100 Subject: [PATCH 036/507] provide functorial construction for coercion --- src/sage/categories/pushout.py | 17 +++++++++++++++++ src/sage/data_structures/stream.py | 5 +++++ src/sage/rings/lazy_series.py | 6 +++++- src/sage/rings/lazy_series_ring.py | 9 +++++++-- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index fd2bbe4bcaf..24c984dfe83 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -1191,6 +1191,23 @@ def _repr_(self): return "MPoly[%s]" % ','.join(self.vars) +class UndeterminedCoefficientsFunctor(ConstructionFunctor): + rank = 0 + + def __init__(self): + from .rings import Rings + Functor.__init__(self, Rings(), Rings()) + + def _apply_functor(self, R): + from sage.data_structures.stream import UndeterminedCoefficientsRing + return UndeterminedCoefficientsRing(R) + + __hash__ = ConstructionFunctor.__hash__ + + def _repr_(self): + return "UndeterminedCoefficients" + + class InfinitePolynomialFunctor(ConstructionFunctor): r""" A Construction Functor for Infinite Polynomial Rings (see :mod:`~sage.rings.polynomial.infinite_polynomial_ring`). diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index a3b7c448b0c..bcce709ddd3 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1340,6 +1340,8 @@ def subs(self, d): if v in V} return P.element_class(P, self._p.subs(d1)) +from sage.categories.pushout import UndeterminedCoefficientsFunctor + class UndeterminedCoefficientsRing(UniqueRepresentation, Parent): """ Rational functions in unknowns over a base ring. @@ -1359,6 +1361,9 @@ def __init__(self, base_ring): self._PF = self._P.fraction_field() Parent.__init__(self, base=base_ring, category=Fields()) + def construction(self): + return (UndeterminedCoefficientsFunctor(), self.base_ring()) + def polynomial_ring(self): """ .. WARNING:: diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 5d538cc862d..cacf6c6f18b 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5272,7 +5272,11 @@ def coefficient(n): def coefficient(n): r = R.zero() for i in range(n // gv + 1): - r += (self._coeff_stream[i](g))[n] + c = self._coeff_stream[i] + if c in self.base_ring(): + c = P(c) + return c[n] + r += c(g)[n] return r coeff_stream = Stream_function(coefficient, P._sparse, sorder * gv) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 31b8f893c9f..c17f192ff80 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -2343,6 +2343,11 @@ def __init__(self, base_ring, names, sparse=True, category=None): Parent.__init__(self, base=base_ring, names=names, category=category) + def construction(self): + from sage.categories.pushout import CompletionFunctor + return (CompletionFunctor(self._names, infinity), + self._laurent_poly_ring) + def _repr_(self): """ String representation of this Taylor series ring. @@ -2590,8 +2595,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if valuation < 0: raise ValueError("the valuation of a Taylor series must be non-negative") # TODO: the following is nonsense, think of an iterator - if self._arity > 1: - raise ValueError("valuation must not be specified for multivariate Taylor series") +# if self._arity > 1 and valuation != 0: +# raise ValueError(f"valuation must not be specified for multivariate Taylor series (for {x}), but was set to {valuation}") if self._arity > 1: valuation = 0 From 44fcaff27d7dbffd61fa34a1988fa37e5b01f628 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 22 Jan 2024 10:42:02 +0100 Subject: [PATCH 037/507] fix bug in composition, work around performance bug in subs --- src/sage/data_structures/stream.py | 27 +++++++++++++++++++------ src/sage/rings/lazy_series.py | 32 +++++++++++++++--------------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index ed0af5b40ef..f52a757c4ec 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -983,6 +983,14 @@ class Stream_function(Stream_inexact): We assume for equality that ``function`` is a function in the mathematical sense. + .. WARNING:: + + To make + :meth:`sage.rings.lazy_series_ring.LazySeriesRing.define_implicitly` + work any streams used in ``function`` must appear in its + ``__closure__`` as instances of :class:`Stream`, as opposed + to, for example, as instances of :class:`LazyPowerSeries`. + EXAMPLES:: sage: from sage.data_structures.stream import Stream_function @@ -1337,10 +1345,17 @@ def subs(self, d): 4/(FESDUMMY_... + 1) """ P = self.parent() - V = self.variables() - d1 = {v: c for v, c in d.items() - if v in V} - return P.element_class(P, self._p.subs(d1)) + p_num = P._P(self._p.numerator()) + V_num = p_num.variables() + d_num = {P._P(v): c for v, c in d.items() + if v in V_num} + num = p_num.subs(d_num) + p_den = P._P(self._p.denominator()) + V_den = p_den.variables() + d_den = {P._P(v): c for v, c in d.items() + if v in V_den} + den = p_den.subs(d_den) + return P.element_class(P, P._PF(num / den)) from sage.categories.pushout import UndeterminedCoefficientsFunctor @@ -1381,7 +1396,7 @@ def _element_constructor_(self, x): if i not in self._pool.values(): break else: - names = self._P.variable_names() + (self._PREFIX+str(n),) + names = self._P.variable_names() + (self._PREFIX+str(n),) # tuple(self._PREFIX+str(i) for i in range(n, 2*n)) self._P = PolynomialRing(self._P.base_ring(), names) self._PF = self._P.fraction_field() i = n @@ -4317,7 +4332,7 @@ def __eq__(self, other): True """ return (isinstance(other, type(self)) - and self._integration_constants == other._integration_constants + and self._integration_constants == other._integration_constants) def is_nonzero(self): r""" diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 9621b532757..076f6d13a47 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5187,8 +5187,9 @@ def __call__(self, *g): cm = get_coercion_model() P = cm.common_parent(self.base_ring(), *[parent(h) for h in g]) + coeff_stream = self._coeff_stream # f = 0 - if isinstance(self._coeff_stream, Stream_zero): + if isinstance(coeff_stream, Stream_zero): return P.zero() # g = (0, ..., 0) @@ -5199,8 +5200,8 @@ def __call__(self, *g): return P(self[0]) # f has finite length and f != 0 - if (isinstance(self._coeff_stream, Stream_exact) - and not self._coeff_stream._constant): + if (isinstance(coeff_stream, Stream_exact) + and not coeff_stream._constant): # constant polynomial poly = self.polynomial() if poly.is_constant(): @@ -5250,7 +5251,7 @@ def __call__(self, *g): h._coeff_stream._approximate_order = 2 # We now have that every element of g has a _coeff_stream - sorder = self._coeff_stream._approximate_order + sorder = coeff_stream._approximate_order if len(g) == 1: g0 = g[0] if isinstance(g0, LazyDirichletSeries): @@ -5258,29 +5259,28 @@ def __call__(self, *g): def coefficient(n): return sum(self[i] * (g0**i)[n] for i in range(n+1)) - coeff_stream = Stream_function(coefficient, P._sparse, 1) - return P.element_class(P, coeff_stream) + return P.element_class(P, Stream_function(coefficient, + P._sparse, 1)) - coeff_stream = Stream_cauchy_compose(self._coeff_stream, - g0._coeff_stream, - P.is_sparse()) - return P.element_class(P, coeff_stream) + return P.element_class(P, Stream_cauchy_compose(coeff_stream, + g0._coeff_stream, + P.is_sparse())) # The arity is at least 2 gv = min(h._coeff_stream._approximate_order for h in g) - def coefficient(n): r = R.zero() for i in range(n // gv + 1): - c = self._coeff_stream[i] + c = coeff_stream[i] if c in self.base_ring(): c = P(c) - return c[n] - r += c(g)[n] + r += c[n] + else: + r += c(g)[n] return r - coeff_stream = Stream_function(coefficient, P._sparse, sorder * gv) - return P.element_class(P, coeff_stream) + return P.element_class(P, Stream_function(coefficient, + P._sparse, sorder * gv)) compose = __call__ From 585a358ff996438a4cb6803e576210942f3b3313 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 22 Jan 2024 11:46:10 +0100 Subject: [PATCH 038/507] make _terms_of_degree more correct for completions in higher arity --- src/sage/rings/lazy_series_ring.py | 76 ++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index e21eb638625..8d1d2841310 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -900,13 +900,46 @@ def define_implicitly(self, series, equations): A bivariate example involving composition of series:: - sage: R. = LazyPowerSeriesRing(QQ) - sage: M1 = R.undefined() - sage: M2 = R.undefined() - sage: eq1 = -t*(x - y)*M1(0, 0, t)*x + t*(x - 1)*(x + 1)*(y^2 + 1)*M1(0, y, t) + (t*x^2*y^2 + t*x*y + t*y^2 + t - x*y)*M1(x, y, t) + t*M2(0, 0, t)*x*y + x*y - sage: eq2 = -t*M1(0, 0, t)*x + t*(x - 1)*(y + 1)*M1(0, y, t) + t*(x*y + y + 1)*M1(x, y, t) - t*M2(0, 0, t)*x + t*(x - 1)*(y^2 + y^2 + y + 1)*M2(0, y, t) + (t*x^2*y^2 + t*x*y^2 + t*x*y + t*y^2 + t*y^2 + t*y + t - x*y)*M2(x, y, t) - sage: R.define_implicitly([M1, M2], [eq1, eq2]) - sage: M1 + sage: R. = LazyPowerSeriesRing(QQ) + sage: g = R.undefined() + sage: R.define_implicitly([g], [g - (z*q + z*g*~(1-g))]) + sage: g + z*q + z^2*q + z^3*q + (z^4*q+z^3*q^2) + (z^5*q+3*z^4*q^2) + O(z,q)^7 + + The following does not work currently, because the equations + determining the coefficients come in bad order:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: M1 = L.undefined() + sage: M2 = L.undefined() + sage: eq1 = t*x*y*M2(0, 0, t) + (t - x*y)*M1(x, y, t) + x*y - t*M1(0, y, t) + sage: eq2 = (t*x-t)*M2(0, y, t) + (t - x*y)*M2(x, y, t) + sage: L.define_implicitly([M1, M2], [eq1, eq2]) + sage: M1[1] # known bug, not tested + + Bicolored rooted trees with black and white roots:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: L.define_implicitly([A, B], [A - x*exp(B), B - y*exp(A)]) + sage: A + x + x*y + (x^2*y+1/2*x*y^2) + (1/2*x^3*y+2*x^2*y^2+1/6*x*y^3) + + (1/6*x^4*y+3*x^3*y^2+2*x^2*y^3+1/24*x*y^4) + + (1/24*x^5*y+8/3*x^4*y^2+27/4*x^3*y^3+4/3*x^2*y^4+1/120*x*y^5) + + (1/120*x^6*y+5/3*x^5*y^2+12*x^4*y^3+9*x^3*y^4+2/3*x^2*y^5+1/720*x*y^6) + + O(x,y)^8 + + sage: h = SymmetricFunctions(QQ).h() + sage: S = LazySymmetricFunctions(h) + sage: E = S(lambda n: h[n]) + sage: T = LazySymmetricFunctions(tensor([h, h])) + sage: X = tensor([h[1],h[[]]]) + sage: Y = tensor([h[[]],h[1]]) + sage: A = T.undefined() + sage: B = T.undefined() + sage: T.define_implicitly([A, B], [A - X*E(B), B - Y*E(A)]) + sage: A[1] # known bug, not tested """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) @@ -3091,8 +3124,33 @@ def _terms_of_degree(self, n, R): sage: L = LazySymmetricFunctions(s) sage: L._terms_of_degree(3, ZZ) [s[3], s[2, 1], s[1, 1, 1]] - """ - return list(self._internal_poly_ring.base_ring().homogeneous_component_basis(n)) + + sage: L = LazySymmetricFunctions(tensor([s, s])) + sage: L._terms_of_degree(3, ZZ) + [s[3] # s[], + s[2, 1] # s[], + s[1, 1, 1] # s[], + s[2] # s[1], + s[1, 1] # s[1], + s[1] # s[2], + s[1] # s[1, 1], + s[] # s[3], + s[] # s[2, 1], + s[] # s[1, 1, 1]] + """ + from sage.combinat.integer_vector import IntegerVectors + from sage.misc.mrange import cartesian_product_iterator + from sage.categories.tensor import tensor + B = self._internal_poly_ring.base_ring() + if self._arity == 1: + return list(B.homogeneous_component_basis(n)) + l = [] + for c in IntegerVectors(n, self._arity): + for m in cartesian_product_iterator([F.homogeneous_component_basis(p) + for F, p in zip(B.tensor_factors(), c)]): + l.append(tensor(m)) + return l + def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, check=True): r""" From 32bcda310ba8a6ef93c1950245ca19288f5a6c5b Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sat, 27 Jan 2024 02:52:48 +0100 Subject: [PATCH 039/507] Implemented `.ramified_places` to extend quaternion algebra functionality to number fields - Implemented method `.ramified_places` for quaternion algebras over number fields. Integrated `.ramified_primes()` into it in the process - Rerouted `.discriminant()` through `.ramified_places` - Modified `.is_division_algebra()`, `.is_matrix_ring()` and `.is_isomorphic` to use `.ramified_places` instead of `.discriminant()`, extending them to base number fields - Added `.is_definite()` and `.is_totally_definite()` methods - Added Voight's book "Quaternion Algebras" to the list of references --- src/doc/en/reference/references/index.rst | 3 + .../algebras/quatalg/quaternion_algebra.py | 336 +++++++++++++++--- 2 files changed, 282 insertions(+), 57 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 1c075a474d6..4535380e922 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -6342,6 +6342,9 @@ REFERENCES: .. [Voi2012] \J. Voight. Identifying the matrix ring: algorithms for quaternion algebras and quadratic forms, to appear. +.. [Voi2021] \J. Voight. Quaternion Algebras. Graduate Texts in + Mathematics 288. Springer Cham, 2021. + .. [VS06] \G.D. Villa Salvador. Topics in the Theory of Algebraic Function Fields. Birkh\"auser, 2006. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 26b02b5d3e8..d479b69a4da 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -401,9 +401,8 @@ def is_commutative(self) -> bool: def is_division_algebra(self) -> bool: """ - Return ``True`` if the quaternion algebra is a division algebra (i.e. - every nonzero element in ``self`` is invertible), and ``False`` if the - quaternion algebra is isomorphic to the 2x2 matrix algebra. + Checks whether the quaternion algebra ``self`` is a division algebra, i.e. whether + every nonzero element in ``self`` is invertible. EXAMPLES:: @@ -411,40 +410,52 @@ def is_division_algebra(self) -> bool: True sage: QuaternionAlgebra(1).is_division_algebra() False - sage: QuaternionAlgebra(2,9).is_division_algebra() - False + + sage: K = QuadraticField(3) + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(K, -1, -1).is_division_algebra() + True + sage: QuaternionAlgebra(L, -1, -1).is_division_algebra() + True + sage: QuaternionAlgebra(RR(2.),1).is_division_algebra() Traceback (most recent call last): ... - NotImplementedError: base field must be rational numbers + NotImplementedError: base field must be rational numbers or a number field """ - if not is_RationalField(self.base_ring()): - raise NotImplementedError("base field must be rational numbers") - return self.discriminant() != 1 + try: + return self.ramified_places(inf=True) != ([], []) + except ValueError: + raise NotImplementedError("base field must be rational numbers or a number field") def is_matrix_ring(self) -> bool: """ - Return ``True`` if the quaternion algebra is isomorphic to the 2x2 - matrix ring, and ``False`` if ``self`` is a division algebra (i.e. - every nonzero element in ``self`` is invertible). + Checks whether the quaternion algebra ``self`` is isomorphic to the 2x2 matrix + ring over the base field. EXAMPLES:: sage: QuaternionAlgebra(QQ,-5,-2).is_matrix_ring() False - sage: QuaternionAlgebra(1).is_matrix_ring() - True sage: QuaternionAlgebra(2,9).is_matrix_ring() True + + sage: K = QuadraticField(3) + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(K, -1, -1).is_matrix_ring() + False + sage: QuaternionAlgebra(L, -1, -1).is_matrix_ring() + False + sage: QuaternionAlgebra(RR(2.),1).is_matrix_ring() Traceback (most recent call last): ... - NotImplementedError: base field must be rational numbers - + NotImplementedError: base field must be rational numbers or a number field """ - if not is_RationalField(self.base_ring()): - raise NotImplementedError("base field must be rational numbers") - return self.discriminant() == 1 + try: + return self.ramified_places(inf=True) == ([], []) + except ValueError: + raise NotImplementedError("base field must be rational numbers or a number field") def is_exact(self) -> bool: """ @@ -1029,86 +1040,297 @@ def inner_product_matrix(self): a, b = self._a, self._b return diagonal_matrix(self.base_ring(), [2, -2*a, -2*b, 2*a*b]) - @cached_method - def discriminant(self): + def is_definite(self): """ - Return the discriminant of this quaternion algebra, i.e. the product of the finite - primes it ramifies at. + Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the + unique Archimedean place of its base field QQ. This is the case if and only if both + invariants of ``self`` are negative, see [Voi2021, Exercise 2.4(c)]. EXAMPLES:: - sage: QuaternionAlgebra(210,-22).discriminant() - 210 - sage: QuaternionAlgebra(19).discriminant() - 19 + sage: QuaternionAlgebra(QQ,-5,-2).is_definite() + True + sage: QuaternionAlgebra(1).is_definite() + False + + sage: QuaternionAlgebra(RR(2.),1).is_definite() + Traceback (most recent call last): + ... + ValueError: base field must be rational numbers + """ + if not is_RationalField(self.base_ring()): + raise ValueError("base field must be rational numbers") + a, b = self.invariants() + return a < 0 and b < 0 + + def is_totally_definite(self): + """ + Checks whether the quaternion algebra ``self`` is totally definite, i.e. whether it ramifies + at all real Archimedean places of its base number field. + + EXAMPLES:: + + sage: QuaternionAlgebra(QQ, -5, -2).is_totally_definite() + True + + sage: K = QuadraticField(3) + sage: QuaternionAlgebra(K, -1, -1).is_totally_definite() + True + + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(L, -1, -1).is_totally_definite() + True sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) - sage: B. = QuaternionAlgebra(F, 2*a, F(-1)) - sage: B.discriminant() - Fractional ideal (2) + sage: QuaternionAlgebra(F, 2*a, F(-1)).is_totally_definite() + False - sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).discriminant() # needs sage.symbolic - Fractional ideal (1) + sage: QuaternionAlgebra(RR(2.),1).is_definite() + Traceback (most recent call last): + ... + ValueError: base field must be rational numbers or a number field """ - if not is_RationalField(self.base_ring()): - try: - F = self.base_ring() - return F.hilbert_conductor(self._a, self._b) - except NotImplementedError: - raise ValueError("base field must be rational numbers or number field") - else: - return ZZ.prod(self.ramified_primes()) + F = self.base_ring() + if is_RationalField(F): + return self.is_definite() + + try: + E = F.real_embeddings() + return [e for e in E if F.hilbert_symbol(self._a, self._b, e) == -1] == E + except (AttributeError, NotImplementedError): + raise ValueError("base field must be rational numbers or a number field") + + @cached_method + def ramified_places(self, inf=True): + """ + Return the places of the base number field at which the quaternion algebra ``self`` ramifies. + + Note: The initial choice of primes (in the case F = QQ) respectively of prime ideals (in the + number field case) to check ramification for is motivated by [Voi2021, 12.4.12(a)]. The + restriction to real Archimedean embeddings is due to [Voi2021, 14.5.8]. + + INPUT: + + - ``inf`` -- (default: ``True``) + + OUTPUT: + + The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ if + ``self`` is defined over the rational field QQ, respectively as fractional ideals of the number + field's ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the Archimedean + (AKA infinite) places at which ``self`` ramifies (given by real embeddings of the base field). + + EXAMPLES:: + + sage: QuaternionAlgebra(210,-22).ramified_places() + ([2, 3, 5, 7], []) + + sage: QuaternionAlgebra(-1, -1).ramified_places() + ([2], + [Ring morphism: + From: Rational Field + To: Real Field with 53 bits of precision + Defn: 1 |--> 1.00000000000000]) + + sage: K = QuadraticField(3) + sage: QuaternionAlgebra(K, -1, -1).ramified_places() + ([], + [Ring morphism: + From: Number Field in a with defining polynomial x^2 - 3 with a = 1.732050807568878? + To: Real Field with 53 bits of precision + Defn: a |--> -1.73205080756888, + Ring morphism: + From: Number Field in a with defining polynomial x^2 - 3 with a = 1.732050807568878? + To: Real Field with 53 bits of precision + Defn: a |--> 1.73205080756888]) + + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(L, -1, -1).ramified_places() + ([Fractional ideal (2, 1/2*a + 1/2), Fractional ideal (2, 1/2*a - 1/2)], []) + + sage: x = polygen(ZZ, 'x') + sage: F. = NumberField(x^2 - x - 1) + sage: QuaternionAlgebra(F, 2*a, F(-1)).ramified_places() + ([Fractional ideal (2)], + [Ring morphism: + From: Number Field in a with defining polynomial x^2 - x - 1 + To: Real Field with 53 bits of precision + Defn: a |--> -0.618033988749895]) + + sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).ramified_places() # needs sage.symbolic + ([], []) + sage: QuaternionAlgebra(RR(2.),1).ramified_places() + Traceback (most recent call last) + ... + ValueError: base field must be rational numbers or a number field + """ + if not isinstace(inf, bool): + raise ValueError("inf must be a truth value") + + F = self.base_ring() + + # For efficiency (and to not convert QQ into a number field manually), we handle F = QQ first + if is_RationalField(F): + ram_fin = sorted([p for p in set([2]).union(prime_divisors(self._a.numerator()), + prime_divisors(self._a.denominator()), prime_divisors(self._b.numerator()), + prime_divisors(self._b.denominator())) if hilbert_symbol(self._a, self._b, p) == -1]) + + if not inf: + return ram_fin + + # The given quaternion algebra ramifies at the unique infinite place of QQ, by definition, + # if and only if it is definite + if self.is_definite(): + return ram_fin, QQ.places() + + return ram_fin, [] + + try: + # Over the number field F, first compute the finite ramified places + ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(self._a), + F.primes_above(self._b)) if F.hilbert_symbol(self._a, self._b, p) == -1] + + if not inf: + return ram_fin + + # At this point the infinite ramified places also need to be computed + return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(self._a, self._b, e) == -1] + except (AttributeError, NotImplementedError): + raise ValueError("base field must be rational numbers or a number field") + @cached_method def ramified_primes(self): """ - Return the (finite) primes that ramify in this rational quaternion algebra. + Return the (finite) primes of the base number field at which the quaternion algebra ``self`` ramifies. OUTPUT: - The list of prime numbers at which ``self`` ramifies (given as integers), sorted by their - magnitude (small to large). + The list of finite primes at which ``self`` ramifies; given as integers, sorted + small-to-large, if ``self`` is defined over QQ, and as fractional ideals in the + ring of integers of the base number field otherwise. EXAMPLES:: - sage: QuaternionAlgebra(QQ, -1, -1).ramified_primes() + sage: QuaternionAlgebra(-58, -69).ramified_primes() + [3, 23, 29] + + sage: K = QuadraticField(3) + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(-1, -1).ramified_primes() [2] + sage: QuaternionAlgebra(K, -1, -1).ramified_primes() + [] + sage: QuaternionAlgebra(L, -1, -1).ramified_primes() + [Fractional ideal (2, 1/2*a + 1/2), Fractional ideal (2, 1/2*a - 1/2)] - sage: QuaternionAlgebra(QQ, -58, -69).ramified_primes() - [3, 23, 29] + sage: x = polygen(ZZ, 'x') + sage: F. = NumberField(x^2 - x - 1) + sage: QuaternionAlgebra(F, 2*a, F(-1)).ramified_primes() + [Fractional ideal (2)] + + sage: QuaternionAlgebra(RR(2.),1).ramified_primes() + Traceback (most recent call last) + ... + ValueError: base field must be rational numbers or a number field """ - if not is_RationalField(self.base_ring()): - raise ValueError("base field must be the rational numbers") + return self.ramified_places(inf=False) + + @cached_method + def discriminant(self): + """ + Return the discriminant of the quaternion algebra ``self``, i.e. the product of the + finite places it ramifies at. - return sorted([p for p in set([2]).union(prime_divisors(self._a.numerator()), - prime_divisors(self._a.denominator()), prime_divisors(self._b.numerator()), - prime_divisors(self._b.denominator())) if hilbert_symbol(self._a, self._b, p) == -1]) + OUTPUT: + + The discriminant of this quaternion algebra (which has to be defined over a number field), + as an element of ZZ if ``self`` is defined over QQ, and as a fractional ideal in the + ring of integers of the base number field otherwise. + + EXAMPLES:: + + sage: QuaternionAlgebra(210, -22).discriminant() + 210 + sage: QuaternionAlgebra(19).discriminant() + 19 + sage: QuaternionAlgebra(-1, -1).discriminant() + 2 + + sage: K = QuadraticField(3) + sage: L = QuadraticField(-15) + sage: QuaternionAlgebra(K, -1, -1).discriminant() + Fractional ideal (1) + sage: QuaternionAlgebra(L, -1, -1).discriminant() + Fractional ideal (2) + + sage: x = polygen(ZZ, 'x') + sage: F. = NumberField(x^2 - x - 1) + sage: QuaternionAlgebra(F, 2*a, F(-1)).discriminant() + Fractional ideal (2) + + sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).discriminant() # needs sage.symbolic + Fractional ideal (1) + + sage: QuaternionAlgebra(RR(2.),1).discriminant() + Traceback (most recent call last) + ... + ValueError: base field must be rational numbers or a number field + """ + F = self.base_ring() + if is_RationalField(F): + return ZZ.prod(self.ramified_places(inf=False)) + + try: + return F.ideal(F.prod(self.ramified_places(inf=False))) + except NotImplementedError: + raise ValueError("base field must be rational numbers or a number field") def is_isomorphic(self, A) -> bool: """ - Return ``True`` if (and only if) ``self`` and ``A`` are isomorphic quaternion algebras over Q. + Checks whether ``self`` and ``A`` are isomorphic quaternion algebras. + + Currently only implemented over a number field; motivated by + [Voi2021, Main Theorem 14.6.1], noting that QQ has a unique infinite place. INPUT: - - ``A`` -- a quaternion algebra defined over the rationals Q + - ``A`` -- a quaternion algebra defined over a number field EXAMPLES:: sage: B = QuaternionAlgebra(-46, -87) sage: A = QuaternionAlgebra(-58, -69) + sage: A == B + False sage: B.is_isomorphic(A) True - sage: A == B + + sage: K = QuadraticField(3) + sage: A = QuaternionAlgebra(K, -1, -1) + sage: B = QuaternionAlgebra(K, 1, -1) + sage: A.discriminant() == B.discriminant() + True + sage: B.is_isomorphic(A) False """ if not isinstance(A, QuaternionAlgebra_ab): raise TypeError("A must be a quaternion algebra of the form (a,b)_K") - if self.base_ring() != QQ or A.base_ring() != QQ: - raise NotImplementedError("isomorphism check only implemented for rational quaternion algebras") + F = self.base_ring() + if F != A.base_ring(): + raise ValueError("both quaternion algebras must be defined over the same base ring") - return self.ramified_primes() == A.ramified_primes() + try: + if is_RationalField(F): + return self.ramified_places(inf=False) == A.ramified_places(inf=False) + + ram_self = self.ramified_places(inf=True) + ram_A = A.ramified_places(inf=True) + return set(ram_self[0]) == set(ram_A[0]) and ram_self[1] == ram_A[1] + except ValueError: + raise NotImplementedError("base field must be rational numbers or a number field") def _magma_init_(self, magma): """ From 97f1257b5d51bc4ea76c092ee66b240f39af2b32 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sat, 27 Jan 2024 03:55:59 +0100 Subject: [PATCH 040/507] Fixed lint issues and refernce formatting --- .../algebras/quatalg/quaternion_algebra.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index d479b69a4da..01c84d4e821 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -401,7 +401,7 @@ def is_commutative(self) -> bool: def is_division_algebra(self) -> bool: """ - Checks whether the quaternion algebra ``self`` is a division algebra, i.e. whether + Checks whether the quaternion algebra ``self`` is a division algebra, i.e. whether every nonzero element in ``self`` is invertible. EXAMPLES:: @@ -1044,7 +1044,7 @@ def is_definite(self): """ Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the unique Archimedean place of its base field QQ. This is the case if and only if both - invariants of ``self`` are negative, see [Voi2021, Exercise 2.4(c)]. + invariants of ``self`` are negative, see Exercise 2.4(c) in [Voi2021]_. EXAMPLES:: @@ -1107,8 +1107,8 @@ def ramified_places(self, inf=True): Return the places of the base number field at which the quaternion algebra ``self`` ramifies. Note: The initial choice of primes (in the case F = QQ) respectively of prime ideals (in the - number field case) to check ramification for is motivated by [Voi2021, 12.4.12(a)]. The - restriction to real Archimedean embeddings is due to [Voi2021, 14.5.8]. + number field case) to check ramification for is motivated by 12.4.12(a) in [Voi2021]_. The + restriction to real Archimedean embeddings is due to 14.5.8 in [Voi2021]_. INPUT: @@ -1116,9 +1116,9 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ if - ``self`` is defined over the rational field QQ, respectively as fractional ideals of the number - field's ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the Archimedean + The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ if + ``self`` is defined over the rational field QQ, respectively as fractional ideals of the number + field's ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the Archimedean (AKA infinite) places at which ``self`` ramifies (given by real embeddings of the base field). EXAMPLES:: @@ -1175,7 +1175,7 @@ def ramified_places(self, inf=True): ram_fin = sorted([p for p in set([2]).union(prime_divisors(self._a.numerator()), prime_divisors(self._a.denominator()), prime_divisors(self._b.numerator()), prime_divisors(self._b.denominator())) if hilbert_symbol(self._a, self._b, p) == -1]) - + if not inf: return ram_fin @@ -1199,7 +1199,6 @@ def ramified_places(self, inf=True): except (AttributeError, NotImplementedError): raise ValueError("base field must be rational numbers or a number field") - @cached_method def ramified_primes(self): """ @@ -1241,14 +1240,14 @@ def ramified_primes(self): def discriminant(self): """ Return the discriminant of the quaternion algebra ``self``, i.e. the product of the - finite places it ramifies at. + finite places it ramifies at. OUTPUT: The discriminant of this quaternion algebra (which has to be defined over a number field), as an element of ZZ if ``self`` is defined over QQ, and as a fractional ideal in the ring of integers of the base number field otherwise. - + EXAMPLES:: sage: QuaternionAlgebra(210, -22).discriminant() @@ -1261,7 +1260,7 @@ def discriminant(self): sage: K = QuadraticField(3) sage: L = QuadraticField(-15) sage: QuaternionAlgebra(K, -1, -1).discriminant() - Fractional ideal (1) + Fractional ideal (1) sage: QuaternionAlgebra(L, -1, -1).discriminant() Fractional ideal (2) @@ -1291,8 +1290,8 @@ def is_isomorphic(self, A) -> bool: """ Checks whether ``self`` and ``A`` are isomorphic quaternion algebras. - Currently only implemented over a number field; motivated by - [Voi2021, Main Theorem 14.6.1], noting that QQ has a unique infinite place. + Currently only implemented over a number field; motivated by Main Theorem 14.6.1 + in [Voi2021]_, noting that QQ has a unique infinite place. INPUT: From 0bb5cfe4539e6bb45dc84ac0c0a7cd52b97cafac Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sun, 28 Jan 2024 00:16:14 +0100 Subject: [PATCH 041/507] Fixed typo and corrected/modified docstrings --- .../algebras/quatalg/quaternion_algebra.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 01c84d4e821..06083f287e0 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1086,7 +1086,7 @@ def is_totally_definite(self): sage: QuaternionAlgebra(F, 2*a, F(-1)).is_totally_definite() False - sage: QuaternionAlgebra(RR(2.),1).is_definite() + sage: QuaternionAlgebra(RR(2.),1).is_totally_definite() Traceback (most recent call last): ... ValueError: base field must be rational numbers or a number field @@ -1116,10 +1116,11 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ if - ``self`` is defined over the rational field QQ, respectively as fractional ideals of the number - field's ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the Archimedean - (AKA infinite) places at which ``self`` ramifies (given by real embeddings of the base field). + The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ, + sorted small to large, if ``self`` is defined over the rational field QQ, respectively as + fractional ideals of the number field's ring of integers, otherwise) and, if ``inf`` is set + to ``True``, also the Archimedean (AKA infinite) places at which ``self`` ramifies (given + by real embeddings of the base field). EXAMPLES:: @@ -1161,11 +1162,11 @@ def ramified_places(self, inf=True): sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).ramified_places() # needs sage.symbolic ([], []) sage: QuaternionAlgebra(RR(2.),1).ramified_places() - Traceback (most recent call last) + Traceback (most recent call last): ... ValueError: base field must be rational numbers or a number field """ - if not isinstace(inf, bool): + if not isinstance(inf, bool): raise ValueError("inf must be a truth value") F = self.base_ring() @@ -1207,7 +1208,7 @@ def ramified_primes(self): OUTPUT: The list of finite primes at which ``self`` ramifies; given as integers, sorted - small-to-large, if ``self`` is defined over QQ, and as fractional ideals in the + small to large, if ``self`` is defined over QQ, and as fractional ideals in the ring of integers of the base number field otherwise. EXAMPLES:: @@ -1230,7 +1231,7 @@ def ramified_primes(self): [Fractional ideal (2)] sage: QuaternionAlgebra(RR(2.),1).ramified_primes() - Traceback (most recent call last) + Traceback (most recent call last): ... ValueError: base field must be rational numbers or a number field """ @@ -1273,7 +1274,7 @@ def discriminant(self): Fractional ideal (1) sage: QuaternionAlgebra(RR(2.),1).discriminant() - Traceback (most recent call last) + Traceback (most recent call last): ... ValueError: base field must be rational numbers or a number field """ From a02472b3b5c66e9bba62fefbbaa0c638b8c8e842 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Tue, 30 Jan 2024 12:58:00 -0500 Subject: [PATCH 042/507] Added pseudomorphism --- src/sage/modules/free_module.py | 12 ++ .../modules/free_module_pseudomorphism.py | 109 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/sage/modules/free_module_pseudomorphism.py diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index ffd6260764d..32243ee1ad8 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3064,6 +3064,18 @@ def hom(self, im_gens, codomain=None, **kwds): codomain = R**n return super().hom(im_gens, codomain, **kwds) + def pseudohom(self, morphism, twist=None, **kwds): + r""" + Created a pseudomorphism defined by a given morphism and twist. + Let A be a ring and M a free module over A. Let \theta: A \to A + + """ + from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism + from sage.structure.element import is_Matrix + if is_Matrix(morphism): + return FreeModulePseudoMorphism(self.hom(morphism), twist) + return FreeModulePseudoMorphism(morphism, twist) + def inner_product_matrix(self): """ Return the default identity inner product matrix associated to this diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py new file mode 100644 index 00000000000..e489c40cff3 --- /dev/null +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -0,0 +1,109 @@ +""" +Pseudomorphisms of free modules + +AUTHORS: + + +TESTS:: + + sage: V = ZZ^2; f = V.hom([V.1, -2*V.0]) +""" + +#################################################################################### +# Copyright (C) 2009 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#################################################################################### + +# A matrix morphism is a morphism that is defined by multiplication by a +# matrix. Elements of domain must either have a method "vector()" that +# returns a vector that the defining matrix can hit from the left, or +# be coercible into vector space of appropriate dimension. +import sage.modules.free_module as free_module +from sage.categories.morphism import Morphism +from sage.modules import free_module_homspace, matrix_morphism +from sage.structure.richcmp import rich_to_bool, richcmp +from sage.structure.sequence import Sequence +from sage.structure.all import parent +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.rings.derivation', 'RingDerivation') + +class FreeModulePseudoMorphism(): + def __init__(self, morphism, twist=None, side="left"): + """ + INPUT: + + - ``parent`` - a homspace in a (sub) category of free modules + + - ``A`` - matrix + + - side -- side of the vectors acted on by the matrix (default: ``"left"``) + + EXAMPLES:: + + sage: V = ZZ^3; W = span([[1,2,3],[-1,2,8]], ZZ) + sage: phi = V.hom(matrix(ZZ,3,[1..9])) + sage: type(phi) + + """ + self.derivation = None + if isinstance(twist, Morphism): + self.twist_morphism = twist + elif isinstance(twist, RingDerivation): + self.twist_morphism = twist.parent().twisting_morphism() + if twist: + self.derivation = twist + else: + self.derivation = None + self.side = side + self.base_morphism = morphism + + def __call__(self, x): + if self.twist_morphism is None and self.derivation is None: + return self.base_morphism(x) + else: + try: + if parent(x) is not self.domain(): + x = self.domain()(x) + except TypeError: + raise TypeError("%s must be coercible into %s" % (x,self.domain())) + if self.domain().is_ambient(): + x = x.element() + else: + x = self.domain().coordinate_vector(x) + if self.twist_morphism is None: + x_twistmorphism = x + else: + x_twistmorphism = self.domain()(list(map(self.twist_morphism, x))) + C = self.codomain() + if self.side == "left": + v = x_twistmorphism * self.matrix() + else: + v = self.matrix() * x_twistmorphism + if self.derivation is not None: + v += self.domain()(list(map(self.derivation, x))) + if not C.is_ambient(): + v = C.linear_combination_of_basis(v) + return C._element_constructor_(v) + + def domain(self): + return self.base_morphism.domain() + + def codomain(self): + return self.base_morphism.codomain() + + def matrix(self): + return self.base_morphism.matrix() + + def base_morphism(self): + return self.base_morphism From e1232f15792bf339e6258e4f72b3c27c3cfa608f Mon Sep 17 00:00:00 2001 From: ymusleh Date: Wed, 31 Jan 2024 05:23:34 -0500 Subject: [PATCH 043/507] Adding pseudohomspace --- src/sage/modules/free_module.py | 12 ++-- .../modules/free_module_pseudohomspace.py | 61 +++++++++++++++++++ .../modules/free_module_pseudomorphism.py | 38 ++++++++++-- 3 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 src/sage/modules/free_module_pseudohomspace.py diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 32243ee1ad8..422e5b50394 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -212,6 +212,8 @@ richcmp_not_equal, ) from sage.structure.sequence import Sequence +from sage.categories.morphism import Morphism +from sage.matrix.constructor import matrix ############################################################################### # @@ -3064,7 +3066,11 @@ def hom(self, im_gens, codomain=None, **kwds): codomain = R**n return super().hom(im_gens, codomain, **kwds) - def pseudohom(self, morphism, twist=None, **kwds): + def PseudoHom(self, codomain=None, twist=None): + from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace + return FreeModulePseudoHomspace(self, codomain, twist) + + def pseudohom(self, morphism, twist=None, codomain=None, **kwds): r""" Created a pseudomorphism defined by a given morphism and twist. Let A be a ring and M a free module over A. Let \theta: A \to A @@ -3072,9 +3078,7 @@ def pseudohom(self, morphism, twist=None, **kwds): """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism from sage.structure.element import is_Matrix - if is_Matrix(morphism): - return FreeModulePseudoMorphism(self.hom(morphism), twist) - return FreeModulePseudoMorphism(morphism, twist) + return FreeModulePseudoMorphism(self, morphism, twist, codomain) def inner_product_matrix(self): """ diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py new file mode 100644 index 00000000000..7728b942245 --- /dev/null +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -0,0 +1,61 @@ + +# **************************************************************************** +# Copyright (C) 2005 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See the GNU General Public License for more details; the full text +# is available at: +# +# https://www.gnu.org/licenses/ +# **************************************************************************** +import sage.categories.homset +from sage.structure.element import is_Matrix +from sage.matrix.constructor import matrix, identity_matrix +from sage.matrix.matrix_space import MatrixSpace +from sage.misc.cachefunc import cached_method +from sage.categories.morphism import Morphism +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.rings.derivation', 'RingDerivation') + +class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): + def __init__(self, X, Y, twist=None): + self._domain = X + self._codomain = X + if Y is not None: + self._codomain = Y + if (twist.domain() is not self.domain().coordinate_ring() + or twist.codomain() is not self.codomain().coordinate_ring()): + raise TypeError("twisting morphism domain/codomain do not match coordinate rings of the modules") + elif isinstance(twist, Morphism) or isinstance(twist, RingDerivation): + self.twist = twist + else: + raise TypeError("twist is not a ring morphism or derivation") + + def __call__(self, A, **kwds): + from . import free_module_pseudomorphism + side = kwds.get("side", "left") + if not is_Matrix(A): + C = self.codomain() + try: + if callable(A): + v = [C(A(g)) for g in self.domain().gens()] + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) + if side == "right": + A = A.transpose() + else: + v = [C(a) for a in A] + if side == "right": + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()).transpose() + else: + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) + except TypeError: + pass + if not self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring()) and not A.is_zero(): + raise TypeError("nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") + return free_module_pseudomorphism.FreeModulePseudoMorphism(self.domain(), A, twist=self.twist, codomain = self.codomain()) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index e489c40cff3..eb86a86f83d 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -35,17 +35,23 @@ from sage.structure.sequence import Sequence from sage.structure.all import parent from sage.misc.lazy_import import lazy_import +from sage.modules.free_module_morphism import FreeModuleMorphism +from sage.modules.free_module_homspace import FreeModuleHomspace, is_FreeModuleHomspace +from sage.matrix.constructor import matrix lazy_import('sage.rings.derivation', 'RingDerivation') -class FreeModulePseudoMorphism(): - def __init__(self, morphism, twist=None, side="left"): +class FreeModulePseudoMorphism(Morphism): + def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left"): """ INPUT: + - ``domain`` - the domain of the pseudomorphism; a free module - - ``parent`` - a homspace in a (sub) category of free modules + - ``base_morphism`` - either a morphism or a matrix defining a morphism - - ``A`` - matrix + - ``twist`` - a twisting morphism, this is either a morphism or a derivation (default: None) + + - ``codomain`` - the codomain of the pseudomorphism; a free module (default: None) - side -- side of the vectors acted on by the matrix (default: ``"left"``) @@ -56,7 +62,15 @@ def __init__(self, morphism, twist=None, side="left"): sage: type(phi) """ + from sage.structure.element import is_Matrix + if is_Matrix(base_morphism): + self.base_morphism = domain.hom(base_morphism, codomain) + elif isinstance(base_morphism, Morphism): + self.base_morphism = base_morphism + else: + self.base_morphism = domain.hom(matrix(domain.coordinate_ring(), base_morphism), codomain) self.derivation = None + self.twist_morphism = None if isinstance(twist, Morphism): self.twist_morphism = twist elif isinstance(twist, RingDerivation): @@ -66,7 +80,6 @@ def __init__(self, morphism, twist=None, side="left"): else: self.derivation = None self.side = side - self.base_morphism = morphism def __call__(self, x): if self.twist_morphism is None and self.derivation is None: @@ -96,6 +109,21 @@ def __call__(self, x): v = C.linear_combination_of_basis(v) return C._element_constructor_(v) + def __repr__(self): + r = "Free module pseudomorphism defined {}by the matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" + act = "" + if self.side == "right": + act = "as left-multiplication " + morph = "" + if self.twist_morphism is not None: + morph = "\nTwisted by the morphism {}" + morph = morph.format(self.twist_morphism.__repr__()) + deriv = "" + if self.derivation is not None: + deriv = "\nTwisted by the derivation {}" + deriv = deriv.format(self.derivation.__repr__()) + return r.format(act, self.matrix(), morph, deriv, self.domain(), self.codomain()) + def domain(self): return self.base_morphism.domain() From 90d836509d35355d183317b9de3983b1c4108fa0 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Wed, 31 Jan 2024 16:00:01 -0500 Subject: [PATCH 044/507] Improve documentation for pseudomorphism --- src/sage/modules/free_module.py | 3 +- .../modules/free_module_pseudohomspace.py | 18 ++- .../modules/free_module_pseudomorphism.py | 110 +++++++++++++++--- 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 422e5b50394..2e4528abf5b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3078,7 +3078,8 @@ def pseudohom(self, morphism, twist=None, codomain=None, **kwds): """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism from sage.structure.element import is_Matrix - return FreeModulePseudoMorphism(self, morphism, twist, codomain) + side = kwds.get("side", "left") + return FreeModulePseudoMorphism(self, morphism, twist, codomain, side) def inner_product_matrix(self): """ diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 7728b942245..8e2682d3b65 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -1,6 +1,6 @@ # **************************************************************************** -# Copyright (C) 2005 William Stein +# Copyright (C) 2024 Yossef Musleh # # Distributed under the terms of the GNU General Public License (GPL) # @@ -24,6 +24,17 @@ lazy_import('sage.rings.derivation', 'RingDerivation') class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): + r""" + This class implements the space of Pseudomorphisms with a fixed twist. + + + + EXAMPLES:: + + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism(5) + sage: PHS = F.PseudoHom(twist) + + """ def __init__(self, X, Y, twist=None): self._domain = X self._codomain = X @@ -58,4 +69,7 @@ def __call__(self, A, **kwds): pass if not self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring()) and not A.is_zero(): raise TypeError("nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") - return free_module_pseudomorphism.FreeModulePseudoMorphism(self.domain(), A, twist=self.twist, codomain = self.codomain()) + return free_module_pseudomorphism.FreeModulePseudoMorphism(self.domain(), A, twist=self.twist, codomain = self.codomain()) + + def __repr__(self): + pass diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index eb86a86f83d..5664410c607 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -3,14 +3,54 @@ AUTHORS: + - Yossef Musleh (2024-02): initial version + +Let M be a free module over a ring R, and let $theta, delta: R to R$ +be a morphism and derivation of $R$ respectively such that + +$delta(xy) = theta(x)delta(y) + x$. + +Then a pseudomorphism from $f : M to M$ is a map such that + + $f(x + y) = f(x) + f(y)$ + $f(lambda x) = theta(lambda)f(x) + delta(lambda)x$ + +If $delta$ is the zero morphism, then we can relax the condition that the +codomain is M and consider a free module $M'$ over a ring $R'$. + TESTS:: - sage: V = ZZ^2; f = V.hom([V.1, -2*V.0]) + sage: V = ZZ^2 + sage: f = V.pseudohom([V.1, -2*V.0]); f + Free module pseudomorphism defined by the matrix + [ 0 1] + [-2 0] + Domain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + sage: f(V((1, 2))) + (-4, 1) + + sage: P. = ZZ[]; deriv = P.derivation() + sage: M = P^2 + sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right"); f + Free module pseudomorphism defined as left-multiplication by the matrix + [ 1 2*x] + [ x 1] + Twisted by the derivation d/dx + Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring + Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring + sage: e = M((2*x^2 + 3*x + 1, x^3 + 7*x + 4)) + sage: f(e) + (2*x^4 + 16*x^2 + 15*x + 4, 3*x^3 + 6*x^2 + 8*x + 11) + sage: f = M.pseudohom([[1, 2], [1, 1]], deriv) + sage: f(e) + (x^3 + 2*x^2 + 14*x + 8, x^3 + 7*x^2 + 13*x + 13) + """ #################################################################################### -# Copyright (C) 2009 William Stein +# Copyright (C) 2024 Yossef Musleh # # Distributed under the terms of the GNU General Public License (GPL) # @@ -24,10 +64,6 @@ # http://www.gnu.org/licenses/ #################################################################################### -# A matrix morphism is a morphism that is defined by multiplication by a -# matrix. Elements of domain must either have a method "vector()" that -# returns a vector that the defining matrix can hit from the left, or -# be coercible into vector space of appropriate dimension. import sage.modules.free_module as free_module from sage.categories.morphism import Morphism from sage.modules import free_module_homspace, matrix_morphism @@ -44,6 +80,8 @@ class FreeModulePseudoMorphism(Morphism): def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left"): """ + Constructs a pseudomorphism of free modules. + INPUT: - ``domain`` - the domain of the pseudomorphism; a free module @@ -57,10 +95,10 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" EXAMPLES:: - sage: V = ZZ^3; W = span([[1,2,3],[-1,2,8]], ZZ) - sage: phi = V.hom(matrix(ZZ,3,[1..9])) + sage: F = GF(25); V = F^3; twist = F.frobenius_endomorphism(5) + sage: phi = V.pseudohom(matrix(F,3,[1..9]), twist) sage: type(phi) - + """ from sage.structure.element import is_Matrix if is_Matrix(base_morphism): @@ -80,8 +118,20 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" else: self.derivation = None self.side = side - + def __call__(self, x): + r""" + Return the result of applying a pseudomorphism to an element of the + free module. + + TESTS:: + + sage: Fq = GF(25); M = Fq^2; frob = Fq.frobenius_endomorphism(5) + sage: ph = M.pseudohom([[1, 2], [0, 1]], frob, side="right") + sage: e = M((3*Fq.gen() + 2, 2*Fq.gen() + 2)) + sage: ph(e) + (z2 + 2, 1) + """ if self.twist_morphism is None and self.derivation is None: return self.base_morphism(x) else: @@ -94,15 +144,14 @@ def __call__(self, x): x = x.element() else: x = self.domain().coordinate_vector(x) - if self.twist_morphism is None: - x_twistmorphism = x - else: - x_twistmorphism = self.domain()(list(map(self.twist_morphism, x))) C = self.codomain() if self.side == "left": - v = x_twistmorphism * self.matrix() + v = x * self.matrix() else: - v = self.matrix() * x_twistmorphism + v = self.matrix() * x + if self.twist_morphism is not None: + for i in range(len(v)): + v[i] *= self.twist_morphism(x[i]) if self.derivation is not None: v += self.domain()(list(map(self.derivation, x))) if not C.is_ambient(): @@ -110,6 +159,11 @@ def __call__(self, x): return C._element_constructor_(v) def __repr__(self): + r""" + Return the string representation of a pseudomorphism. + + TESTS:: + """ r = "Free module pseudomorphism defined {}by the matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" act = "" if self.side == "right": @@ -125,13 +179,37 @@ def __repr__(self): return r.format(act, self.matrix(), morph, deriv, self.domain(), self.codomain()) def domain(self): + r""" + Return the domain of the pseudomorphism. + """ return self.base_morphism.domain() def codomain(self): + r""" + Return the codomain of the pseudomorphism. + """ return self.base_morphism.codomain() def matrix(self): + r""" + Return the underlying matrix of a pseudomorphism. + """ return self.base_morphism.matrix() def base_morphism(self): + r""" + Return the underlying morphism of a pseudomorphism. This is an element + of the Hom space of the free module. + """ return self.base_morphism + + def twisting_morphism(self): + r""" + Return the twisting homomorphism of the pseudomorphism. + """ + return self.twist_morphism + + def derivation(self): + r""" + """ + return self.derivation From eab0a12feea62822b9bc58bfc92918e65cc09aa2 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 03:27:21 -0500 Subject: [PATCH 045/507] Finishing documentation --- src/sage/modules/free_module.py | 8 +- .../modules/free_module_pseudohomspace.py | 75 +++++++++-- .../modules/free_module_pseudomorphism.py | 119 ++++++++++-------- 3 files changed, 135 insertions(+), 67 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 2e4528abf5b..d34fb91c6fc 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3066,15 +3066,19 @@ def hom(self, im_gens, codomain=None, **kwds): codomain = R**n return super().hom(im_gens, codomain, **kwds) - def PseudoHom(self, codomain=None, twist=None): + def PseudoHom(self, twist=None, codomain=None): from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace return FreeModulePseudoHomspace(self, codomain, twist) def pseudohom(self, morphism, twist=None, codomain=None, **kwds): r""" - Created a pseudomorphism defined by a given morphism and twist. + Create a pseudomorphism defined by a given morphism and twist. Let A be a ring and M a free module over A. Let \theta: A \to A + EXAMPLE:: + + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism(5) + sage: """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism from sage.structure.element import is_Matrix diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 8e2682d3b65..09dd12934f7 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -27,12 +27,17 @@ class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): r""" This class implements the space of Pseudomorphisms with a fixed twist. - + For free modules, the elements of a pseudomorphism correspond to matrices + which define the mapping on elements of a basis. EXAMPLES:: - sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism(5) - sage: PHS = F.PseudoHom(twist) + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: h = PHS([[1, 2], [1, 1]]) + sage: e = M((4*F.gen()^2 + F.gen() + 2, 4*F.gen()^2 + 4*F.gen() + 4)) + sage: h(e) + (3*z3^2 + z3, 4*z3^2 + 3*z3 + 3) """ def __init__(self, X, Y, twist=None): @@ -40,15 +45,22 @@ def __init__(self, X, Y, twist=None): self._codomain = X if Y is not None: self._codomain = Y + self.base_homspace = self._domain.Hom(self._codomain) + if twist is None: + return if (twist.domain() is not self.domain().coordinate_ring() or twist.codomain() is not self.codomain().coordinate_ring()): - raise TypeError("twisting morphism domain/codomain do not match coordinate rings of the modules") + raise TypeError("twisting morphism domain/codomain do not match\ + coordinate rings of the modules") elif isinstance(twist, Morphism) or isinstance(twist, RingDerivation): self.twist = twist else: raise TypeError("twist is not a ring morphism or derivation") def __call__(self, A, **kwds): + r""" + Coerce a matrix or free module homomorphism into a pseudomorphism. + """ from . import free_module_pseudomorphism side = kwds.get("side", "left") if not is_Matrix(A): @@ -62,14 +74,57 @@ def __call__(self, A, **kwds): else: v = [C(a) for a in A] if side == "right": - A = matrix([C.coordinates(a) for a in v], ncols=C.rank()).transpose() + A = matrix([C.coordinates(a) for a in v], \ + ncols=C.rank()).transpose() else: - A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) + A = matrix([C.coordinates(a) for a in v], \ + ncols=C.rank()) except TypeError: pass - if not self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring()) and not A.is_zero(): - raise TypeError("nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") - return free_module_pseudomorphism.FreeModulePseudoMorphism(self.domain(), A, twist=self.twist, codomain = self.codomain()) + if not self.codomain().base_ring().has_coerce_map_from(\ + self.domain().base_ring()) and not A.is_zero(): + raise TypeError("nontrivial morphisms require a coercion map \ + from the base ring of the domain to the base ring of the \ + codomain") + return free_module_pseudomorphism.FreeModulePseudoMorphism(\ + self.domain(), A, twist=self.twist, \ + codomain = self.codomain()) def __repr__(self): - pass + r""" + Returns the string representation of the pseudomorphism space. + + EXAMPLE:: + + """ + r = "Set of Pseudomorphisms from {} to {} {} {}" + morph = "" + if self.twist_morphism is not None: + morph = "\nTwisted by the morphism {}" + morph = morph.format(self.twist_morphism.__repr__()) + deriv = "" + if self.derivation is not None: + deriv = "\nTwisted by the derivation {}" + deriv = deriv.format(self.derivation.__repr__()) + return r.format(self.domain(), self.codomain(), morph, deriv) + + def zero(self): + r""" + Return the zero pseudomorphism. + """ + return self.base_homspace.zero() + + def _matrix_space(self): + return self.base_homspace._matrix_space() + + def basis(self, side="left"): + r""" + Return a basis for the underlying matrix space. + """ + return self.base_homspace.basis(side) + + def identity(self): + r""" + Return the pseudomorphism corresponding to the identity transformation + """ + return self.base_homspace.identity() diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 5664410c607..44145b2b247 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -5,48 +5,6 @@ - Yossef Musleh (2024-02): initial version -Let M be a free module over a ring R, and let $theta, delta: R to R$ -be a morphism and derivation of $R$ respectively such that - -$delta(xy) = theta(x)delta(y) + x$. - -Then a pseudomorphism from $f : M to M$ is a map such that - - $f(x + y) = f(x) + f(y)$ - $f(lambda x) = theta(lambda)f(x) + delta(lambda)x$ - -If $delta$ is the zero morphism, then we can relax the condition that the -codomain is M and consider a free module $M'$ over a ring $R'$. - - -TESTS:: - - sage: V = ZZ^2 - sage: f = V.pseudohom([V.1, -2*V.0]); f - Free module pseudomorphism defined by the matrix - [ 0 1] - [-2 0] - Domain: Ambient free module of rank 2 over the principal ideal domain Integer Ring - Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring - sage: f(V((1, 2))) - (-4, 1) - - sage: P. = ZZ[]; deriv = P.derivation() - sage: M = P^2 - sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right"); f - Free module pseudomorphism defined as left-multiplication by the matrix - [ 1 2*x] - [ x 1] - Twisted by the derivation d/dx - Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring - Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring - sage: e = M((2*x^2 + 3*x + 1, x^3 + 7*x + 4)) - sage: f(e) - (2*x^4 + 16*x^2 + 15*x + 4, 3*x^3 + 6*x^2 + 8*x + 11) - sage: f = M.pseudohom([[1, 2], [1, 1]], deriv) - sage: f(e) - (x^3 + 2*x^2 + 14*x + 8, x^3 + 7*x^2 + 13*x + 13) - """ #################################################################################### @@ -78,6 +36,49 @@ lazy_import('sage.rings.derivation', 'RingDerivation') class FreeModulePseudoMorphism(Morphism): + r""" + Let `M, M'` be free modules over a ring `R`, `\theta: R \to R` a ring + homomorphism, and `\theta: R \to R` a derivation i.e. an additive + map such that + + `\delta(xy) = x\delta(y) + \delta(x)y`. + + Then a pseudomorphism `f : M to M` is a map such that + + `f(x + y) = f(x) + f(y)` + `f(\lambda x) = `\theta(\lambda)f(x) + \delta(\lambda)x` + + The pair `(\theta, \delta)` may be referred to as the *twist* of + the morphism. + + TESTS:: + + sage: V = ZZ^2 + sage: f = V.pseudohom([V.1, -2*V.0]); f + Free module pseudomorphism defined by the matrix + [ 0 1] + [-2 0] + Domain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + sage: f(V((1, 2))) + (-4, 1) + + sage: P. = ZZ[]; deriv = P.derivation() + sage: M = P^2 + sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right"); f + Free module pseudomorphism defined as left-multiplication by the matrix + [ 1 2*x] + [ x 1] + twisted by the derivation d/dx + Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring + Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring + sage: e = M((2*x^2 + 3*x + 1, x^3 + 7*x + 4)) + sage: f(e) + (2*x^4 + 16*x^2 + 15*x + 4, 3*x^3 + 6*x^2 + 8*x + 11) + sage: f = M.pseudohom([[1, 2], [1, 1]], deriv) + sage: f(e) + (x^3 + 2*x^2 + 14*x + 8, x^3 + 7*x^2 + 13*x + 13) + """ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left"): """ Constructs a pseudomorphism of free modules. @@ -85,13 +86,17 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" INPUT: - ``domain`` - the domain of the pseudomorphism; a free module - - ``base_morphism`` - either a morphism or a matrix defining a morphism + - ``base_morphism`` - either a morphism or a matrix defining a + morphism - - ``twist`` - a twisting morphism, this is either a morphism or a derivation (default: None) + - ``twist`` - a twisting morphism, this is either a morphism or + a derivation (default: None) - - ``codomain`` - the codomain of the pseudomorphism; a free module (default: None) + - ``codomain`` - the codomain of the pseudomorphism; a free + module (default: None) - - side -- side of the vectors acted on by the matrix (default: ``"left"``) + - side -- side of the vectors acted on by the matrix + (default: ``"left"``) EXAMPLES:: @@ -106,7 +111,8 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" elif isinstance(base_morphism, Morphism): self.base_morphism = base_morphism else: - self.base_morphism = domain.hom(matrix(domain.coordinate_ring(), base_morphism), codomain) + self.base_morphism = domain.hom(matrix(domain.coordinate_ring(), \ + base_morphism), codomain) self.derivation = None self.twist_morphism = None if isinstance(twist, Morphism): @@ -126,11 +132,11 @@ def __call__(self, x): TESTS:: - sage: Fq = GF(25); M = Fq^2; frob = Fq.frobenius_endomorphism(5) - sage: ph = M.pseudohom([[1, 2], [0, 1]], frob, side="right") - sage: e = M((3*Fq.gen() + 2, 2*Fq.gen() + 2)) + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) sage: ph(e) - (z2 + 2, 1) + (3*z3 + 3, 2*z3^2, 5*z3^2 + 4) """ if self.twist_morphism is None and self.derivation is None: return self.base_morphism(x) @@ -164,19 +170,21 @@ def __repr__(self): TESTS:: """ - r = "Free module pseudomorphism defined {}by the matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" + r = "Free module pseudomorphism defined {}by the \ + matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" act = "" if self.side == "right": act = "as left-multiplication " morph = "" if self.twist_morphism is not None: - morph = "\nTwisted by the morphism {}" + morph = "\ntwisted by the morphism {}" morph = morph.format(self.twist_morphism.__repr__()) deriv = "" if self.derivation is not None: - deriv = "\nTwisted by the derivation {}" + deriv = "\ntwisted by the derivation {}" deriv = deriv.format(self.derivation.__repr__()) - return r.format(act, self.matrix(), morph, deriv, self.domain(), self.codomain()) + return r.format(act, self.matrix(), morph, deriv, \ + self.domain(), self.codomain()) def domain(self): r""" @@ -211,5 +219,6 @@ def twisting_morphism(self): def derivation(self): r""" + Return the twisting derivation of the pseudomorphism. """ return self.derivation From 0af5e3be1e8f21f815a5d7cad29ac4b57b9f0bc6 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 03:49:57 -0500 Subject: [PATCH 046/507] finishing docs --- .../modules/free_module_pseudohomspace.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 09dd12934f7..8b22700d892 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -60,6 +60,18 @@ def __init__(self, X, Y, twist=None): def __call__(self, A, **kwds): r""" Coerce a matrix or free module homomorphism into a pseudomorphism. + + EXAMPLES:: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: h = PHS([[1, 2], [1, 1]]); h + Free module pseudomorphism defined by the matrix + [1 2] + [1 1] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ from . import free_module_pseudomorphism side = kwds.get("side", "left") @@ -110,7 +122,15 @@ def __repr__(self): def zero(self): r""" - Return the zero pseudomorphism. + Return the zero pseudomorphism. This corresponds to the zero matrix. + + EXAMPLES:: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: PHS.zero().matrix() + [0 0] + [0 0] """ return self.base_homspace.zero() @@ -126,5 +146,13 @@ def basis(self, side="left"): def identity(self): r""" Return the pseudomorphism corresponding to the identity transformation + EXAMPLES:: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: PHS.zero().matrix() + [1 0] + [0 1] + """ return self.base_homspace.identity() From b15fa0590cf623285bb96cf9f91fd78e00ce3bc2 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 04:29:24 -0500 Subject: [PATCH 047/507] fixed pseudo action --- src/sage/modules/free_module.py | 9 +++++++-- src/sage/modules/free_module_pseudohomspace.py | 5 +++-- src/sage/modules/free_module_pseudomorphism.py | 13 +++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index d34fb91c6fc..0530fe2b0f4 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3067,6 +3067,11 @@ def hom(self, im_gens, codomain=None, **kwds): return super().hom(im_gens, codomain, **kwds) def PseudoHom(self, twist=None, codomain=None): + r""" + Create the Pseudohomspace corresponding to given twist data. + + + """ from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace return FreeModulePseudoHomspace(self, codomain, twist) @@ -3075,9 +3080,9 @@ def pseudohom(self, morphism, twist=None, codomain=None, **kwds): Create a pseudomorphism defined by a given morphism and twist. Let A be a ring and M a free module over A. Let \theta: A \to A - EXAMPLE:: + EXAMPLES:: - sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism(5) + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() sage: """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 8b22700d892..f95aeb2fb9d 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -37,7 +37,7 @@ class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): sage: h = PHS([[1, 2], [1, 1]]) sage: e = M((4*F.gen()^2 + F.gen() + 2, 4*F.gen()^2 + 4*F.gen() + 4)) sage: h(e) - (3*z3^2 + z3, 4*z3^2 + 3*z3 + 3) + (z3, 2*z3^2 + 3*z3 + 3) """ def __init__(self, X, Y, twist=None): @@ -146,11 +146,12 @@ def basis(self, side="left"): def identity(self): r""" Return the pseudomorphism corresponding to the identity transformation + EXAMPLES:: sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) - sage: PHS.zero().matrix() + sage: PHS.identity().matrix() [1 0] [0 1] diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 44145b2b247..d707dc44d6a 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -136,7 +136,7 @@ def __call__(self, x): sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) sage: ph(e) - (3*z3 + 3, 2*z3^2, 5*z3^2 + 4) + (2*z3^2 + 3*z3 + 2, z3^2 + 2*z3 + 1, 2*z3^2 + 4*z3) """ if self.twist_morphism is None and self.derivation is None: return self.base_morphism(x) @@ -151,13 +151,14 @@ def __call__(self, x): else: x = self.domain().coordinate_vector(x) C = self.codomain() + if self.twist_morphism is None: + x_twist = x + else: + x_twist = self.domain()(list(map(self.twist_morphism, x))) if self.side == "left": - v = x * self.matrix() + v = x_twist * self.matrix() else: - v = self.matrix() * x - if self.twist_morphism is not None: - for i in range(len(v)): - v[i] *= self.twist_morphism(x[i]) + v = self.matrix() * x_twist if self.derivation is not None: v += self.domain()(list(map(self.derivation, x))) if not C.is_ambient(): From 1dbb2b791748f77523810e158a91039f0f4e6501 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 08:41:25 -0500 Subject: [PATCH 048/507] Added examples for all pseudomorphism methods --- src/sage/modules/free_module.py | 20 ++- .../modules/free_module_pseudohomspace.py | 10 +- .../modules/free_module_pseudomorphism.py | 130 ++++++++++++------ 3 files changed, 109 insertions(+), 51 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 0530fe2b0f4..6fe6d516124 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3068,12 +3068,18 @@ def hom(self, im_gens, codomain=None, **kwds): def PseudoHom(self, twist=None, codomain=None): r""" - Create the Pseudohomspace corresponding to given twist data. + Create the Pseudo Hom space corresponding to given twist data. + EXAMPLES:: + + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist); PHS + Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z2 of size 5^2 to Vector space of dimension 2 over Finite Field in z2 of size 5^2 + Twisted by the morphism Frobenius endomorphism z2 |--> z2^5 on Finite Field in z2 of size 5^2 """ from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace - return FreeModulePseudoHomspace(self, codomain, twist) + return FreeModulePseudoHomspace(self, codomain, twist=twist) def pseudohom(self, morphism, twist=None, codomain=None, **kwds): r""" @@ -3082,8 +3088,14 @@ def pseudohom(self, morphism, twist=None, codomain=None, **kwds): EXAMPLES:: - sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() - sage: + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2], [0, 1]], twist, side="right"); ph + Free module pseudomorphism defined as left-multiplication by the matrix + [1 2] + [0 1] + twisted by the morphism Frobenius endomorphism z2 |--> z2^5 on Finite Field in z2 of size 5^2 + Domain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 + Codomain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism from sage.structure.element import is_Matrix diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index f95aeb2fb9d..790b8002d75 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -45,15 +45,21 @@ def __init__(self, X, Y, twist=None): self._codomain = X if Y is not None: self._codomain = Y + super().__init__(self._domain, self._codomain, category=None) self.base_homspace = self._domain.Hom(self._codomain) + self.twist = twist + self.twist_morphism = None + self.derivation = None if twist is None: return if (twist.domain() is not self.domain().coordinate_ring() or twist.codomain() is not self.codomain().coordinate_ring()): raise TypeError("twisting morphism domain/codomain do not match\ coordinate rings of the modules") - elif isinstance(twist, Morphism) or isinstance(twist, RingDerivation): - self.twist = twist + elif isinstance(twist, Morphism): + self.twist_morphism = twist + elif isinstance(twist, RingDerivation): + self.derivation = twist else: raise TypeError("twist is not a ring morphism or derivation") diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index d707dc44d6a..8055867d0cb 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -106,6 +106,7 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" """ from sage.structure.element import is_Matrix + Morphism.__init__(self, domain.PseudoHom(twist, codomain)) if is_Matrix(base_morphism): self.base_morphism = domain.hom(base_morphism, codomain) elif isinstance(base_morphism, Morphism): @@ -125,54 +126,58 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" self.derivation = None self.side = side - def __call__(self, x): + def _call_(self, x): r""" Return the result of applying a pseudomorphism to an element of the free module. TESTS:: - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) - sage: ph(e) - (2*z3^2 + 3*z3 + 2, z3^2 + 2*z3 + 1, 2*z3^2 + 4*z3) + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, Fq.gen(), 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) + sage: ph(e) + (z3^2 + 6*z3 + 2, z3^2 + 2*z3 + 1, 2*z3^2 + 4*z3) """ if self.twist_morphism is None and self.derivation is None: return self.base_morphism(x) + if self.domain().is_ambient(): + x = x.element() else: - try: - if parent(x) is not self.domain(): - x = self.domain()(x) - except TypeError: - raise TypeError("%s must be coercible into %s" % (x,self.domain())) - if self.domain().is_ambient(): - x = x.element() - else: - x = self.domain().coordinate_vector(x) - C = self.codomain() - if self.twist_morphism is None: - x_twist = x - else: - x_twist = self.domain()(list(map(self.twist_morphism, x))) - if self.side == "left": - v = x_twist * self.matrix() - else: - v = self.matrix() * x_twist - if self.derivation is not None: - v += self.domain()(list(map(self.derivation, x))) - if not C.is_ambient(): - v = C.linear_combination_of_basis(v) - return C._element_constructor_(v) + x = self.domain().coordinate_vector(x) + C = self.codomain() + if self.twist_morphism is None: + x_twist = x + else: + x_twist = self.domain()(list(map(self.twist_morphism, x))) + if self.side == "left": + v = x_twist * self.matrix() + else: + v = self.matrix() * x_twist + if self.derivation is not None: + v += self.domain()(list(map(self.derivation, x))) + if not C.is_ambient(): + v = C.linear_combination_of_basis(v) + return C._element_constructor_(v) def __repr__(self): r""" Return the string representation of a pseudomorphism. TESTS:: + + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1,1,1],[2,2,2],[3,3,3]], frob); ph + Free module pseudomorphism defined by the matrix + [1 1 1] + [2 2 2] + [3 3 3] + twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 + Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 """ - r = "Free module pseudomorphism defined {}by the \ - matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" + r = "Free module pseudomorphism defined {}by the "\ + "matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" act = "" if self.side == "right": act = "as left-multiplication " @@ -187,39 +192,74 @@ def __repr__(self): return r.format(act, self.matrix(), morph, deriv, \ self.domain(), self.codomain()) - def domain(self): - r""" - Return the domain of the pseudomorphism. - """ - return self.base_morphism.domain() - - def codomain(self): - r""" - Return the codomain of the pseudomorphism. - """ - return self.base_morphism.codomain() - def matrix(self): r""" Return the underlying matrix of a pseudomorphism. + + If a pseudomorphism `f` on free module `M` has matrix m acting on + the left on elements `v \in M`, with twisting morphism `\theta`. + Then we have + + `f(v) = m*\theta(v)` + + where `\theta` acts of the coefficients of `v` in terms of the basis + for `m`. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) + sage: ph.matrix() + [1 2 3] + [0 1 1] + [2 1 1] + sage: ph(e) == ph.matrix()*vector([frob(c) for c in e]) + True """ return self.base_morphism.matrix() - def base_morphism(self): + def _base_morphism(self): r""" Return the underlying morphism of a pseudomorphism. This is an element of the Hom space of the free module. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: ph._base_morphism() + Vector space morphism represented by the matrix: + [1 2 3] + [0 1 1] + [2 1 1] + Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 """ return self.base_morphism def twisting_morphism(self): r""" Return the twisting homomorphism of the pseudomorphism. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: ph.twisting_morphism() + Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 """ return self.twist_morphism - def derivation(self): + def twisting_derivation(self): r""" Return the twisting derivation of the pseudomorphism. + + EXAMPLES:: + + sage: P. = ZZ[]; deriv = P.derivation(); M = P^2 + sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right") + sage: f.twisting_derivation() + d/dx """ return self.derivation From 120d7a119d73c1a552e89c79b549700ce7472beb Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 09:10:52 -0500 Subject: [PATCH 049/507] Completed documentation for pseudomorphism and pseudohomspace --- src/sage/modules/free_module.py | 2 +- .../modules/free_module_pseudohomspace.py | 50 ++++++++++++++++++- .../modules/free_module_pseudomorphism.py | 1 - 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 6fe6d516124..ece22120141 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3071,7 +3071,7 @@ def PseudoHom(self, twist=None, codomain=None): Create the Pseudo Hom space corresponding to given twist data. EXAMPLES:: - + sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist); PHS Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z2 of size 5^2 to Vector space of dimension 2 over Finite Field in z2 of size 5^2 diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 790b8002d75..ecd644f642e 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -1,4 +1,11 @@ +""" +Space of Pseudomorphisms of free modules +AUTHORS: + + - Yossef Musleh (2024-02): initial version + +""" # **************************************************************************** # Copyright (C) 2024 Yossef Musleh # @@ -72,7 +79,7 @@ def __call__(self, A, **kwds): sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) sage: h = PHS([[1, 2], [1, 1]]); h - Free module pseudomorphism defined by the matrix + Free module pseudomorphism defined by the matrix [1 2] [1 1] twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 @@ -114,6 +121,10 @@ def __repr__(self): EXAMPLE:: + sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() + sage: PHS = M.PseudoHom(frob); PHS + Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z3 of size 7^3 to Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 """ r = "Set of Pseudomorphisms from {} to {} {} {}" morph = "" @@ -141,11 +152,47 @@ def zero(self): return self.base_homspace.zero() def _matrix_space(self): + r""" + Return the full matrix space of the underlying morphisms. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() + sage: PHS = M.PseudoHom(frob) + sage: PHS._matrix_space() + Full MatrixSpace of 2 by 2 dense matrices over Finite Field in z3 of size 7^3 + """ return self.base_homspace._matrix_space() def basis(self, side="left"): r""" Return a basis for the underlying matrix space. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() + sage: PHS = M.PseudoHom(frob) + sage: PHS.basis() + (Vector space morphism represented by the matrix: + [1 0] + [0 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Vector space morphism represented by the matrix: + [0 1] + [0 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Vector space morphism represented by the matrix: + [0 0] + [1 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Vector space morphism represented by the matrix: + [0 0] + [0 1] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3) """ return self.base_homspace.basis(side) @@ -160,6 +207,5 @@ def identity(self): sage: PHS.identity().matrix() [1 0] [0 1] - """ return self.base_homspace.identity() diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 8055867d0cb..00c56e21e1f 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -6,7 +6,6 @@ - Yossef Musleh (2024-02): initial version """ - #################################################################################### # Copyright (C) 2024 Yossef Musleh # From 957e414dcc863935245007fafa2f4b97ecaa6127 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 12:19:17 -0500 Subject: [PATCH 050/507] Wrap identity/zero morphism --- src/sage/modules/free_module_pseudohomspace.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index ecd644f642e..ffca84bafbb 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -145,11 +145,15 @@ def zero(self): sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) - sage: PHS.zero().matrix() + sage: PHS.zero() + Free module pseudomorphism defined by the matrix [0 0] [0 0] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - return self.base_homspace.zero() + return self(self.base_homspace.zero()) def _matrix_space(self): r""" @@ -204,8 +208,12 @@ def identity(self): sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) - sage: PHS.identity().matrix() + sage: PHS.identity() + Free module pseudomorphism defined by the matrix [1 0] [0 1] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - return self.base_homspace.identity() + return self(self.base_homspace.identity()) From 051b0f3acd18b929c33620b8d7672445c87a35a5 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 12:27:45 -0500 Subject: [PATCH 051/507] Removed intermediate morphism --- .../modules/free_module_pseudomorphism.py | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 00c56e21e1f..de442ff670f 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -107,12 +107,12 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" from sage.structure.element import is_Matrix Morphism.__init__(self, domain.PseudoHom(twist, codomain)) if is_Matrix(base_morphism): - self.base_morphism = domain.hom(base_morphism, codomain) + self._base_matrix = base_morphism elif isinstance(base_morphism, Morphism): - self.base_morphism = base_morphism + self._base_matrix = base_morphism.matrix() else: - self.base_morphism = domain.hom(matrix(domain.coordinate_ring(), \ - base_morphism), codomain) + self._base_matrix = matrix(domain.coordinate_ring(), \ + base_morphism) self.derivation = None self.twist_morphism = None if isinstance(twist, Morphism): @@ -138,8 +138,6 @@ def _call_(self, x): sage: ph(e) (z3^2 + 6*z3 + 2, z3^2 + 2*z3 + 1, 2*z3^2 + 4*z3) """ - if self.twist_morphism is None and self.derivation is None: - return self.base_morphism(x) if self.domain().is_ambient(): x = x.element() else: @@ -150,9 +148,9 @@ def _call_(self, x): else: x_twist = self.domain()(list(map(self.twist_morphism, x))) if self.side == "left": - v = x_twist * self.matrix() + v = x_twist * self._base_matrix else: - v = self.matrix() * x_twist + v = self._base_matrix * x_twist if self.derivation is not None: v += self.domain()(list(map(self.derivation, x))) if not C.is_ambient(): @@ -216,26 +214,7 @@ def matrix(self): sage: ph(e) == ph.matrix()*vector([frob(c) for c in e]) True """ - return self.base_morphism.matrix() - - def _base_morphism(self): - r""" - Return the underlying morphism of a pseudomorphism. This is an element - of the Hom space of the free module. - - EXAMPLES:: - - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: ph._base_morphism() - Vector space morphism represented by the matrix: - [1 2 3] - [0 1 1] - [2 1 1] - Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 - """ - return self.base_morphism + return self._base_matrix def twisting_morphism(self): r""" From 11dd611bdc5645d76a7dea37d84c84a37c503cd1 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Thu, 1 Feb 2024 12:43:01 -0500 Subject: [PATCH 052/507] Improve documentation --- .../modules/free_module_pseudohomspace.py | 20 +++++++++---------- .../modules/free_module_pseudomorphism.py | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index ffca84bafbb..f696be346f6 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -99,27 +99,27 @@ def __call__(self, A, **kwds): else: v = [C(a) for a in A] if side == "right": - A = matrix([C.coordinates(a) for a in v], \ + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()).transpose() else: - A = matrix([C.coordinates(a) for a in v], \ + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) except TypeError: pass - if not self.codomain().base_ring().has_coerce_map_from(\ + if not self.codomain().base_ring().has_coerce_map_from( self.domain().base_ring()) and not A.is_zero(): - raise TypeError("nontrivial morphisms require a coercion map \ - from the base ring of the domain to the base ring of the \ - codomain") - return free_module_pseudomorphism.FreeModulePseudoMorphism(\ - self.domain(), A, twist=self.twist, \ - codomain = self.codomain()) + raise TypeError("nontrivial morphisms require a coercion map" + "from the base ring of the domain to the base ring of the" + "codomain") + return free_module_pseudomorphism.FreeModulePseudoMorphism( + self.domain(), A, twist=self.twist, + codomain=self.codomain()) def __repr__(self): r""" Returns the string representation of the pseudomorphism space. - EXAMPLE:: + EXAMPLES:: sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() sage: PHS = M.PseudoHom(frob); PHS diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index de442ff670f..b5d894a7ac2 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -83,18 +83,18 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" Constructs a pseudomorphism of free modules. INPUT: - - ``domain`` - the domain of the pseudomorphism; a free module + - ``domain`` - the domain of the pseudomorphism; a free module - ``base_morphism`` - either a morphism or a matrix defining a morphism - - ``twist`` - a twisting morphism, this is either a morphism or + - ``twist`` - a twisting morphism, this is either a morphism or a derivation (default: None) - ``codomain`` - the codomain of the pseudomorphism; a free module (default: None) - - side -- side of the vectors acted on by the matrix + - side -- side of the vectors acted on by the matrix (default: ``"left"``) EXAMPLES:: @@ -111,7 +111,7 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" elif isinstance(base_morphism, Morphism): self._base_matrix = base_morphism.matrix() else: - self._base_matrix = matrix(domain.coordinate_ring(), \ + self._base_matrix = matrix(domain.coordinate_ring(), base_morphism) self.derivation = None self.twist_morphism = None @@ -186,7 +186,7 @@ def __repr__(self): if self.derivation is not None: deriv = "\ntwisted by the derivation {}" deriv = deriv.format(self.derivation.__repr__()) - return r.format(act, self.matrix(), morph, deriv, \ + return r.format(act, self.matrix(), morph, deriv, self.domain(), self.codomain()) def matrix(self): From 0cc3eaf874ff53e07c7f9d24e221a23796ea3ec5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 1 Feb 2024 21:31:02 +0100 Subject: [PATCH 053/507] provide a construction functor --- src/sage/combinat/sf/elementary.py | 1 + src/sage/combinat/sf/homogeneous.py | 1 + src/sage/combinat/sf/macdonald.py | 5 ++ src/sage/combinat/sf/monomial.py | 1 + src/sage/combinat/sf/powersum.py | 1 + src/sage/combinat/sf/schur.py | 1 + src/sage/combinat/sf/sf.py | 1 - src/sage/combinat/sf/sfa.py | 78 ++++++++++++++++++++++++++++- 8 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/sf/elementary.py b/src/sage/combinat/sf/elementary.py index f0942314323..878cfe4f73f 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -50,6 +50,7 @@ def __init__(self, Sym): sage: TestSuite(e).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(e).run(elements = [e[1,1]+e[2], e[1]+2*e[1,1]]) """ + self._descriptor = (("elementary",),) classical.SymmetricFunctionAlgebra_classical.__init__(self, Sym, "elementary", 'e') def _dual_basis_default(self): diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index 29cf294ea80..d21dd197c58 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -52,6 +52,7 @@ def __init__(self, Sym): sage: TestSuite(h).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(h).run(elements = [h[1,1]+h[2], h[1]+2*h[1,1]]) """ + self._descriptor = (("homogeneous",),) classical.SymmetricFunctionAlgebra_classical.__init__(self, Sym, "homogeneous", 'h') def _dual_basis_default(self): diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 1358c5779df..a467ad29eef 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -1082,6 +1082,7 @@ def __init__(self, macdonald): sage: TestSuite(Q).run(elements = [Q.t*Q[1,1]+Q.q*Q[2], Q[1]+(Q.q+Q.t)*Q[1,1]]) # long time (depends on previous) """ MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("Q",)) self._J = macdonald.J() self._P = macdonald.P() @@ -1118,6 +1119,7 @@ def __init__(self, macdonald): self._self_to_s_cache = _j_to_s_cache self._s_to_self_cache = _s_to_j_cache MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("J",)) def _s_cache(self, n): r""" @@ -1218,6 +1220,7 @@ def __init__(self, macdonald): """ MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("H",)) self._m = self._sym.m() self._Lmunu = macdonald.Ht()._Lmunu if not self.t: @@ -1440,6 +1443,7 @@ def __init__(self, macdonald): """ MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("Ht",)) self._self_to_m_cache = _ht_to_m_cache self._m = self._sym.m() category = ModulesWithBasis(self.base_ring()) @@ -1735,6 +1739,7 @@ def __init__(self, macdonald): """ MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("S",)) self._s = macdonald._s self._self_to_s_cache = _S_to_s_cache self._s_to_self_cache = _s_to_S_cache diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 583008830af..18d25b1af25 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -45,6 +45,7 @@ def __init__(self, Sym): sage: TestSuite(m).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(m).run(elements = [m[1,1]+m[2], m[1]+2*m[1,1]]) """ + self._descriptor = (("monomial",),) classical.SymmetricFunctionAlgebra_classical.__init__(self, Sym, "monomial", 'm') def _dual_basis_default(self): diff --git a/src/sage/combinat/sf/powersum.py b/src/sage/combinat/sf/powersum.py index 8d7f744e75f..ea7ca6bcc1f 100644 --- a/src/sage/combinat/sf/powersum.py +++ b/src/sage/combinat/sf/powersum.py @@ -44,6 +44,7 @@ def __init__(self, Sym): sage: TestSuite(p).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(p).run(elements = [p[1,1]+p[2], p[1]+2*p[1,1]]) """ + self._descriptor = (("powersum",),) classical.SymmetricFunctionAlgebra_classical.__init__(self, Sym, "powersum", 'p') def coproduct_on_generators(self, i): diff --git a/src/sage/combinat/sf/schur.py b/src/sage/combinat/sf/schur.py index 40e1de75812..672bac86c91 100644 --- a/src/sage/combinat/sf/schur.py +++ b/src/sage/combinat/sf/schur.py @@ -48,6 +48,7 @@ def __init__(self, Sym): sage: TestSuite(s).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(s).run(elements = [s[1,1]+s[2], s[1]+2*s[1,1]]) """ + self._descriptor = (("schur",),) classical.SymmetricFunctionAlgebra_classical.__init__(self, Sym, "Schur", 's') def _dual_basis_default(self): diff --git a/src/sage/combinat/sf/sf.py b/src/sage/combinat/sf/sf.py index ad86bfd6c7f..5dc923f8df2 100644 --- a/src/sage/combinat/sf/sf.py +++ b/src/sage/combinat/sf/sf.py @@ -845,7 +845,6 @@ class function on the symmetric group where the elements - Devise a mechanism so that pickling bases of symmetric functions pickles the coercions which have a cache. """ - def __init__(self, R): r""" Initialization of ``self``. diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 2b42ebb7be6..5d985031f7e 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -1786,7 +1786,6 @@ class SymmetricFunctionAlgebra_generic(CombinatorialFreeModule): sage: s(m([2,1])) -2*s[1, 1, 1] + s[2, 1] """ - def __init__(self, Sym, basis_name=None, prefix=None, graded=True): r""" Initializes the symmetric function algebra. @@ -3013,6 +3012,21 @@ def coproduct_by_coercion(self, elt): return self.tensor_square().sum(coeff * tensor([self(s[x]), self(s[y])]) for ((x,y), coeff) in s(elt).coproduct()) + def construction(self): + """ + Return a pair ``(F, R)``, where ``F`` is a + :class:`SymmetricFunctionsFunctor` and `R` is a ring, such + that ``F(R)`` returns ``self``. + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: F, R = s.construction() + sage: F(QQ) + Symmetric Functions over Rational Field in the Schur basis + """ + return SymmetricFunctionsFunctor(self._descriptor), self.base_ring() + class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): r""" @@ -3033,7 +3047,6 @@ class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): m[1, 1, 1] + m[2, 1] + m[3] sage: m.set_print_style('lex') """ - def factor(self): """ Return the factorization of this symmetric function. @@ -6375,6 +6388,67 @@ def exponential_specialization(self, t=None, q=1): SymmetricFunctionAlgebra_generic.Element = SymmetricFunctionAlgebra_generic_Element +from sage.categories.pushout import ConstructionFunctor +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.functor import Functor + +class SymmetricFunctionsFunctor(ConstructionFunctor): + rank = 9 + + def __init__(self, descriptor): + self._descriptor = descriptor + Functor.__init__(self, CommutativeRings(), CommutativeRings()) + + def _apply_functor(self, R): + """ + Apply the functor to an object of ``self``'s domain. + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: F, R = s.construction() + sage: F(QQ) + Symmetric Functions over Rational Field in the Schur basis + """ + from sage.combinat.sf.sf import SymmetricFunctions + S = SymmetricFunctions(R) + for method, *params in self._descriptor: + if params: + assert len(params) == 1 + S = S.__getattribute__(method)(**params[0]) + else: + S = S.__getattribute__(method)() + return S + + def _apply_functor_to_morphism(self, f): + """ + Apply the functor ``self`` to the ring morphism `f`. + + """ + dom = self(f.domain()) + codom = self(f.codomain()) + + def action(x): + return codom._from_dict({a: f(b) + for a, b in x.monomial_coefficients().items()}) + return dom.module_morphism(function=action, codomain=codom) + + def __eq__(self, other): + if not isinstance(other, SymmetricFunctionsFunctor): + return False + return self.vars == other.vars + + def _repr_(self): + """ + TESTS:: + + sage: R. = ZZ[] + sage: H = SymmetricFunctions(R).macdonald().H() + sage: F, R = H.construction() + sage: F + (('macdonald', {'q': q, 't': t}), ('H',)) + """ + return repr(self._descriptor) ################### def _lmax(x): From 6e95539541983ff74871d87400b0b5de5254c5fb Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 2 Feb 2024 22:52:08 +0100 Subject: [PATCH 054/507] Moved code in `.is_isomorphic`, edited some docstrings --- .../algebras/quatalg/quaternion_algebra.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 04fd2e866d7..71ee1022336 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1045,7 +1045,7 @@ def is_definite(self): """ Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the unique Archimedean place of its base field QQ. This is the case if and only if both - invariants of ``self`` are negative, see Exercise 2.4(c) in [Voi2021]_. + invariants of ``self`` are negative; see Exercise 2.4(c) in [Voi2021]_. EXAMPLES:: @@ -1323,10 +1323,10 @@ def is_isomorphic(self, A) -> bool: if F != A.base_ring(): raise ValueError("both quaternion algebras must be defined over the same base ring") - try: - if is_RationalField(F): - return self.ramified_places(inf=False) == A.ramified_places(inf=False) + if is_RationalField(F): + return self.ramified_places(inf=False) == A.ramified_places(inf=False) + try: ram_self = self.ramified_places(inf=True) ram_A = A.ramified_places(inf=True) return set(ram_self[0]) == set(ram_A[0]) and ram_self[1] == ram_A[1] @@ -2021,7 +2021,8 @@ def is_maximal(self): r""" Check whether the order of ``self`` is maximal in the ambient quaternion algebra. - Only works in quaternion algebras over number fields + Only implemented for quaternion algebras over number fields; for reference, + see Theorem 15.5.5 in [Voi2021]_. OUTPUT: Boolean @@ -3268,12 +3269,12 @@ def cyclic_right_subideals(self, p, alpha=None): def is_integral(self): r""" - Check if a quaternion fractional ideal is integral. An ideal in a quaternion algebra is - said integral if it is contained in its left order. If the left order is already defined it just - check the definition, otherwise it uses one of the alternative definition of Lemma 16.2.8 of - [Voi2021]_. + Checks whether a quaternion fractional ideal is integral. An ideal in a quaternion algebra + is integral if and only if it is contained in its left order. If the left order is already + defined this method just checks this definition, otherwise it uses one of the alternative + definitions from Lemma 16.2.8 of [Voi2021]_. - OUTPUT: a boolean. + OUTPUT: A boolean. EXAMPLES:: From 693c051110c5783433217ad45beed457bb1fdf86 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 6 Feb 2024 11:04:26 +0100 Subject: [PATCH 055/507] more constructions --- src/sage/combinat/sf/character.py | 2 ++ src/sage/combinat/sf/hall_littlewood.py | 10 +++++++++- src/sage/combinat/sf/hecke.py | 8 ++++++-- src/sage/combinat/sf/jack.py | 12 ++++++++++-- src/sage/combinat/sf/macdonald.py | 11 ++++++++--- src/sage/combinat/sf/orthogonal.py | 2 +- src/sage/combinat/sf/symplectic.py | 1 + src/sage/combinat/sf/witt.py | 1 + 8 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/sf/character.py b/src/sage/combinat/sf/character.py index 3bd8ab5e0a1..827e7ad33ae 100644 --- a/src/sage/combinat/sf/character.py +++ b/src/sage/combinat/sf/character.py @@ -224,6 +224,7 @@ def __init__(self, Sym, pfix): SFA_generic.__init__(self, Sym, basis_name="induced trivial symmetric group character", prefix=pfix, graded=False) + self._descriptor = (("ht",),) self._other = Sym.complete() self._p = Sym.powersum() @@ -451,6 +452,7 @@ def __init__(self, Sym, pfix): SFA_generic.__init__(self, Sym, basis_name="irreducible symmetric group character", prefix=pfix, graded=False) + self._descriptor = (("st",),) self._other = Sym.Schur() self._p = Sym.powersum() diff --git a/src/sage/combinat/sf/hall_littlewood.py b/src/sage/combinat/sf/hall_littlewood.py index c3376537a35..cf19404b4eb 100644 --- a/src/sage/combinat/sf/hall_littlewood.py +++ b/src/sage/combinat/sf/hall_littlewood.py @@ -77,7 +77,11 @@ def __repr__(self): """ return self._name + " over %s" % self._sym.base_ring() - def __init__(self, Sym, t='t'): + @staticmethod + def __classcall__(cls, Sym, t='t'): + return super().__classcall__(cls, Sym, Sym.base_ring()(t)) + + def __init__(self, Sym, t): """ Initialize ``self``. @@ -708,6 +712,7 @@ def __init__(self, hall_littlewood): sage: TestSuite(P).run(elements = [P.t*P[1,1]+P[2], P[1]+(1+P.t)*P[1,1]]) """ HallLittlewood_generic.__init__(self, hall_littlewood) + self._descriptor = (("hall_littlewood", {"t": self.t}), ("P",)) self._self_to_s_cache = p_to_s_cache self._s_to_self_cache = s_to_p_cache @@ -846,6 +851,7 @@ def __init__(self, hall_littlewood): (1/(t^2-1))*HLQ[1, 1] - (1/(t-1))*HLQ[2] """ HallLittlewood_generic.__init__(self, hall_littlewood) + self._descriptor = (("hall_littlewood", {"t": self.t}), ("Q",)) self._P = self._hall_littlewood.P() # temporary until Hom(GradedHopfAlgebrasWithBasis work better) @@ -942,6 +948,8 @@ def __init__(self, hall_littlewood): [ 0 0 1] """ HallLittlewood_generic.__init__(self, hall_littlewood) + self._descriptor = (("hall_littlewood", {"t": self.t}), ("Qp",)) + self._self_to_s_cache = qp_to_s_cache self._s_to_self_cache = s_to_qp_cache diff --git a/src/sage/combinat/sf/hecke.py b/src/sage/combinat/sf/hecke.py index 5cea11ccb2c..f2071d5f4b6 100644 --- a/src/sage/combinat/sf/hecke.py +++ b/src/sage/combinat/sf/hecke.py @@ -121,8 +121,11 @@ class HeckeCharacter(SymmetricFunctionAlgebra_multiplicative): - [Ram1991]_ - [RR1997]_ """ + @staticmethod + def __classcall__(cls, Sym, q='q'): + return super().__classcall__(cls, Sym, Sym.base_ring()(q)) - def __init__(self, sym, q='q'): + def __init__(self, sym, q): r""" Initialize ``self``. @@ -156,10 +159,11 @@ def __init__(self, sym, q='q'): ....: for mu in Partitions(n)) True """ - self.q = sym.base_ring()(q) + self.q = q SymmetricFunctionAlgebra_multiplicative.__init__(self, sym, basis_name="Hecke character with q={}".format(self.q), prefix="qbar") + self._descriptor = (("qbar", {"q": self.q}),) self._p = sym.power() # temporary until Hom(GradedHopfAlgebrasWithBasis work better) diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index a7a0fec6186..601f6468337 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -50,8 +50,11 @@ class Jack(UniqueRepresentation): + @staticmethod + def __classcall__(cls, Sym, t='t'): + return super().__classcall__(cls, Sym, Sym.base_ring()(t)) - def __init__(self, Sym, t='t'): + def __init__(self, Sym, t): r""" The family of Jack symmetric functions including the `P`, `Q`, `J`, `Qp` bases. The default parameter is ``t``. @@ -70,7 +73,7 @@ def __init__(self, Sym, t='t'): Jack polynomials with t=1 over Rational Field """ self._sym = Sym - self.t = Sym.base_ring()(t) + self.t = t self._name_suffix = "" if str(t) != 't': self._name_suffix += " with t=%s" % t @@ -874,6 +877,7 @@ def __init__(self, jack): self._m_to_self_cache = m_to_p_cache self._self_to_m_cache = p_to_m_cache JackPolynomials_generic.__init__(self, jack) + self._descriptor = (("jack", {"t": self.t}), ("P",)) def _m_cache(self, n): r""" @@ -1076,6 +1080,7 @@ def __init__(self, jack): self._name = "Jack polynomials in the J basis" self._prefix = "JackJ" JackPolynomials_generic.__init__(self, jack) + self._descriptor = (("jack", {"t": self.t}), ("J",)) # Should be shared with _q (and possibly other bases in Macdo/HL) as BasesByRenormalization self._P = self._jack.P() @@ -1112,6 +1117,7 @@ def __init__(self, jack): self._name = "Jack polynomials in the Q basis" self._prefix = "JackQ" JackPolynomials_generic.__init__(self, jack) + self._descriptor = (("jack", {"t": self.t}), ("Q",)) # Should be shared with _j (and possibly other bases in Macdo/HL) as BasesByRenormalization self._P = self._jack.P() @@ -1150,6 +1156,7 @@ def __init__(self, jack): self._name = "Jack polynomials in the Qp basis" self._prefix = "JackQp" JackPolynomials_generic.__init__(self, jack) + self._descriptor = (("jack", {"t": self.t}), ("Qp",)) self._P = self._jack.P() self._self_to_h_cache = qp_to_h_cache self._h_to_self_cache = h_to_qp_cache @@ -1354,6 +1361,7 @@ def __init__(self, Sym): #self._self_to_m_cache = {} and we don't need to compute it separately for zonals sfa.SymmetricFunctionAlgebra_generic.__init__(self, self._sym, prefix="Z", basis_name="zonal") + self._descriptor = (("zonal",),) category = sage.categories.all.ModulesWithBasis(self._sym.base_ring()) self .register_coercion(SetMorphism(Hom(self._P, self, category), self.sum_of_terms)) self._P.register_coercion(SetMorphism(Hom(self, self._P, category), self._P.sum_of_terms)) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index a467ad29eef..03ec0bcc5a2 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -97,7 +97,11 @@ def __repr__(self): """ return self._name - def __init__(self, Sym, q='q', t='t'): + @staticmethod + def __classcall__(cls, Sym, q='q', t='t'): + return super().__classcall__(cls, Sym, Sym.base_ring()(q), Sym.base_ring()(t)) + + def __init__(self, Sym, q, t): r""" Macdonald Symmetric functions including `P`, `Q`, `J`, `H`, `Ht` bases also including the S basis which is the plethystic transformation @@ -119,8 +123,8 @@ def __init__(self, Sym, q='q', t='t'): """ self._sym = Sym self._s = Sym.s() - self.q = Sym.base_ring()(q) - self.t = Sym.base_ring()(t) + self.q = q + self.t = t self._name_suffix = "" if str(q) != 'q': self._name_suffix += " with q=%s" % q @@ -1012,6 +1016,7 @@ def __init__(self, macdonald): sage: TestSuite(P).run(elements = [P.t*P[1,1]+P.q*P[2], P[1]+(P.q+P.t)*P[1,1]]) # long time (depends on previous) """ MacdonaldPolynomials_generic.__init__(self, macdonald) + self._descriptor = (("macdonald", {"q": self.q, "t": self.t}), ("P",)) self._J = macdonald.J() # temporary until Hom(GradedHopfAlgebrasWithBasis work better) diff --git a/src/sage/combinat/sf/orthogonal.py b/src/sage/combinat/sf/orthogonal.py index 3ab5f56debc..b6c3576a557 100644 --- a/src/sage/combinat/sf/orthogonal.py +++ b/src/sage/combinat/sf/orthogonal.py @@ -170,7 +170,7 @@ def __init__(self, Sym): """ sfa.SymmetricFunctionAlgebra_generic.__init__(self, Sym, "orthogonal", 'o', graded=False) - + self._descriptor = (("o",),) # We make a strong reference since we use it for our computations # and so we can define the coercion below (only codomains have # strong references) diff --git a/src/sage/combinat/sf/symplectic.py b/src/sage/combinat/sf/symplectic.py index f6db1782489..a6eebc438af 100644 --- a/src/sage/combinat/sf/symplectic.py +++ b/src/sage/combinat/sf/symplectic.py @@ -178,6 +178,7 @@ def __init__(self, Sym): """ sfa.SymmetricFunctionAlgebra_generic.__init__(self, Sym, "symplectic", 'sp', graded=False) + self._descriptor = (("sp",),) # We make a strong reference since we use it for our computations # and so we can define the coercion below (only codomains have diff --git a/src/sage/combinat/sf/witt.py b/src/sage/combinat/sf/witt.py index 138b2647826..1f5ed292bd0 100644 --- a/src/sage/combinat/sf/witt.py +++ b/src/sage/combinat/sf/witt.py @@ -407,6 +407,7 @@ def __init__(self, Sym, coerce_h=True, coerce_e=False, coerce_p=False): sage: TestSuite(w).run(skip=['_test_associativity', '_test_distributivity', '_test_prod']) sage: TestSuite(w).run(elements = [w[1,1]+w[2], w[1]+2*w[1,1]]) """ + self._descriptor = (("w",),) self._coerce_h = coerce_h self._coerce_e = coerce_e self._coerce_p = coerce_p From 7d7ccebe07f2b81049b619f3e4b493f45195cca5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 6 Feb 2024 14:08:21 +0100 Subject: [PATCH 056/507] fix oversight in factorization of symmetric function: unit should be in the base ring --- src/sage/combinat/sf/sfa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 8a73014fe6b..0fd2d890b88 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -3103,7 +3103,7 @@ def factor(self): poly = _to_polynomials([self], self.base_ring())[0] factors = poly.factor() - unit = factors.unit() + unit = self.base_ring()(factors.unit()) if factors.universe() == self.base_ring(): return Factorization(factors, unit=unit) factors = [(_from_polynomial(factor, M), exponent) From 7bd1c5b32534a5ae4a520bc1a5c99dcf05a0d3cf Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 6 Feb 2024 14:31:39 +0100 Subject: [PATCH 057/507] skip construction for dual and orthotriang --- src/sage/combinat/sf/dual.py | 5 ++++- src/sage/combinat/sf/orthotriang.py | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index 6a68eb7dfe6..58b69743c7b 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -74,7 +74,7 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N sage: e = SymmetricFunctions(QQ).e() sage: f = e.dual_basis(prefix = "m", basis_name="Forgotten symmetric functions"); f Symmetric Functions over Rational Field in the Forgotten symmetric functions basis - sage: TestSuite(f).run(elements = [f[1,1]+2*f[2], f[1]+3*f[1,1]]) + sage: TestSuite(f).run(skip='_test_construction', elements = [f[1,1]+2*f[2], f[1]+3*f[1,1]]) sage: TestSuite(f).run() # long time (11s on sage.math, 2011) This class defines canonical coercions between ``self`` and @@ -157,6 +157,9 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N self.register_coercion(SetMorphism(Hom(self._dual_basis, self, category), self._dual_to_self)) self._dual_basis.register_coercion(SetMorphism(Hom(self, self._dual_basis, category), self._self_to_dual)) + def construction(self): + raise NotImplementedError + def _dual_to_self(self, x): """ Coerce an element of the dual of ``self`` canonically into ``self``. diff --git a/src/sage/combinat/sf/orthotriang.py b/src/sage/combinat/sf/orthotriang.py index 2e1650e57a7..5f2f01b7f6c 100644 --- a/src/sage/combinat/sf/orthotriang.py +++ b/src/sage/combinat/sf/orthotriang.py @@ -84,8 +84,8 @@ def __init__(self, Sym, base, scalar, prefix, basis_name, leading_coeff=None): TESTS:: - sage: TestSuite(s).run(elements = [s[1,1]+2*s[2], s[1]+3*s[1,1]]) - sage: TestSuite(s).run(skip = ["_test_associativity", "_test_prod"]) # long time (7s on sage.math, 2011) + sage: TestSuite(s).run(skip='_test_construction', elements = [s[1,1]+2*s[2], s[1]+3*s[1,1]]) + sage: TestSuite(s).run(skip = ["_test_associativity", "_test_prod", '_test_construction']) # long time (7s on sage.math, 2011) Note: ``s.an_element()`` is of degree 4; so we skip ``_test_associativity`` and ``_test_prod`` which involve @@ -102,6 +102,9 @@ def __init__(self, Sym, base, scalar, prefix, basis_name, leading_coeff=None): self.register_coercion(SetMorphism(Hom(base, self), self._base_to_self)) base.register_coercion(SetMorphism(Hom(self, base), self._self_to_base)) + def construction(self): + raise NotImplementedError + def _base_to_self(self, x): """ Coerce a symmetric function in base ``x`` into ``self``. From c87409e13afbe222f9fa75e240e34be30f3e9137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 24 Jan 2024 18:10:11 +0100 Subject: [PATCH 058/507] use Parent in quotient rings too --- src/sage/rings/quotient_ring.py | 36 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index a6a0a8ce7f8..5b8013dbf26 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -110,17 +110,20 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** import sage.interfaces.abc import sage.misc.latex as latex import sage.structure.parent_gens + +from sage.structure.parent import Parent from sage.categories.commutative_rings import CommutativeRings from sage.categories.rings import Rings from sage.misc.cachefunc import cached_method from sage.rings import ideal, quotient_ring_element, ring from sage.structure.category_object import normalize_names from sage.structure.richcmp import richcmp, richcmp_method +from sage.structure.category_object import check_default_category _Rings = Rings() _CommRings = CommutativeRings() @@ -334,7 +337,7 @@ def QuotientRing(R, I, names=None, **kwds): I_lift = S.ideal(G) J = R.defining_ideal() if S == ZZ: - return Integers((I_lift+J).gen(), **kwds) + return Integers((I_lift + J).gen(), **kwds) return R.__class__(S, I_lift + J, names=names) if R in _CommRings: return QuotientRing_generic(R, I, names, **kwds) @@ -372,16 +375,15 @@ def is_QuotientRing(x): _RingsQuotients = _Rings.Quotients() _CommutativeRingsQuotients = _CommRings.Quotients() -from sage.structure.category_object import check_default_category @richcmp_method -class QuotientRing_nc(ring.Ring, sage.structure.parent_gens.ParentWithGens): +class QuotientRing_nc(Parent): """ The quotient ring of `R` by a twosided ideal `I`. - This class is for rings that do not inherit from - :class:`~sage.rings.ring.CommutativeRing`. + This class is for rings that are not in the category + ``Rings().Commutative()``. EXAMPLES: @@ -489,8 +491,8 @@ def __init__(self, R, I, names, category=None): raise TypeError("The second argument must be an ideal of the given ring, but %s is not" % I) self.__R = R self.__I = I - #sage.structure.parent_gens.ParentWithGens.__init__(self, R.base_ring(), names) - ## + # sage.structure.parent_gens.ParentWithGens.__init__(self, R.base_ring(), names) + # Unfortunately, computing the join of categories, which is done in # check_default_category, is very expensive. # However, we don't just want to use the given category without mixing in @@ -504,10 +506,10 @@ def __init__(self, R, I, names, category=None): except (AttributeError, NotImplementedError): commutative = False if commutative: - category = check_default_category(_CommutativeRingsQuotients,category) + category = check_default_category(_CommutativeRingsQuotients, category) else: - category = check_default_category(_RingsQuotients,category) - ring.Ring.__init__(self, R.base_ring(), names=names, category=category) + category = check_default_category(_RingsQuotients, category) + Parent.__init__(self, base=R.base_ring(), names=names, category=category) # self._populate_coercion_lists_([R]) # we don't want to do this, since subclasses will often implement improved coercion maps. def construction(self): @@ -790,7 +792,7 @@ def lift(self, x=None): return self.lifting_map() return self.lifting_map()(x) - def retract(self,x): + def retract(self, x): """ The image of an element of the cover ring under the quotient map. @@ -1146,7 +1148,7 @@ def _coerce_map_from_(self, R): try: if R.defining_ideal().change_ring(C) <= self.defining_ideal(): return True - except AttributeError: # Not all ideals have a change_ring + except AttributeError: # Not all ideals have a change_ring pass return C.has_coerce_map_from(R) @@ -1246,6 +1248,10 @@ def gen(self, i=0): """ return self(self.__R.gen(i)) + def gens(self) -> tuple: + return tuple(self(self.__R.gen(i)) + for i in range(self.cover_ring().ngens())) + def _singular_(self, singular=None): """ Returns the Singular quotient ring of ``self`` if the base ring is @@ -1310,7 +1316,7 @@ def _singular_init_(self, singular=None): if singular is None: from sage.interfaces.singular import singular self.__R._singular_().set_ring() - self.__singular = singular("%s" % self.__I._singular_().name(),"qring") + self.__singular = singular("%s" % self.__I._singular_().name(), "qring") return self.__singular def _magma_init_(self, magma): @@ -1468,6 +1474,7 @@ def _ideal_class_(self, num_gens): return QuotientRingIdeal_principal return QuotientRingIdeal_generic + class QuotientRingIdeal_generic(ideal.Ideal_generic): r""" Specialized class for quotient-ring ideals. @@ -1517,6 +1524,7 @@ def _contains_(self, other): J = R.cover_ring().ideal(Igens) return other.lift() in J + class QuotientRingIdeal_principal(ideal.Ideal_principal, QuotientRingIdeal_generic): r""" Specialized class for principal quotient-ring ideals. From a7c11be09ae0bd4f840cf14a6530e99494fdd29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 25 Jan 2024 15:10:19 +0100 Subject: [PATCH 059/507] moving ideals to the categories --- src/sage/categories/commutative_rings.py | 32 +++++++ src/sage/categories/rings.py | 80 +++++++++++++++- src/sage/rings/morphism.pyx | 4 +- src/sage/rings/ring.pyx | 117 ----------------------- 4 files changed, 114 insertions(+), 119 deletions(-) diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index 1601ca3d3e3..c2f6c1ba683 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -216,6 +216,38 @@ def over(self, base=None, gen=None, gens=None, name=None, names=None): gens = (gen,) return RingExtension(self, base, gens, names) + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + ring. + + This class can depend on `n`, the number of generators of the ideal. + The default input of `n=0` indicates an unspecified number of generators, + in which case a class that works for any number of generators is returned. + + EXAMPLES:: + + sage: ZZ._ideal_class_() + + sage: RR._ideal_class_() + + sage: R. = GF(5)[] + sage: R._ideal_class_(1) + + sage: S = R.quo(x^3 - y^2) + sage: S._ideal_class_(1) + + sage: S._ideal_class_(2) + + sage: T. = S[] # needs sage.libs.singular + sage: T._ideal_class_(5) # needs sage.libs.singular + + sage: T._ideal_class_(1) # needs sage.libs.singular + + """ + from sage.rings.ideal import Ideal_generic, Ideal_principal + return Ideal_principal if n == 1 else Ideal_generic + class ElementMethods: pass diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index a162fdb0def..f6f66245dff 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -562,6 +562,84 @@ def ideal_monoid(self): from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + ring. + + The argument `n`, standing for the number of generators + of the ideal, is ignored. + + EXAMPLES: + + Since :trac:`7797`, non-commutative rings have ideals as well:: + + sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules + sage: A._ideal_class_() # needs sage.combinat sage.modules + + """ + from sage.rings.noncommutative_ideals import Ideal_nc + return Ideal_nc + + def principal_ideal(self, gen, coerce=True): + """ + Return the principal ideal generated by ``gen``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: R.principal_ideal(x+2*y) + Ideal (x + 2*y) of Multivariate Polynomial Ring in x, y over Integer Ring + """ + C = self._ideal_class_(1) + if coerce: + gen = self(gen) + return C(self, [gen]) + + @cached_method + def unit_ideal(self): + """ + Return the unit ideal of this ring. + + EXAMPLES:: + + sage: Zp(7).unit_ideal() # needs sage.rings.padics + Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 + """ + return self.principal_ideal(self.one(), coerce=False) + + @cached_method + def zero_ideal(self): + """ + Return the zero ideal of this ring (cached). + + EXAMPLES:: + + sage: ZZ.zero_ideal() + Principal ideal (0) of Integer Ring + sage: QQ.zero_ideal() + Principal ideal (0) of Rational Field + sage: QQ['x'].zero_ideal() + Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field + + The result is cached:: + + sage: ZZ.zero_ideal() is ZZ.zero_ideal() + True + + TESTS: + + Make sure that :trac:`13644` is fixed:: + + sage: # needs sage.rings.padics + sage: K = Qp(3) + sage: R. = K[] + sage: L. = K.extension(a^2-3) + sage: L.ideal(a) + Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 + """ + return self.principal_ideal(self.zero(), coerce=False) + def characteristic(self): """ Return the characteristic of this ring. @@ -753,7 +831,7 @@ def _ideal_class_(self, n=0): example:: sage: super(Ring,QQ)._ideal_class_.__module__ - 'sage.categories.rings' + 'sage.categories.commutative_rings' sage: super(Ring,QQ)._ideal_class_() sage: super(Ring,QQ)._ideal_class_(1) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index c65f75a248c..59e0fca9781 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -410,6 +410,7 @@ from sage.rings import ideal import sage.structure.all from sage.structure.richcmp cimport (richcmp, rich_to_bool) from sage.misc.cachefunc import cached_method +from sage.categories.rings import Rings cdef class RingMap(Morphism): @@ -1291,7 +1292,8 @@ cdef class RingHomomorphism(RingMap): from sage.rings.ideal import Ideal_generic A = self.domain() B = self.codomain() - if not (A.is_commutative() and B.is_commutative()): + Comm = Rings().Commutative() + if not (A in Comm and B in Comm): raise NotImplementedError("rings are not commutative") if A.base_ring() != B.base_ring(): raise NotImplementedError("base rings must be equal") diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 650fb387fa0..dcd95e2660f 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -506,123 +506,6 @@ cdef class Ring(ParentWithGens): else: raise TypeError("Don't know how to transform %s into an ideal of %s" % (self, x)) - def _ideal_class_(self, n=0): - r""" - Return a callable object that can be used to create ideals in this - ring. For generic rings, this returns the factory function - :func:`sage.rings.ideal.Ideal`, which does its best to be clever about - what is required. - - This class can depend on `n`, the number of generators of the ideal. - The default input of `n=0` indicates an unspecified number of generators, - in which case a class that works for any number of generators is returned. - - EXAMPLES:: - - sage: ZZ._ideal_class_() - - sage: RR._ideal_class_() - - sage: R. = GF(5)[] - sage: R._ideal_class_(1) - - sage: S = R.quo(x^3 - y^2) - sage: S._ideal_class_(1) - - sage: S._ideal_class_(2) - - sage: T. = S[] # needs sage.libs.singular - sage: T._ideal_class_(5) # needs sage.libs.singular - - sage: T._ideal_class_(1) # needs sage.libs.singular - - - Since :trac:`7797`, non-commutative rings have ideals as well:: - - sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules - sage: A._ideal_class_() # needs sage.combinat sage.modules - - - """ - # One might need more than just n, but I can't think of an example. - from sage.rings.noncommutative_ideals import Ideal_nc - try: - if not self.is_commutative(): - return Ideal_nc - except (NotImplementedError, AttributeError): - return Ideal_nc - from sage.rings.ideal import Ideal_generic, Ideal_principal - if n == 1: - return Ideal_principal - else: - return Ideal_generic - - def principal_ideal(self, gen, coerce=True): - """ - Return the principal ideal generated by gen. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: R.principal_ideal(x+2*y) - Ideal (x + 2*y) of Multivariate Polynomial Ring in x, y over Integer Ring - """ - C = self._ideal_class_(1) - if coerce: - gen = self(gen) - return C(self, [gen]) - - def unit_ideal(self): - """ - Return the unit ideal of this ring. - - EXAMPLES:: - - sage: Zp(7).unit_ideal() # needs sage.rings.padics - Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 - """ - if self._unit_ideal is None: - I = Ring.ideal(self, [self(1)], coerce=False) - self._unit_ideal = I - return I - return self._unit_ideal - - def zero_ideal(self): - """ - Return the zero ideal of this ring (cached). - - EXAMPLES:: - - sage: ZZ.zero_ideal() - Principal ideal (0) of Integer Ring - sage: QQ.zero_ideal() - Principal ideal (0) of Rational Field - sage: QQ['x'].zero_ideal() - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - - The result is cached:: - - sage: ZZ.zero_ideal() is ZZ.zero_ideal() - True - - TESTS: - - Make sure that :trac:`13644` is fixed:: - - sage: # needs sage.rings.padics - sage: K = Qp(3) - sage: R. = K[] - sage: L. = K.extension(a^2-3) - sage: L.ideal(a) - Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 - - """ - if self._zero_ideal is None: - I = Ring.ideal(self, [self.zero()], coerce=False) - self._zero_ideal = I - return I - return self._zero_ideal - def zero(self): """ Return the zero element of this ring (cached). From 55226d6ae0025bf65ecaf9f0eec19efbd521b48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 26 Jan 2024 09:32:22 +0100 Subject: [PATCH 060/507] some fixes in function fields --- src/sage/categories/rings.py | 55 -------------------------- src/sage/rings/function_field/ideal.py | 2 +- src/sage/rings/function_field/order.py | 2 +- 3 files changed, 2 insertions(+), 57 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index f6f66245dff..c39a29b66af 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -795,61 +795,6 @@ def ideal(self, *args, **kwds): gens = gens[0] return C(self, gens, **kwds) - def _ideal_class_(self, n=0): - """ - Return the class that is used to implement ideals of this ring. - - .. NOTE:: - - We copy the code from :class:`~sage.rings.ring.Ring`. This is - necessary because not all rings inherit from that class, such - as matrix algebras. - - INPUT: - - - ``n`` (optional integer, default 0): The number of generators - of the ideal to be created. - - OUTPUT: - - The class that is used to implement ideals of this ring with - ``n`` generators. - - .. NOTE:: - - Often principal ideals (``n==1``) are implemented via - a different class. - - EXAMPLES:: - - sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules - sage: MS._ideal_class_() # needs sage.modules - - - We do not know of a commutative ring in Sage that does not inherit - from the base class of rings. So, we need to cheat in the next - example:: - - sage: super(Ring,QQ)._ideal_class_.__module__ - 'sage.categories.commutative_rings' - sage: super(Ring,QQ)._ideal_class_() - - sage: super(Ring,QQ)._ideal_class_(1) - - sage: super(Ring,QQ)._ideal_class_(2) - - """ - from sage.rings.noncommutative_ideals import Ideal_nc - try: - if not self.is_commutative(): - return Ideal_nc - except (NotImplementedError, AttributeError): - return Ideal_nc - from sage.rings.ideal import Ideal_generic, Ideal_principal - if n == 1: - return Ideal_principal - return Ideal_generic - ## # Quotient rings def quotient(self, I, names=None, **kwds): diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index 22aa15299b1..21ccf2b4f4c 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -1070,7 +1070,7 @@ def __init__(self, R): sage: M = O.ideal_monoid() sage: TestSuite(M).run() """ - self.Element = R._ideal_class + self.Element = R._ideal_class_ Parent.__init__(self, category=Monoids()) self.__R = R diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index 615cbab3690..a1cb369ee8e 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -146,7 +146,7 @@ def __init__(self, field, ideal_class=FunctionFieldIdeal, category=None): category = IntegralDomains().or_subcategory(category).Infinite() Parent.__init__(self, category=category, facade=field) - self._ideal_class = ideal_class # element class for parent ideal monoid + self._ideal_class_ = ideal_class # element class for parent ideal monoid self._field = field def is_field(self, proof=True): From 3a1f0e00912927d4ac2b81ec23af82493aac9537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 15:18:12 +0100 Subject: [PATCH 061/507] little detail --- src/sage/rings/function_field/order_polymod.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 97f5a625819..7c75a72f51d 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -649,7 +649,8 @@ def decomposition(self, ideal): # matrices_reduced give the multiplication matrices used to form the # algebra O mod pO. matrices_reduced = list(map(lambda M: M.mod(p), matrices)) - A = FiniteDimensionalAlgebra(k, matrices_reduced) + A = FiniteDimensionalAlgebra(k, matrices_reduced, + assume_associative=True) # Each prime ideal of the algebra A corresponds to a prime ideal of O, # and since the algebra is an Artinian ring, all of its prime ideals From 296d7e3a996101af6cb87d270e8231bb5d392d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Feb 2024 16:28:27 +0100 Subject: [PATCH 062/507] little detail --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 5294b37fb7f..0cedb9f8ff9 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -159,7 +159,6 @@ This base class provides a lot more methods than a general parent:: 'random_element', 'unit_ideal', 'zero', - 'zero_ideal', 'zeta', 'zeta_order'] From 9f5239ace31382af9b634cd149f0adf833a04e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Feb 2024 11:20:23 +0100 Subject: [PATCH 063/507] fix doctest --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 0cedb9f8ff9..a79ef618a5f 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -155,9 +155,7 @@ This base class provides a lot more methods than a general parent:: 'one', 'order', 'prime_subfield', - 'principal_ideal', 'random_element', - 'unit_ideal', 'zero', 'zeta', 'zeta_order'] From 6617f7963fcaf0572abaf184ee7e41604a2f2f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Feb 2024 11:32:37 +0100 Subject: [PATCH 064/507] tentative fixes --- .../categories/finite_dimensional_algebras_with_basis.py | 8 +++++++- src/sage/rings/function_field/order_polymod.py | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 0858078f41b..40458687a71 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -396,10 +396,16 @@ def center(self): center.rename("Center of {}".format(self)) return center - def principal_ideal(self, a, side='left'): + def principal_ideal(self, a, side='left', coerce=None): r""" Construct the ``side`` principal ideal generated by ``a``. + INPUT: + + - ``a`` -- an element + - ``side`` -- ``left`` (default) or ``right`` + - ``coerce`` -- ignored, for compatibility with categories + EXAMPLES: In order to highlight the difference between left and diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index 7c75a72f51d..c9b8e27d1d9 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -17,6 +17,7 @@ # **************************************************************************** from sage.arith.functions import lcm +from sage.categories.algebras import Algebras from sage.misc.cachefunc import cached_method from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -648,9 +649,9 @@ def decomposition(self, ideal): # Let O denote the maximal order self. When reduced modulo p, # matrices_reduced give the multiplication matrices used to form the # algebra O mod pO. - matrices_reduced = list(map(lambda M: M.mod(p), matrices)) - A = FiniteDimensionalAlgebra(k, matrices_reduced, - assume_associative=True) + matrices_reduced = [M.mod(p) for M in matrices] + cat = Algebras(k).FiniteDimensional().WithBasis() + A = FiniteDimensionalAlgebra(k, matrices_reduced, category=cat) # Each prime ideal of the algebra A corresponds to a prime ideal of O, # and since the algebra is an Artinian ring, all of its prime ideals From aede7ce0ee78591267b1868befdefd9055de3ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Feb 2024 11:46:37 +0100 Subject: [PATCH 065/507] one more fix --- src/sage/rings/function_field/order_polymod.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index c9b8e27d1d9..a84f1fd0229 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -650,7 +650,7 @@ def decomposition(self, ideal): # matrices_reduced give the multiplication matrices used to form the # algebra O mod pO. matrices_reduced = [M.mod(p) for M in matrices] - cat = Algebras(k).FiniteDimensional().WithBasis() + cat = Algebras(k).Commutative().FiniteDimensional().WithBasis() A = FiniteDimensionalAlgebra(k, matrices_reduced, category=cat) # Each prime ideal of the algebra A corresponds to a prime ideal of O, From 10f796a442cd6184378ccaad6847a316fcc5be84 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 7 Feb 2024 20:43:22 +0100 Subject: [PATCH 066/507] adapt to PolynomialSequence.coefficients_monomials --- src/sage/data_structures/stream.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index f52a757c4ec..442b0ddf4e4 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1781,20 +1781,19 @@ def _compute(self): # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence eqs = PolynomialSequence(self._P.polynomial_ring(), coeffs) - m1, v1 = eqs.coefficient_matrix() + m1, v1 = eqs.coefficients_monomials() # there should be at most one entry in v1 of degree 0 - # v1 is a matrix, not a vector - for j, (c,) in enumerate(v1): + for j, c in enumerate(v1): if c.degree() == 0: b = -m1.column(j) - m = m1.matrix_from_columns([i for i in range(v1.nrows()) if i != j]) - v = [c for i, (c,) in enumerate(v1) if i != j] + m = m1.matrix_from_columns([i for i in range(len(v1)) if i != j]) + v = [c for i, c in enumerate(v1) if i != j] break else: from sage.modules.free_module_element import zero_vector b = zero_vector(m1.nrows()) m = m1 - v = v1.list() + v = list(v1) x = m.solve_right(b) k = m.right_kernel_matrix() # substitute From 2d997393036b3387b0bc740a0942da4de04d6e28 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 8 Feb 2024 20:20:07 +0100 Subject: [PATCH 067/507] add change_ring, add doctests demonstrating that coercions with different base rings work now --- src/sage/combinat/sf/sfa.py | 43 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 0fd2d890b88..80f2db928e9 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -1,6 +1,5 @@ # sage.doctest: needs sage.combinat sage.modules -r""" -Symmetric Functions +r"""Symmetric Functions For a comprehensive tutorial on how to use symmetric functions in Sage @@ -170,21 +169,22 @@ BACKWARD INCOMPATIBLE CHANGES (:trac:`5457`): -The symmetric functions code has been refactored to take -advantage of the coercion systems. This introduced a couple of glitches: +The symmetric functions code has been refactored to take advantage of +the coercion systems. This introduced a couple of glitches, in +particular, on some bases changes, coefficients in Jack polynomials +are not normalized -- On some bases changes, coefficients in Jack polynomials are not normalized +However, conversions and coercions are now also defined between +symmetric functions over different coefficient rings:: -- Except in a few cases, conversions and coercions are only defined - between symmetric functions over the same coefficient ring. E.g. - the following does not work anymore:: + sage: S = SymmetricFunctions(QQ) + sage: S2 = SymmetricFunctions(QQ['t']) + sage: S3 = SymmetricFunctions(ZZ) + sage: S.m()[1] + S2.m()[2] + m[1] + m[2] - sage: s = SymmetricFunctions(QQ) - sage: s2 = SymmetricFunctions(QQ['t']) - sage: s([1]) + s2([2]) # todo: not implemented - - This feature will probably come back at some point through - improvements to the Sage coercion system. + sage: S.m()(S3.sp()[2,1]) + -m[1] + 2*m[1, 1, 1] + m[2, 1] Backward compatibility should be essentially retained. @@ -3028,6 +3028,21 @@ def construction(self): """ return SymmetricFunctionsFunctor(self._descriptor), self.base_ring() + def change_ring(self, R): + r""" + Return the base change of ``self`` to `R`. + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: s.change_ring(QQ) + Symmetric Functions over Rational Field in the Schur basis + """ + if R is self.base_ring(): + return self + functor, _ = self.construction() + return functor(R) + class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): r""" From 71c12598d9bfda998499150aaa92055551086a7e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 9 Feb 2024 16:51:44 +0100 Subject: [PATCH 068/507] add change_ring, add doctests demonstrating that coercions with different base rings work now --- src/sage/combinat/sf/sfa.py | 40 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 0fd2d890b88..07daa75375a 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -170,21 +170,22 @@ BACKWARD INCOMPATIBLE CHANGES (:trac:`5457`): -The symmetric functions code has been refactored to take -advantage of the coercion systems. This introduced a couple of glitches: +The symmetric functions code has been refactored to take advantage of +the coercion systems. This introduced a couple of glitches, in +particular, on some bases changes, coefficients in Jack polynomials +are not normalized -- On some bases changes, coefficients in Jack polynomials are not normalized +However, conversions and coercions are now also defined between +symmetric functions over different coefficient rings:: -- Except in a few cases, conversions and coercions are only defined - between symmetric functions over the same coefficient ring. E.g. - the following does not work anymore:: + sage: S = SymmetricFunctions(QQ) + sage: S2 = SymmetricFunctions(QQ['t']) + sage: S3 = SymmetricFunctions(ZZ) + sage: S.m()[1] + S2.m()[2] + m[1] + m[2] - sage: s = SymmetricFunctions(QQ) - sage: s2 = SymmetricFunctions(QQ['t']) - sage: s([1]) + s2([2]) # todo: not implemented - - This feature will probably come back at some point through - improvements to the Sage coercion system. + sage: S.m()(S3.sp()[2,1]) + -m[1] + 2*m[1, 1, 1] + m[2, 1] Backward compatibility should be essentially retained. @@ -3028,6 +3029,21 @@ def construction(self): """ return SymmetricFunctionsFunctor(self._descriptor), self.base_ring() + def change_ring(self, R): + r""" + Return the base change of ``self`` to `R`. + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: s.change_ring(QQ) + Symmetric Functions over Rational Field in the Schur basis + """ + if R is self.base_ring(): + return self + functor, _ = self.construction() + return functor(R) + class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): r""" From 08d4d78120302bfe02ea2489f6e2d7e14ef5bf6a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 9 Feb 2024 16:53:36 +0100 Subject: [PATCH 069/507] make map_coefficients more general, in line with polynomials --- src/sage/categories/modules_with_basis.py | 49 +++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index df7a04edc1f..41236ea3491 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -28,6 +28,7 @@ from sage.categories.fields import Fields from sage.categories.modules import Modules from sage.categories.poor_man_map import PoorManMap +from sage.categories.map import Map from sage.structure.element import Element, parent @@ -2031,17 +2032,25 @@ def trailing_term(self, *args, **kwds): """ return self.parent().term(*self.trailing_item(*args, **kwds)) - def map_coefficients(self, f): + def map_coefficients(self, f, new_base_ring=None): """ - Mapping a function on coefficients. + Return the element obtained by applying ``f`` to the non-zero + coefficients of ``self``. + + If ``f`` is a :class:`sage.categories.map.Map`, then the resulting + polynomial will be defined over the codomain of ``f``. Otherwise, the + resulting polynomial will be over the same ring as ``self``. Set + ``new_base_ring`` to override this behaviour. + + An error is raised if the coefficients are not in the new base ring. INPUT: - - ``f`` -- an endofunction on the coefficient ring of the - free module + - ``f`` -- a callable that will be applied to the + coefficients of ``self``. - Return a new element of ``self.parent()`` obtained by applying the - function ``f`` to all of the coefficients of ``self``. + - ``new_base_ring`` (optional) -- if given, the resulting element + will be defined over this ring. EXAMPLES:: @@ -2065,8 +2074,32 @@ def map_coefficients(self, f): sage: a = s([2,1]) + 2*s([3,2]) # needs sage.combinat sage.modules sage: a.map_coefficients(lambda x: x * 2) # needs sage.combinat sage.modules 2*s[2, 1] + 4*s[3, 2] - """ - return self.parent().sum_of_terms( (m, f(c)) for m,c in self ) + + We can map into a different base ring:: + + sage: # needs sage.combinat + sage: e = SymmetricFunctions(QQ).elementary() + sage: a = 1/2*(e([2,1]) + e([1,1,1])); a + 1/2*e[1, 1, 1] + 1/2*e[2, 1] + sage: b = a.map_coefficients(lambda c: 2*c, ZZ); b + e[1, 1, 1] + e[2, 1] + sage: b.parent() + Symmetric Functions over Integer Ring in the elementary basis + sage: b.map_coefficients(lambda c: 1/2*c, ZZ) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + """ + R = self.parent() + if new_base_ring is not None: + B = new_base_ring + R = R.change_ring(B) + elif isinstance(f, Map): + B = f.codomain() + R = R.change_ring(B) + else: + B = self.base_ring() + return R.sum_of_terms((m, B(f(c))) for m, c in self) def map_support(self, f): """ From 4c1d4557226a50cfff1dc5facfc44e32517dc798 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 9 Feb 2024 17:26:00 +0100 Subject: [PATCH 070/507] correct equality --- src/sage/combinat/sf/sfa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 07daa75375a..8db655bb658 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -6455,7 +6455,7 @@ def action(x): def __eq__(self, other): if not isinstance(other, SymmetricFunctionsFunctor): return False - return self.vars == other.vars + return self._descriptor == other._descriptor def _repr_(self): """ From 3a056bd6458c52b1238444507c0a7326b7c34394 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 9 Feb 2024 17:29:41 +0100 Subject: [PATCH 071/507] fix equality and subs in UndeterminedCoefficientsRingElement, fix construction of univariate LazyPowerSeriesRing --- src/sage/data_structures/stream.py | 29 +++++++++++++++++------------ src/sage/rings/lazy_series_ring.py | 3 +++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 442b0ddf4e4..783d16fd155 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1257,16 +1257,12 @@ def __init__(self, parent, v): Element.__init__(self, parent) self._p = v + def __bool__(self): + return bool(self._p) + def _repr_(self): return repr(self._p) - def _richcmp_(self, other, op): - r""" - Compare ``self`` with ``other`` with respect to the comparison - operator ``op``. - """ - return self._p._richcmp_(other._p, op) - def _add_(self, other): """ @@ -1332,7 +1328,7 @@ def variables(self): def rational_function(self): return self._p - def subs(self, d): + def subs(self, in_dict=None, *args, **kwds): """ EXAMPLES:: @@ -1344,19 +1340,26 @@ def subs(self, d): sage: (p/q).subs({v: 3}) 4/(FESDUMMY_... + 1) """ +# if isinstance(in_dict, dict): +# R = self._p.parent() +# in_dict = {ZZ(m) if m in ZZ else R(m): v for m, v in in_dict.items()} +# +# P = self.parent() +# return P.element_class(P, self._p.subs(in_dict, *args, **kwds)) P = self.parent() p_num = P._P(self._p.numerator()) V_num = p_num.variables() - d_num = {P._P(v): c for v, c in d.items() + d_num = {P._P(v): c for v, c in in_dict.items() if v in V_num} num = p_num.subs(d_num) p_den = P._P(self._p.denominator()) V_den = p_den.variables() - d_den = {P._P(v): c for v, c in d.items() + d_den = {P._P(v): c for v, c in in_dict.items() if v in V_den} den = p_den.subs(d_den) return P.element_class(P, P._PF(num / den)) + from sage.categories.pushout import UndeterminedCoefficientsFunctor class UndeterminedCoefficientsRing(UniqueRepresentation, Parent): @@ -1527,7 +1530,8 @@ def define_implicitly(self, series, initial_values, equations, - ``series`` -- a list of series - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - - ``R`` -- the ring containing the coefficients (after substitution) + - ``base_ring`` -- the base ring + - ``coefficient_ring`` -- the ring containing the coefficients (after substitution) - ``terms_of_degree`` -- a function returning the list of terms of a given degree """ @@ -1694,7 +1698,8 @@ def _subs_in_caches(self, var, val): else: c = c.map_coefficients(lambda e: e.subs({var: val})) try: - c = c.map_coefficients(lambda e: self._base_ring(e.rational_function()), + c = c.map_coefficients(lambda e: (e if e in self._base_ring + else self._base_ring(e.rational_function())), self._base_ring) except TypeError: pass diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 8d1d2841310..f4aae77c6e5 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -2378,6 +2378,9 @@ def __init__(self, base_ring, names, sparse=True, category=None): def construction(self): from sage.categories.pushout import CompletionFunctor + if self._arity == 1: + return (CompletionFunctor(self._names[0], infinity), + self._laurent_poly_ring) return (CompletionFunctor(self._names, infinity), self._laurent_poly_ring) From 791c63b728e60048740936ad86a3bf3219a727fd Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 10 Feb 2024 22:59:33 +0100 Subject: [PATCH 072/507] beautify repr, add doctests, deprecate corresponding_basis_over --- src/sage/combinat/sf/sfa.py | 182 +++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 46 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 8db655bb658..92e39be373f 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -625,6 +625,11 @@ def corresponding_basis_over(self, R): sage: Sym = SymmetricFunctions(QQ) sage: m = Sym.monomial() sage: m.corresponding_basis_over(ZZ) + doctest:warning + ... + DeprecationWarning: S.corresponding_basis_over(R) is deprecated. + Use S.change_ring(R) instead. + See https://github.com/sagemath/sage/issues/37220 for details. Symmetric Functions over Integer Ring in the monomial basis sage: Sym = SymmetricFunctions(CyclotomicField()) @@ -635,7 +640,8 @@ def corresponding_basis_over(self, R): sage: P = ZZ['q','t'] sage: Sym = SymmetricFunctions(P) sage: mj = Sym.macdonald().J() - sage: mj.corresponding_basis_over(Integers(13)) + sage: mj.corresponding_basis_over(Integers(13)['q','t']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Ring of integers modulo 13 in the Macdonald J basis TESTS: @@ -658,23 +664,40 @@ def corresponding_basis_over(self, R): Symmetric Functions over Universal Cyclotomic Field in the forgotten basis sage: Sym.w().corresponding_basis_over(CyclotomicField()) Symmetric Functions over Universal Cyclotomic Field in the Witt basis - sage: Sym.macdonald().P().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald().Q().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald().J().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald().H().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald().Ht().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald().S().corresponding_basis_over(CyclotomicField()) - sage: Sym.macdonald(q=1).S().corresponding_basis_over(CyclotomicField()) + sage: Sym.macdonald().P().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald P basis + sage: Sym.macdonald().Q().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald Q basis + sage: Sym.macdonald().J().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald J basis + sage: Sym.macdonald().H().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald H basis + sage: Sym.macdonald().Ht().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald Ht basis + sage: Sym.macdonald().S().corresponding_basis_over(CyclotomicField()['q', 't']) + Symmetric Functions over Multivariate Polynomial Ring in q, t over Universal Cyclotomic Field in the Macdonald S basis + sage: Sym.macdonald(q=1).S().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Macdonald S with q=1 basis sage: Sym.macdonald(q=1,t=3).P().corresponding_basis_over(CyclotomicField()) - sage: Sym.hall_littlewood().P().corresponding_basis_over(CyclotomicField()) - sage: Sym.hall_littlewood().Q().corresponding_basis_over(CyclotomicField()) - sage: Sym.hall_littlewood().Qp().corresponding_basis_over(CyclotomicField()) + Symmetric Functions over Universal Cyclotomic Field in the Macdonald P with q=1 and t=3 basis + sage: Sym.hall_littlewood().P().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Hall-Littlewood P basis + sage: Sym.hall_littlewood().Q().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Hall-Littlewood Q basis + sage: Sym.hall_littlewood().Qp().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Hall-Littlewood Qp basis sage: Sym.hall_littlewood(t=1).P().corresponding_basis_over(CyclotomicField()) - sage: Sym.jack().J().corresponding_basis_over(CyclotomicField()) - sage: Sym.jack().P().corresponding_basis_over(CyclotomicField()) - sage: Sym.jack().Q().corresponding_basis_over(CyclotomicField()) - sage: Sym.jack().Qp().corresponding_basis_over(CyclotomicField()) + Symmetric Functions over Universal Cyclotomic Field in the Hall-Littlewood P with t=1 basis + sage: Sym.jack().J().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Jack J basis + sage: Sym.jack().P().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Jack P basis + sage: Sym.jack().Q().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Jack Q basis + sage: Sym.jack().Qp().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the Jack Qp basis sage: Sym.jack(t=1).J().corresponding_basis_over(CyclotomicField()) + Symmetric Functions over Universal Cyclotomic Field in the Jack J with t=1 basis sage: Sym.zonal().corresponding_basis_over(CyclotomicField()) Symmetric Functions over Universal Cyclotomic Field in the zonal basis sage: Sym.llt(3).hspin().corresponding_basis_over(CyclotomicField()) @@ -688,26 +711,13 @@ def corresponding_basis_over(self, R): rewritten as soon as the bases of ``SymmetricFunctions`` are put on a more robust and systematic footing. """ - from sage.combinat.sf.sf import SymmetricFunctions - from sage.misc.call import attrcall + from sage.misc.superseded import deprecation + deprecation(37220, 'S.corresponding_basis_over(R) is deprecated.' + ' Use S.change_ring(R) instead.') try: - return attrcall(self._basis)(SymmetricFunctions(R)) - except AttributeError: # or except (AttributeError, ValueError): + return self.change_ring(R) + except NotImplementedError: return None - #Alternative code proposed by Florent Hivert, which sadly fails for the - #forgotten basis (which reduces differently than the other ones): - #try: - # parentred1 = self._reduction - # parentred2 = parentred1[1][0]._reduction - # parentred2prime = tuple([parentred2[0], tuple([R]), parentred2[2]]) - # from sage.structure.unique_representation import unreduce - # parent2 = unreduce(*parentred2prime) - # parentred1prime = tuple([parentred1[0], tuple([parent2]), parentred1[2]]) - # return unreduce(*parentred1prime) - #except (AttributeError, ValueError): - # return None - #This code relied heavily on the construction of bases of - #``SymmetricFunctions`` and on their reduction. def skew_schur(self, x): """ @@ -1034,8 +1044,9 @@ def component(i, g): # == h_g[L_i] # Now let's try to find out what basis self is in, and # construct the corresponding basis of symmetric functions # over QQ. - corresponding_parent_over_QQ = self.corresponding_basis_over(QQ) - if corresponding_parent_over_QQ is None: + try: + corresponding_parent_over_QQ = self.change_ring(QQ) + except (NotImplementedError, TypeError): # This is the case where the corresponding basis # over QQ cannot be found. This can have two reasons: # Either the basis depends on variables (like the @@ -1205,8 +1216,9 @@ def component(i, g): # == h_g[L_i] or e_g[L_i] # Now let's try to find out what basis self is in, and # construct the corresponding basis of symmetric functions # over QQ. - corresponding_parent_over_QQ = self.corresponding_basis_over(QQ) - if corresponding_parent_over_QQ is None: + try: + corresponding_parent_over_QQ = self.change_ring(QQ) + except (NotImplementedError, TypeError): # This is the case where the corresponding basis # over QQ cannot be found. This can have two reasons: # Either the basis depends on variables (like the @@ -4115,8 +4127,9 @@ def itensor(self, x): # Now let's try to find out what basis self is in, and # construct the corresponding basis of symmetric functions # over QQ. - corresponding_parent_over_QQ = parent.corresponding_basis_over(QQ) - if corresponding_parent_over_QQ is None: + try: + corresponding_parent_over_QQ = parent.change_ring(QQ) + except (NotImplementedError, TypeError): # This is the case where the corresponding basis # over QQ cannot be found. This can have two reasons: # Either the basis depends on variables (like the @@ -4314,7 +4327,7 @@ def reduced_kronecker_product(self, x): comp_x = comp_parent(x) # Now, comp_self and comp_x are the same as self and x, but in the # Schur basis, which we call comp_parent. - schur_Q = comp_parent.corresponding_basis_over(QQ) + schur_Q = comp_parent.change_ring(QQ) # schur_Q is the Schur basis of the symmetric functions over QQ. result = comp_parent.zero() for lam, a in comp_self: @@ -4756,8 +4769,9 @@ def f(lam, mu): return parent(p._apply_multi_module_morphism(p(self),p(x),f)) comp_parent = parent comp_self = self - corresponding_parent_over_QQ = parent.corresponding_basis_over(QQ) - if corresponding_parent_over_QQ is None: + try: + corresponding_parent_over_QQ = parent.change_ring(QQ) + except (NotImplementedError, TypeError): comp_parent = parent.realization_of().schur() comp_self = comp_parent(self) from sage.combinat.sf.sf import SymmetricFunctions @@ -6412,9 +6426,43 @@ def exponential_specialization(self, t=None, q=1): from sage.categories.functor import Functor class SymmetricFunctionsFunctor(ConstructionFunctor): + """ + A constructor for free Zinbiel algebras. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: s.construction() + (SymmetricFunctionsFunctor[schur], Rational Field) + """ rank = 9 def __init__(self, descriptor): + r""" + Initialise the functor. + + INPUT: + + - ``descriptor`` -- an iterable of pairs ``(family, params)`` + or singletons ``(basis,)``, where ``family`` and ``basis`` + are strings and ``params`` is a dictionary whose keys are + strings. + + .. WARNING: + + Strictly speaking, this is not necessarily a functor on + :class:`CommutativeRings`, but rather a functor on + commutative rings with some distinguished elements. For + example, for the Macdonald polynomials, we have to + specify `q` and `t` in the ring. + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import SymmetricFunctionsFunctor + sage: R. = ZZ[] + sage: SymmetricFunctionsFunctor((('macdonald', {'q': q, 't': t}), ('H',))) + SymmetricFunctionsFunctor[macdonald(q=q, t=t).H] + """ self._descriptor = descriptor Functor.__init__(self, CommutativeRings(), CommutativeRings()) @@ -6425,7 +6473,7 @@ def _apply_functor(self, R): EXAMPLES:: sage: s = SymmetricFunctions(ZZ).s() - sage: F, R = s.construction() + sage: F, R = s.construction() # indirect doctest sage: F(QQ) Symmetric Functions over Rational Field in the Schur basis """ @@ -6443,6 +6491,27 @@ def _apply_functor_to_morphism(self, f): """ Apply the functor ``self`` to the ring morphism `f`. + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: F, R = s.construction() + sage: F(ZZ.hom(GF(3))) # indirect doctest + Generic morphism: + From: Symmetric Functions over Integer Ring in the Schur basis + To: Symmetric Functions over Finite Field of size 3 in the Schur basis + + sage: R. = ZZ[] + sage: P = SymmetricFunctions(R).jack().P() + sage: F, R = P.construction() + sage: F(ZZ["t"].hom(GF(3)["t"])) + Generic morphism: + From: Symmetric Functions over Univariate Polynomial Ring in t over Integer Ring in the Jack P basis + To: Symmetric Functions over Univariate Polynomial Ring in t over Finite Field of size 3 in the Jack P basis + + sage: R. = ZZ[] + sage: H = SymmetricFunctions(R).macdonald().H() + sage: F, R = H.construction() + sage: F(ZZ["q", "t"].hom(GF(3)["q", "t"])) # known bug """ dom = self(f.domain()) codom = self(f.codomain()) @@ -6453,6 +6522,20 @@ def action(x): return dom.module_morphism(function=action, codomain=codom) def __eq__(self, other): + """ + EXAMPLES:: + + sage: R. = ZZ[] + sage: S. = QQ[] + sage: T. = QQ[] + sage: PR = SymmetricFunctions(R).jack().P() + sage: PS = SymmetricFunctions(S).jack().P() + sage: PT = SymmetricFunctions(T).jack(t=s).P() + sage: PR.construction()[0] == PS.construction()[0] + True + sage: PR.construction()[0] == PT.construction()[0] + False + """ if not isinstance(other, SymmetricFunctionsFunctor): return False return self._descriptor == other._descriptor @@ -6461,13 +6544,20 @@ def _repr_(self): """ TESTS:: - sage: R. = ZZ[] + sage: R. = ZZ[] sage: H = SymmetricFunctions(R).macdonald().H() sage: F, R = H.construction() sage: F - (('macdonald', {'q': q, 't': t}), ('H',)) + SymmetricFunctionsFunctor[macdonald(q=q, t=t).H] """ - return repr(self._descriptor) + basis = ".".join(method + + ("(" + + ", ".join(key + "=" + repr(param) + for key, param in params[0].items()) + + ")" if params + else "") + for method, *params in self._descriptor) + return "SymmetricFunctionsFunctor[" + basis + "]" ################### def _lmax(x): From 6a209947d608e59fb40a19daaff1df9cf5220e1d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 10 Feb 2024 23:44:34 +0100 Subject: [PATCH 073/507] provide _descriptor for llt --- src/sage/combinat/sf/llt.py | 2 ++ src/sage/combinat/sf/sfa.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/sf/llt.py b/src/sage/combinat/sf/llt.py index 6e0e20d49ba..cc7bfb3e688 100644 --- a/src/sage/combinat/sf/llt.py +++ b/src/sage/combinat/sf/llt.py @@ -645,6 +645,7 @@ def __init__(self, llt): self._m_to_self_cache = m_to_hsp_cache[level] LLT_generic.__init__(self, llt, prefix="HSp%s" % level) + self._descriptor = (("llt", {"k": self.level(), "t": self.t}), ("hspin",)) def _to_m(self, part): r""" @@ -713,6 +714,7 @@ def __init__(self, llt): self._self_to_m_cache = hcosp_to_m_cache[level] self._m_to_self_cache = m_to_hcosp_cache[level] LLT_generic.__init__(self, llt, prefix="HCosp%s" % level) + self._descriptor = (("llt", {"k": self.level(), "t": self.t}), ("hcospin",)) def _to_m(self, part): r""" diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 92e39be373f..79cce5a9292 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -700,10 +700,14 @@ def corresponding_basis_over(self, R): Symmetric Functions over Universal Cyclotomic Field in the Jack J with t=1 basis sage: Sym.zonal().corresponding_basis_over(CyclotomicField()) Symmetric Functions over Universal Cyclotomic Field in the zonal basis - sage: Sym.llt(3).hspin().corresponding_basis_over(CyclotomicField()) - sage: Sym.llt(3).hcospin().corresponding_basis_over(CyclotomicField()) + sage: Sym.llt(3).hspin().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the level 3 LLT spin basis + sage: Sym.llt(3).hcospin().corresponding_basis_over(CyclotomicField()['t']) + Symmetric Functions over Univariate Polynomial Ring in t over Universal Cyclotomic Field in the level 3 LLT cospin basis sage: Sym.llt(3, t=1).hspin().corresponding_basis_over(CyclotomicField()) + Symmetric Functions over Universal Cyclotomic Field in the level 3 LLT spin with t=1 basis sage: Sym.llt(3, t=1).hcospin().corresponding_basis_over(CyclotomicField()) + Symmetric Functions over Universal Cyclotomic Field in the level 3 LLT cospin with t=1 basis .. TODO:: From 77b79c74c7b9ab3333d3b1256078a6aac36ffe0e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 11 Feb 2024 12:11:45 +0100 Subject: [PATCH 074/507] construction for dual bases --- src/sage/combinat/sf/dual.py | 21 ++++++++++++--------- src/sage/combinat/sf/sfa.py | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index 58b69743c7b..69cada9e2ac 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -26,7 +26,13 @@ class SymmetricFunctionAlgebra_dual(classical.SymmetricFunctionAlgebra_classical): - def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=None): + @staticmethod + def __classcall__(cls, dual_basis, scalar, scalar_name="", basis_name=None, prefix=None): + if prefix is None: + prefix = 'd_'+dual_basis.prefix() + return super().__classcall__(cls, dual_basis, scalar, scalar_name, basis_name, prefix) + + def __init__(self, dual_basis, scalar, scalar_name, basis_name, prefix): r""" Generic dual basis of a basis of symmetric functions. @@ -72,9 +78,9 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N EXAMPLES:: sage: e = SymmetricFunctions(QQ).e() - sage: f = e.dual_basis(prefix = "m", basis_name="Forgotten symmetric functions"); f + sage: f = e.dual_basis(prefix="m", basis_name="Forgotten symmetric functions"); f Symmetric Functions over Rational Field in the Forgotten symmetric functions basis - sage: TestSuite(f).run(skip='_test_construction', elements = [f[1,1]+2*f[2], f[1]+3*f[1,1]]) + sage: TestSuite(f).run(elements=[f[1,1]+2*f[2], f[1]+3*f[1,1]]) sage: TestSuite(f).run() # long time (11s on sage.math, 2011) This class defines canonical coercions between ``self`` and @@ -123,6 +129,9 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N self._dual_basis = dual_basis self._scalar = scalar self._scalar_name = scalar_name + if self._scalar == sage.combinat.sf.sfa.zee: + self._descriptor = (self._dual_basis._descriptor + + (("dual_basis", {"prefix": prefix, "basis_name": basis_name}),)) # Set up the cache @@ -145,9 +154,6 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N self._sym = sage.combinat.sf.sf.SymmetricFunctions(scalar_target) self._p = self._sym.power() - if prefix is None: - prefix = 'd_'+dual_basis.prefix() - classical.SymmetricFunctionAlgebra_classical.__init__(self, self._sym, basis_name=basis_name, prefix=prefix) @@ -157,9 +163,6 @@ def __init__(self, dual_basis, scalar, scalar_name="", basis_name=None, prefix=N self.register_coercion(SetMorphism(Hom(self._dual_basis, self, category), self._dual_to_self)) self._dual_basis.register_coercion(SetMorphism(Hom(self, self._dual_basis, category), self._self_to_dual)) - def construction(self): - raise NotImplementedError - def _dual_to_self(self, x): """ Coerce an element of the dual of ``self`` canonically into ``self``. diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 79cce5a9292..070ae8f64e8 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -6431,7 +6431,7 @@ def exponential_specialization(self, t=None, q=1): class SymmetricFunctionsFunctor(ConstructionFunctor): """ - A constructor for free Zinbiel algebras. + A constructor for algebras of symmetric functions. EXAMPLES:: From 3752688a112b8b089b3e21f2c3d5f79a7468b952 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 11 Feb 2024 16:07:24 +0100 Subject: [PATCH 075/507] fix equality for llt --- src/sage/combinat/sf/llt.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/sf/llt.py b/src/sage/combinat/sf/llt.py index cc7bfb3e688..1e46ddeeae3 100644 --- a/src/sage/combinat/sf/llt.py +++ b/src/sage/combinat/sf/llt.py @@ -91,8 +91,11 @@ class LLT_class(UniqueRepresentation): sage: HS3x(HC3t2[3,1]) 2*HSp3[3, 1] + (-2*x+1)*HSp3[4] """ + @staticmethod + def __classcall__(cls, Sym, k, t='t'): + return super().__classcall__(cls, Sym, k, Sym.base_ring()(t)) - def __init__(self, Sym, k, t='t'): + def __init__(self, Sym, k, t): r""" Class of LLT symmetric function bases @@ -129,7 +132,7 @@ def __init__(self, Sym, k, t='t'): self._k = k self._sym = Sym self._name = "level %s LLT polynomials" % self._k - self.t = Sym.base_ring()(t) + self.t = t self._name_suffix = "" if str(t) != 't': self._name_suffix += " with t=%s" % self.t From 0d72c5892589168f8987d3fbebd6d1ca5f3060f8 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Wed, 21 Feb 2024 00:51:50 -0500 Subject: [PATCH 076/507] Improved doctests --- .../modules/free_module_pseudohomspace.py | 143 ++++++++++++------ .../modules/free_module_pseudomorphism.py | 52 ++++--- 2 files changed, 133 insertions(+), 62 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index f696be346f6..5299ff2fca7 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -7,7 +7,7 @@ """ # **************************************************************************** -# Copyright (C) 2024 Yossef Musleh +# Copyright (C) 2024 Yossef Musleh # # Distributed under the terms of the GNU General Public License (GPL) # @@ -24,7 +24,6 @@ from sage.structure.element import is_Matrix from sage.matrix.constructor import matrix, identity_matrix from sage.matrix.matrix_space import MatrixSpace -from sage.misc.cachefunc import cached_method from sage.categories.morphism import Morphism from sage.misc.lazy_import import lazy_import @@ -37,7 +36,7 @@ class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): For free modules, the elements of a pseudomorphism correspond to matrices which define the mapping on elements of a basis. - EXAMPLES:: + TESTS:: sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) @@ -47,11 +46,30 @@ class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): (z3, 2*z3^2 + 3*z3 + 3) """ - def __init__(self, X, Y, twist=None): - self._domain = X - self._codomain = X - if Y is not None: - self._codomain = Y + def __init__(self, domain, codomain=None, twist=None): + r""" + Constructs the space of pseudomorphisms with a given twist. + + INPUT: + - ``domain`` - the domain of the pseudomorphism; a free module + + - ``codomain`` - the codomain of the pseudomorphism; a free + module (default: None) + + - ``twist`` - a twisting morphism, this is either a morphism or + a derivation (default: None) + + EXAMPLES:: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist); PHS + Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z3 of size 5^3 to Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + """ + self._domain = domain + self._codomain = domain + if codomain is not None: + self._codomain = codomain super().__init__(self._domain, self._codomain, category=None) self.base_homspace = self._domain.Hom(self._codomain) self.twist = twist @@ -74,6 +92,10 @@ def __call__(self, A, **kwds): r""" Coerce a matrix or free module homomorphism into a pseudomorphism. + INPUTS: + - ``A`` - either a matrix defining the morphism or a free module + morphism + EXAMPLES:: sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() @@ -86,34 +108,7 @@ def __call__(self, A, **kwds): Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - from . import free_module_pseudomorphism - side = kwds.get("side", "left") - if not is_Matrix(A): - C = self.codomain() - try: - if callable(A): - v = [C(A(g)) for g in self.domain().gens()] - A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) - if side == "right": - A = A.transpose() - else: - v = [C(a) for a in A] - if side == "right": - A = matrix([C.coordinates(a) for a in v], - ncols=C.rank()).transpose() - else: - A = matrix([C.coordinates(a) for a in v], - ncols=C.rank()) - except TypeError: - pass - if not self.codomain().base_ring().has_coerce_map_from( - self.domain().base_ring()) and not A.is_zero(): - raise TypeError("nontrivial morphisms require a coercion map" - "from the base ring of the domain to the base ring of the" - "codomain") - return free_module_pseudomorphism.FreeModulePseudoMorphism( - self.domain(), A, twist=self.twist, - codomain=self.codomain()) + return self._element_constructor_(A, **kwds) def __repr__(self): r""" @@ -137,23 +132,67 @@ def __repr__(self): deriv = deriv.format(self.derivation.__repr__()) return r.format(self.domain(), self.codomain(), morph, deriv) - def zero(self): + def _element_constructor_(self, A, **kwds): r""" - Return the zero pseudomorphism. This corresponds to the zero matrix. + Coerce a matrix or free module homomorphism into a pseudomorphism. - EXAMPLES:: + INPUTS: + - ``A`` - either a matrix defining the morphism or a free module + morphism + + TESTS:: sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() sage: PHS = M.PseudoHom(twist) - sage: PHS.zero() + sage: h = PHS._element_constructor_([[1, 2], [1, 1]]); h Free module pseudomorphism defined by the matrix - [0 0] - [0 0] + [1 2] + [1 1] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + + :: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: morph = M.hom(matrix([[1, 2], [1, 1]])) + sage: phi = PHS._element_constructor_(morph, side="right"); phi + Free module pseudomorphism defined as left-multiplication by the matrix + [1 1] + [2 1] twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - return self(self.base_homspace.zero()) + from . import free_module_pseudomorphism + side = kwds.get("side", "left") + if not is_Matrix(A): + C = self.codomain() + try: + if callable(A): + v = [C(A(g)) for g in self.domain().gens()] + A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) + if side == "right": + A = A.transpose() + else: + v = [C(a) for a in A] + if side == "right": + A = matrix([C.coordinates(a) for a in v], + ncols=C.rank()).transpose() + else: + A = matrix([C.coordinates(a) for a in v], + ncols=C.rank()) + except TypeError: + pass + if not self.codomain().base_ring().has_coerce_map_from( + self.domain().base_ring()) and not A.is_zero(): + raise TypeError("nontrivial morphisms require a coercion map" + "from the base ring of the domain to the base ring of the" + "codomain") + return free_module_pseudomorphism.FreeModulePseudoMorphism( + self.domain(), A, twist=self.twist, + codomain=self.codomain(), side=side) def _matrix_space(self): r""" @@ -217,3 +256,21 @@ def identity(self): Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ return self(self.base_homspace.identity()) + + def zero(self): + r""" + Return the zero pseudomorphism. This corresponds to the zero matrix. + + EXAMPLES:: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(twist) + sage: PHS.zero() + Free module pseudomorphism defined by the matrix + [0 0] + [0 0] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + """ + return self(self.base_homspace.zero()) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index b5d894a7ac2..2c3f0491407 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -37,10 +37,10 @@ class FreeModulePseudoMorphism(Morphism): r""" Let `M, M'` be free modules over a ring `R`, `\theta: R \to R` a ring - homomorphism, and `\theta: R \to R` a derivation i.e. an additive - map such that + homomorphism, and `\delta: R \to R` a `\theta`-derivation, which is a map + such that: - `\delta(xy) = x\delta(y) + \delta(x)y`. + `\delta(xy) = \theta(x)\delta(y) + \delta(x)y`. Then a pseudomorphism `f : M to M` is a map such that @@ -99,10 +99,22 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" EXAMPLES:: - sage: F = GF(25); V = F^3; twist = F.frobenius_endomorphism(5) - sage: phi = V.pseudohom(matrix(F,3,[1..9]), twist) + sage: F = GF(25); M = F^3; twist = F.frobenius_endomorphism(5) + sage: phi = M.pseudohom(matrix(F,3,[1..9]), twist) sage: type(phi) + + :: + + sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() + sage: morph = M.hom(matrix([[1,2],[0,1]])) + sage: phi = M.pseudohom(morph, twist, side="right"); phi + Free module pseudomorphism defined as left-multiplication by the matrix + [1 2] + [0 1] + twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 + Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ from sage.structure.element import is_Matrix Morphism.__init__(self, domain.PseudoHom(twist, codomain)) @@ -115,6 +127,7 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" base_morphism) self.derivation = None self.twist_morphism = None + self.side = side if isinstance(twist, Morphism): self.twist_morphism = twist elif isinstance(twist, RingDerivation): @@ -123,7 +136,8 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" self.derivation = twist else: self.derivation = None - self.side = side + elif twist is not None: + raise TypeError("twist is not a ring morphism or derivation") def _call_(self, x): r""" @@ -216,19 +230,6 @@ def matrix(self): """ return self._base_matrix - def twisting_morphism(self): - r""" - Return the twisting homomorphism of the pseudomorphism. - - EXAMPLES:: - - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: ph.twisting_morphism() - Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 - """ - return self.twist_morphism - def twisting_derivation(self): r""" Return the twisting derivation of the pseudomorphism. @@ -241,3 +242,16 @@ def twisting_derivation(self): d/dx """ return self.derivation + + def twisting_morphism(self): + r""" + Return the twisting homomorphism of the pseudomorphism. + + EXAMPLES:: + + sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() + sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") + sage: ph.twisting_morphism() + Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 + """ + return self.twist_morphism From f2751254d9ce717b62ffe1d7b8a32550345fe438 Mon Sep 17 00:00:00 2001 From: ymusleh Date: Wed, 21 Feb 2024 01:01:28 -0500 Subject: [PATCH 077/507] Removed extraneous code from free module pseudomorphism methods --- src/sage/modules/free_module.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index ece22120141..8fde8c3ff7d 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3098,9 +3098,7 @@ def pseudohom(self, morphism, twist=None, codomain=None, **kwds): Codomain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism - from sage.structure.element import is_Matrix - side = kwds.get("side", "left") - return FreeModulePseudoMorphism(self, morphism, twist, codomain, side) + return FreeModulePseudoMorphism(self, morphism, twist, codomain, **kwds) def inner_product_matrix(self): """ From 1134baaa1b7824db6b7623c8e21baab0a935365c Mon Sep 17 00:00:00 2001 From: ymusleh Date: Wed, 21 Feb 2024 17:31:32 -0500 Subject: [PATCH 078/507] refactor pseudomorphism constructor --- src/sage/modules/free_module.py | 5 +- .../modules/free_module_pseudohomspace.py | 26 +------- .../modules/free_module_pseudomorphism.py | 62 ++++++++++--------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 8fde8c3ff7d..b7cd80e61ad 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3079,7 +3079,7 @@ def PseudoHom(self, twist=None, codomain=None): """ from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace - return FreeModulePseudoHomspace(self, codomain, twist=twist) + return FreeModulePseudoHomspace(self, codomain=codomain, twist=twist) def pseudohom(self, morphism, twist=None, codomain=None, **kwds): r""" @@ -3098,7 +3098,8 @@ def pseudohom(self, morphism, twist=None, codomain=None, **kwds): Codomain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism - return FreeModulePseudoMorphism(self, morphism, twist, codomain, **kwds) + side = kwds.get("side", "left") + return FreeModulePseudoMorphism(self.PseudoHom(twist=twist, codomain=codomain), morphism, side) def inner_product_matrix(self): """ diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 5299ff2fca7..65af841f37b 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -159,40 +159,20 @@ def _element_constructor_(self, A, **kwds): sage: morph = M.hom(matrix([[1, 2], [1, 1]])) sage: phi = PHS._element_constructor_(morph, side="right"); phi Free module pseudomorphism defined as left-multiplication by the matrix + [1 2] [1 1] - [2 1] twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - from . import free_module_pseudomorphism + from . import free_module_pseudomorphism as pseudo side = kwds.get("side", "left") - if not is_Matrix(A): - C = self.codomain() - try: - if callable(A): - v = [C(A(g)) for g in self.domain().gens()] - A = matrix([C.coordinates(a) for a in v], ncols=C.rank()) - if side == "right": - A = A.transpose() - else: - v = [C(a) for a in A] - if side == "right": - A = matrix([C.coordinates(a) for a in v], - ncols=C.rank()).transpose() - else: - A = matrix([C.coordinates(a) for a in v], - ncols=C.rank()) - except TypeError: - pass if not self.codomain().base_ring().has_coerce_map_from( self.domain().base_ring()) and not A.is_zero(): raise TypeError("nontrivial morphisms require a coercion map" "from the base ring of the domain to the base ring of the" "codomain") - return free_module_pseudomorphism.FreeModulePseudoMorphism( - self.domain(), A, twist=self.twist, - codomain=self.codomain(), side=side) + return pseudo.FreeModulePseudoMorphism(self, A, side=side) def _matrix_space(self): r""" diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 2c3f0491407..0051aaf9528 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -31,6 +31,7 @@ from sage.modules.free_module_morphism import FreeModuleMorphism from sage.modules.free_module_homspace import FreeModuleHomspace, is_FreeModuleHomspace from sage.matrix.constructor import matrix +from sage.matrix.matrix_space import MatrixSpace lazy_import('sage.rings.derivation', 'RingDerivation') @@ -62,6 +63,8 @@ class FreeModulePseudoMorphism(Morphism): sage: f(V((1, 2))) (-4, 1) + :: + sage: P. = ZZ[]; deriv = P.derivation() sage: M = P^2 sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right"); f @@ -77,23 +80,32 @@ class FreeModulePseudoMorphism(Morphism): sage: f = M.pseudohom([[1, 2], [1, 1]], deriv) sage: f(e) (x^3 + 2*x^2 + 14*x + 8, x^3 + 7*x^2 + 13*x + 13) + + :: + + sage: Fq = GF(343); M = Fq^3; N = Fq^2; frob = Fq.frobenius_endomorphism(); z = Fq.gen() + sage: phi = M.pseudohom([[2, 3, 1], [1, 4, 6]], frob, N, side="right"); phi + Free module pseudomorphism defined as left-multiplication by the matrix + [2 3 1] + [1 4 6] + twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 + Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + sage: elem = (4*z^2 + 4*z + 3, 2, z + 5) + sage: phi(elem) + (2*z3 + 1, 6*z3^2 + 4*z3 + 5) """ - def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left"): + def __init__(self, pseudohomspace, base_morphism, side="left"): """ Constructs a pseudomorphism of free modules. INPUT: - - ``domain`` - the domain of the pseudomorphism; a free module + - ``pseudohomspace`` - the parent space of pseudomorphisms, + containing - ``base_morphism`` - either a morphism or a matrix defining a morphism - - ``twist`` - a twisting morphism, this is either a morphism or - a derivation (default: None) - - - ``codomain`` - the codomain of the pseudomorphism; a free - module (default: None) - - side -- side of the vectors acted on by the matrix (default: ``"left"``) @@ -116,28 +128,22 @@ def __init__(self, domain, base_morphism, twist=None, codomain=None, side="left" Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - from sage.structure.element import is_Matrix - Morphism.__init__(self, domain.PseudoHom(twist, codomain)) - if is_Matrix(base_morphism): - self._base_matrix = base_morphism - elif isinstance(base_morphism, Morphism): - self._base_matrix = base_morphism.matrix() + Morphism.__init__(self, pseudohomspace) + dom = pseudohomspace.domain() + codom = pseudohomspace.codomain() + rows = dom.dimension() + cols = codom.dimension() + if side == "right": + rows = codom.dimension() + cols = dom.dimension() + matrix_space = MatrixSpace(dom.coordinate_ring(), rows, cols) + if isinstance(base_morphism, FreeModuleMorphism): + self._base_matrix = matrix_space(base_morphism.matrix()) else: - self._base_matrix = matrix(domain.coordinate_ring(), - base_morphism) - self.derivation = None - self.twist_morphism = None + self._base_matrix = matrix_space(base_morphism) + self.derivation = pseudohomspace.derivation + self.twist_morphism = pseudohomspace.twist_morphism self.side = side - if isinstance(twist, Morphism): - self.twist_morphism = twist - elif isinstance(twist, RingDerivation): - self.twist_morphism = twist.parent().twisting_morphism() - if twist: - self.derivation = twist - else: - self.derivation = None - elif twist is not None: - raise TypeError("twist is not a ring morphism or derivation") def _call_(self, x): r""" From 0688f74e84eb85cc6d6a24d925d157f3a63d7bfd Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 24 Feb 2024 10:50:25 +0100 Subject: [PATCH 079/507] correct ring for _terms_of_degrees in LazySymmetricFunctions --- src/sage/rings/lazy_series_ring.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 02a477e4e01..67a89a9af80 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -939,8 +939,8 @@ def define_implicitly(self, series, equations): sage: A = T.undefined() sage: B = T.undefined() sage: T.define_implicitly([A, B], [A - X*E(B), B - Y*E(A)]) - sage: A[1] # known bug, not tested - + sage: A[:3] + [h[1] # h[], h[1] # h[1]] """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -2438,13 +2438,16 @@ def _terms_of_degree(self, n, R): EXAMPLES:: - sage: L. = LazyPowerSeriesRing(QQ) - sage: L._terms_of_degree(3, QQ) + sage: L. = LazyPowerSeriesRing(ZZ) + sage: m = L._terms_of_degree(3, QQ["z"]); m [1] - sage: L. = LazyPowerSeriesRing(QQ) - sage: L._terms_of_degree(3, QQ) + sage: m[0].parent() + Univariate Polynomial Ring in z over Rational Field + sage: L. = LazyPowerSeriesRing(ZZ) + sage: m = L._terms_of_degree(3, QQ["z"]); m [y^3, x*y^2, x^2*y, x^3] - + sage: m[0].parent() + Multivariate Polynomial Ring in x, y over Univariate Polynomial Ring in z over Rational Field """ if self._arity == 1: return [R.one()] @@ -3127,11 +3130,13 @@ def _terms_of_degree(self, n, R): sage: # needs sage.modules sage: s = SymmetricFunctions(ZZ).s() sage: L = LazySymmetricFunctions(s) - sage: L._terms_of_degree(3, ZZ) + sage: m = L._terms_of_degree(3, QQ["x"]); m [s[3], s[2, 1], s[1, 1, 1]] + sage: m[0].parent() + Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis sage: L = LazySymmetricFunctions(tensor([s, s])) - sage: L._terms_of_degree(3, ZZ) + sage: m = L._terms_of_degree(3, QQ["x"]); m [s[3] # s[], s[2, 1] # s[], s[1, 1, 1] # s[], @@ -3142,11 +3147,14 @@ def _terms_of_degree(self, n, R): s[] # s[3], s[] # s[2, 1], s[] # s[1, 1, 1]] + sage: m[0].parent() + Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis # Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis """ from sage.combinat.integer_vector import IntegerVectors from sage.misc.mrange import cartesian_product_iterator from sage.categories.tensor import tensor B = self._internal_poly_ring.base_ring() + B = B.change_ring(R) if self._arity == 1: return list(B.homogeneous_component_basis(n)) l = [] From ffbbe789297a7db18a48004a705270e693840903 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 24 Feb 2024 18:22:10 +0100 Subject: [PATCH 080/507] fix doctests --- src/sage/combinat/species/generating_series.py | 4 ++-- src/sage/data_structures/stream.py | 4 ++-- src/sage/rings/lazy_series_ring.py | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index b7b208c3d87..0764f99beee 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -142,7 +142,7 @@ def __init__(self, base_ring): sage: from sage.combinat.species.generating_series import OrdinaryGeneratingSeriesRing sage: OrdinaryGeneratingSeriesRing.options.halting_precision(15) sage: R = OrdinaryGeneratingSeriesRing(QQ) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip="_test_construction") sage: OrdinaryGeneratingSeriesRing.options._reset() # reset options """ @@ -269,7 +269,7 @@ def __init__(self, base_ring): sage: from sage.combinat.species.generating_series import ExponentialGeneratingSeriesRing sage: ExponentialGeneratingSeriesRing.options.halting_precision(15) sage: R = ExponentialGeneratingSeriesRing(QQ) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip="_test_construction") sage: ExponentialGeneratingSeriesRing.options._reset() # reset options """ diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 783d16fd155..4993c0b1ddb 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -3514,12 +3514,12 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_function, Stream_dirichlet_invert - sage: f = Stream_function(lambda n: n, True, 1) + sage: f = Stream_function(lambda n: QQ(n), True, 1) sage: h = Stream_dirichlet_invert(f, True) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] - [0, -2, -8, -12, -48] + [0, 1, -2, -3, 0] """ # this is the true order, but we want to check first if self._series._approximate_order > 1: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 67a89a9af80..01139abe8ed 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -927,8 +927,7 @@ def define_implicitly(self, series, equations): x + x*y + (x^2*y+1/2*x*y^2) + (1/2*x^3*y+2*x^2*y^2+1/6*x*y^3) + (1/6*x^4*y+3*x^3*y^2+2*x^2*y^3+1/24*x*y^4) + (1/24*x^5*y+8/3*x^4*y^2+27/4*x^3*y^3+4/3*x^2*y^4+1/120*x*y^5) - + (1/120*x^6*y+5/3*x^5*y^2+12*x^4*y^3+9*x^3*y^4+2/3*x^2*y^5+1/720*x*y^6) - + O(x,y)^8 + + O(x,y)^7 sage: h = SymmetricFunctions(QQ).h() sage: S = LazySymmetricFunctions(h) @@ -3134,7 +3133,6 @@ def _terms_of_degree(self, n, R): [s[3], s[2, 1], s[1, 1, 1]] sage: m[0].parent() Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis - sage: L = LazySymmetricFunctions(tensor([s, s])) sage: m = L._terms_of_degree(3, QQ["x"]); m [s[3] # s[], From 32cebca7b091dad75f9f7d6d8afaec2b3c8b804a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 24 Feb 2024 18:58:40 +0100 Subject: [PATCH 081/507] fix blank lines --- src/sage/rings/lazy_series.py | 1 + src/sage/rings/lazy_series_ring.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 15d39d765d1..29c2c5df94c 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5268,6 +5268,7 @@ def coefficient(n): # The arity is at least 2 gv = min(h._coeff_stream._approximate_order for h in g) + def coefficient(n): r = R.zero() for i in range(n // gv + 1): diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 01139abe8ed..25323bb38c4 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -3162,7 +3162,6 @@ def _terms_of_degree(self, n, R): l.append(tensor(m)) return l - def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, check=True): r""" Construct a lazy element in ``self`` from ``x``. From 6cd4f94e30508a538de9825a9f3b77fd569aa631 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 24 Feb 2024 22:31:58 +0100 Subject: [PATCH 082/507] another test, fix type of constant in Stream_exact --- src/sage/rings/lazy_series_ring.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 25323bb38c4..2db222d2f15 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -940,6 +940,21 @@ def define_implicitly(self, series, equations): sage: T.define_implicitly([A, B], [A - X*E(B), B - Y*E(A)]) sage: A[:3] [h[1] # h[], h[1] # h[1]] + + + Permutations with two kinds of labels such that each cycle + contains at least one element of each kind (defined + implicitely to have a test):: + + sage: p = SymmetricFunctions(QQ).p() + sage: S = LazySymmetricFunctions(p) + sage: P = S(lambda n: sum(p[la] for la in Partitions(n))) + sage: T = LazySymmetricFunctions(tensor([p, p])) + sage: X = tensor([p[1],p[[]]]) + sage: Y = tensor([p[[]],p[1]]) + sage: A = T.undefined() + sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) + sage: A[:3] """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -3278,7 +3293,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No coeff_stream = Stream_exact(p_list, order=v, - constant=0, + constant=self.base_ring().zero(), degree=degree) return self.element_class(self, coeff_stream) @@ -3322,7 +3337,7 @@ def check_homogeneous_of_degree(f, d): check_homogeneous_of_degree(e, i) coeff_stream = Stream_exact(p, order=valuation, - constant=0, + constant=self.base_ring().zero(), degree=degree) return self.element_class(self, coeff_stream) if callable(x): @@ -3332,7 +3347,7 @@ def check_homogeneous_of_degree(f, d): check_homogeneous_of_degree(e, i) coeff_stream = Stream_exact(p, order=valuation, - constant=0, + constant=self.base_ring().zero(), degree=degree) return self.element_class(self, coeff_stream) if check: From 26781b39e3f043ca0e318b31ac59012efd374349 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 25 Feb 2024 20:37:08 +0100 Subject: [PATCH 083/507] add output for a doctest --- src/sage/rings/lazy_series_ring.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 2db222d2f15..86e8aa64a00 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -955,6 +955,7 @@ def define_implicitly(self, series, equations): sage: A = T.undefined() sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) sage: A[:3] + [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream From 57e4b403c4574f54ad1a4c5b553307299338a253 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 27 Feb 2024 20:36:40 +0100 Subject: [PATCH 084/507] move UndeterminedCoefficientsFunctor to stream.py, improve substitution and retraction of undetermined coefficients --- src/sage/categories/pushout.py | 17 --------- src/sage/data_structures/stream.py | 56 +++++++++++++++++++----------- src/sage/rings/lazy_series_ring.py | 9 +++-- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 07bbb5461ac..55cd22d87dd 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -1191,23 +1191,6 @@ def _repr_(self): return "MPoly[%s]" % ','.join(self.vars) -class UndeterminedCoefficientsFunctor(ConstructionFunctor): - rank = 0 - - def __init__(self): - from .rings import Rings - Functor.__init__(self, Rings(), Rings()) - - def _apply_functor(self, R): - from sage.data_structures.stream import UndeterminedCoefficientsRing - return UndeterminedCoefficientsRing(R) - - __hash__ = ConstructionFunctor.__hash__ - - def _repr_(self): - return "UndeterminedCoefficients" - - class InfinitePolynomialFunctor(ConstructionFunctor): r""" A Construction Functor for Infinite Polynomial Rings (see :mod:`~sage.rings.polynomial.infinite_polynomial_ring`). diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 4993c0b1ddb..21d863894fc 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -102,9 +102,10 @@ from sage.misc.lazy_import import lazy_import from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis +from sage.categories.rings import Rings from sage.misc.cachefunc import cached_method -from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense -from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing +from sage.categories.functor import Functor +from sage.categories.pushout import ConstructionFunctor from collections import defaultdict lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1328,6 +1329,10 @@ def variables(self): def rational_function(self): return self._p + def is_constant(self): + return (self._p.numerator().is_constant() + and self._p.denominator().is_constant()) + def subs(self, in_dict=None, *args, **kwds): """ EXAMPLES:: @@ -1360,7 +1365,20 @@ def subs(self, in_dict=None, *args, **kwds): return P.element_class(P, P._PF(num / den)) -from sage.categories.pushout import UndeterminedCoefficientsFunctor +class UndeterminedCoefficientsFunctor(ConstructionFunctor): + rank = 0 + + def __init__(self): + Functor.__init__(self, Rings(), Rings()) + + def _apply_functor(self, R): + return UndeterminedCoefficientsRing(R) + + __hash__ = ConstructionFunctor.__hash__ + + def _repr_(self): + return "UndeterminedCoefficients" + class UndeterminedCoefficientsRing(UniqueRepresentation, Parent): """ @@ -1531,7 +1549,7 @@ def define_implicitly(self, series, initial_values, equations, - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - ``base_ring`` -- the base ring - - ``coefficient_ring`` -- the ring containing the coefficients (after substitution) + - ``coefficient_ring`` -- the ring containing the elements of the stream (after substitution) - ``terms_of_degree`` -- a function returning the list of terms of a given degree """ @@ -1550,7 +1568,8 @@ def define_implicitly(self, series, initial_values, equations, self._coefficient_ring = coefficient_ring self._base_ring = base_ring self._P = UndeterminedCoefficientsRing(self._base_ring) - # elements of the stream have self._P as base ring + if self._coefficient_ring != self._base_ring: + self._U = self._coefficient_ring.change_ring(self._P) self._uncomputed = True self._eqs = equations self._series = series @@ -1684,28 +1703,23 @@ def _subs_in_caches(self, var, val): # substitute variable and determine last good element good = m for i0, i in enumerate(indices): - # the following looks dangerous - could there be a - # ring that contains the UndeterminedCoefficientRing? - # it is not enough to look at the parent of - # s._cache[i] c = s._cache[i] - if c not in self._coefficient_ring: - if self._base_ring == self._coefficient_ring: + if self._base_ring == self._coefficient_ring: + if c.parent() == self._P: c = c.subs({var: val}) - f = c.rational_function() - if f in self._coefficient_ring: - c = self._coefficient_ring(f) - else: + if c.is_constant(): + c = self._base_ring(c.rational_function()) + else: + good = m - i0 - 1 + else: + if c.parent() == self._U: c = c.map_coefficients(lambda e: e.subs({var: val})) try: - c = c.map_coefficients(lambda e: (e if e in self._base_ring - else self._base_ring(e.rational_function())), + c = c.map_coefficients(lambda e: self._base_ring(e.rational_function()), self._base_ring) except TypeError: - pass - s._cache[i] = c - if c not in self._coefficient_ring: - good = m - i0 - 1 + good = m - i0 - 1 + s._cache[i] = c self._good_cache[j] += good # fix approximate_order and true_order ao = s._approximate_order diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 86e8aa64a00..43fc00b2c15 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -941,7 +941,6 @@ def define_implicitly(self, series, equations): sage: A[:3] [h[1] # h[], h[1] # h[1]] - Permutations with two kinds of labels such that each cycle contains at least one element of each kind (defined implicitely to have a test):: @@ -954,7 +953,7 @@ def define_implicitly(self, series, equations): sage: Y = tensor([p[[]],p[1]]) sage: A = T.undefined() sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) - sage: A[:3] + sage: A[:4] [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) @@ -2495,7 +2494,7 @@ def gen(self, n=0): if len(self.variable_names()) == 1: coeff_stream = Stream_exact([BR.one()], constant=BR.zero(), order=1) else: - coeff_stream = Stream_exact([R.gen(n)], constant=BR.zero(), order=1) + coeff_stream = Stream_exact([R.gen(n)], constant=R.zero(), order=1) return self.element_class(self, coeff_stream) def ngens(self): @@ -3338,7 +3337,7 @@ def check_homogeneous_of_degree(f, d): check_homogeneous_of_degree(e, i) coeff_stream = Stream_exact(p, order=valuation, - constant=self.base_ring().zero(), + constant=self._laurent_poly_ring.zero(), degree=degree) return self.element_class(self, coeff_stream) if callable(x): @@ -3348,7 +3347,7 @@ def check_homogeneous_of_degree(f, d): check_homogeneous_of_degree(e, i) coeff_stream = Stream_exact(p, order=valuation, - constant=self.base_ring().zero(), + constant=self._laurent_poly_ring.zero(), degree=degree) return self.element_class(self, coeff_stream) if check: From ab229656217c073fbebd86a14e5b957b38e8baed Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 27 Feb 2024 21:55:41 +0100 Subject: [PATCH 085/507] micro-optimization --- src/sage/data_structures/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 21d863894fc..3836985aaba 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1362,7 +1362,7 @@ def subs(self, in_dict=None, *args, **kwds): d_den = {P._P(v): c for v, c in in_dict.items() if v in V_den} den = p_den.subs(d_den) - return P.element_class(P, P._PF(num / den)) + return P.element_class(P, P._PF(num, den)) class UndeterminedCoefficientsFunctor(ConstructionFunctor): From 4032e432fa6cb771f65abcbdc21a9ca86c380221 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 17:42:34 +0100 Subject: [PATCH 086/507] Adapted `.ramified_places` slightly, removed docstring modifications --- src/doc/en/reference/references/index.rst | 3 +- .../algebras/quatalg/quaternion_algebra.py | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index b0194d1eef7..09b78514e1c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -6360,8 +6360,7 @@ REFERENCES: .. [Voi2012] \J. Voight. Identifying the matrix ring: algorithms for quaternion algebras and quadratic forms, to appear. -.. [Voi2021] \J. Voight. Quaternion Algebras. Graduate Texts in - Mathematics 288. Springer Cham, 2021. +.. [Voi2021] \J. Voight. Quaternion algebras, Springer Nature (2021). .. [VS06] \G.D. Villa Salvador. Topics in the Theory of Algebraic Function Fields. Birkh\"auser, 2006. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index e655004fa6f..97ffc7c21bc 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1202,18 +1202,21 @@ def ramified_places(self, inf=True): return ram_fin, [] - try: - # Over the number field F, first compute the finite ramified places - ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(self._a), - F.primes_above(self._b)) if F.hilbert_symbol(self._a, self._b, p) == -1] + # At this point F needs to be a number field + # Note: Support for global function fields will be added in a future update + if not F is in NumberFields(): + raise NotImplementedError("base field must be rational numbers or a number field") + + # Over the number field F, first compute the finite ramified places + ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(self._a), + F.primes_above(self._b)) if F.hilbert_symbol(self._a, self._b, p) == -1] - if not inf: - return ram_fin + if not inf: + return ram_fin - # At this point the infinite ramified places also need to be computed - return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(self._a, self._b, e) == -1] - except (AttributeError, NotImplementedError): - raise ValueError("base field must be rational numbers or a number field") + # At this point the infinite ramified places also need to be computed + return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(self._a, self._b, e) == -1] + @cached_method def ramified_primes(self): @@ -2035,8 +2038,7 @@ def is_maximal(self): r""" Check whether the order of ``self`` is maximal in the ambient quaternion algebra. - Only implemented for quaternion algebras over number fields; for reference, - see Theorem 15.5.5 in [Voi2021]_. + Only works in quaternion algebras over number fields OUTPUT: Boolean @@ -3502,12 +3504,12 @@ def cyclic_right_subideals(self, p, alpha=None): def is_integral(self): r""" - Checks whether a quaternion fractional ideal is integral. An ideal in a quaternion algebra - is integral if and only if it is contained in its left order. If the left order is already - defined this method just checks this definition, otherwise it uses one of the alternative - definitions from Lemma 16.2.8 of [Voi2021]_. + Check if a quaternion fractional ideal is integral. An ideal in a quaternion algebra is + said integral if it is contained in its left order. If the left order is already defined it just + check the definition, otherwise it uses one of the alternative definition of Lemma 16.2.8 of + [Voi2021]_. - OUTPUT: A boolean. + OUTPUT: a boolean. EXAMPLES:: From 89dae3f4289aee0b8bfcb97ce2001e2f8b577a6a Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 18:02:52 +0100 Subject: [PATCH 087/507] Fixed LINT --- src/sage/algebras/quatalg/quaternion_algebra.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 97ffc7c21bc..1e7ff5184ca 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1204,9 +1204,9 @@ def ramified_places(self, inf=True): # At this point F needs to be a number field # Note: Support for global function fields will be added in a future update - if not F is in NumberFields(): - raise NotImplementedError("base field must be rational numbers or a number field") - + if F not in NumberFields(): + raise ValueError("base field must be rational numbers or a number field") + # Over the number field F, first compute the finite ramified places ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(self._a), F.primes_above(self._b)) if F.hilbert_symbol(self._a, self._b, p) == -1] @@ -1216,7 +1216,6 @@ def ramified_places(self, inf=True): # At this point the infinite ramified places also need to be computed return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(self._a, self._b, e) == -1] - @cached_method def ramified_primes(self): @@ -1300,10 +1299,7 @@ def discriminant(self): if is_RationalField(F): return ZZ.prod(self.ramified_places(inf=False)) - try: - return F.ideal(F.prod(self.ramified_places(inf=False))) - except NotImplementedError: - raise ValueError("base field must be rational numbers or a number field") + return F.ideal(F.prod(self.ramified_places(inf=False))) def is_isomorphic(self, A) -> bool: """ From deb764e284b0b401012ee871daea7fecffe7a4bb Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 8 Mar 2024 21:26:42 +0100 Subject: [PATCH 088/507] Small rewrite of `.is_totally_definite()` --- src/sage/algebras/quatalg/quaternion_algebra.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 1e7ff5184ca..0880281edab 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1110,12 +1110,12 @@ def is_totally_definite(self): if is_RationalField(F): return self.is_definite() - try: - E = F.real_embeddings() - return [e for e in E if F.hilbert_symbol(self._a, self._b, e) == -1] == E - except (AttributeError, NotImplementedError): + if F not in NumberFields(): raise ValueError("base field must be rational numbers or a number field") + E = F.real_embeddings() + return [e for e in E if F.hilbert_symbol(self._a, self._b, e) == -1] == E + @cached_method def ramified_places(self, inf=True): """ From ad7d628d43e78e566077151e8838a13fb96a0e9a Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sat, 9 Mar 2024 00:10:17 +0100 Subject: [PATCH 089/507] Cleaned up some code --- .../algebras/quatalg/quaternion_algebra.py | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 0880281edab..92c7efc852d 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -403,8 +403,8 @@ def is_commutative(self) -> bool: def is_division_algebra(self) -> bool: """ - Checks whether the quaternion algebra ``self`` is a division algebra, i.e. whether - every nonzero element in ``self`` is invertible. + Check whether the quaternion algebra ``self`` is a division algebra, + i.e. whether every nonzero element in ``self`` is invertible. EXAMPLES:: @@ -432,8 +432,8 @@ def is_division_algebra(self) -> bool: def is_matrix_ring(self) -> bool: """ - Checks whether the quaternion algebra ``self`` is isomorphic to the 2x2 matrix - ring over the base field. + Check whether the quaternion algebra ``self`` is isomorphic to the + 2x2 matrix ring over the base field. EXAMPLES:: @@ -1057,9 +1057,11 @@ def inner_product_matrix(self): def is_definite(self): """ - Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the - unique Archimedean place of its base field QQ. This is the case if and only if both - invariants of ``self`` are negative; see Exercise 2.4(c) in [Voi2021]_. + Check whether the quaternion algebra ``self`` is definite, i.e. whether + it ramifies at the unique Archimedean place of its base field `QQ`. + + A quaternion algebra over `QQ` is definite if and only if both of + its invariants are negative (see Exercise 2.4(c) in [Voi2021]_). EXAMPLES:: @@ -1080,8 +1082,8 @@ def is_definite(self): def is_totally_definite(self): """ - Checks whether the quaternion algebra ``self`` is totally definite, i.e. whether it ramifies - at all real Archimedean places of its base number field. + Check whether the quaternion algebra ``self`` is totally definite, i.e. + whether it ramifies at all real Archimedean places of its base number field. EXAMPLES:: @@ -1119,11 +1121,13 @@ def is_totally_definite(self): @cached_method def ramified_places(self, inf=True): """ - Return the places of the base number field at which the quaternion algebra ``self`` ramifies. + Return the places of the base number field at which the quaternion + algebra``self`` ramifies. - Note: The initial choice of primes (in the case F = QQ) respectively of prime ideals (in the - number field case) to check ramification for is motivated by 12.4.12(a) in [Voi2021]_. The - restriction to real Archimedean embeddings is due to 14.5.8 in [Voi2021]_. + Note: The initial choice of primes (in the case F = QQ) respectively + of prime ideals (in the number field case) to check ramification for + is motivated by 12.4.12(a) in [Voi2021]_. The restriction to real + Archimedean embeddings is due to 14.5.8 in [Voi2021]_. INPUT: @@ -1131,11 +1135,12 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which ``self`` ramifies (given as elements of ZZ, - sorted small to large, if ``self`` is defined over the rational field QQ, respectively as - fractional ideals of the number field's ring of integers, otherwise) and, if ``inf`` is set - to ``True``, also the Archimedean (AKA infinite) places at which ``self`` ramifies (given - by real embeddings of the base field). + The non-Archimedean (AKA finite) places at which ``self`` ramifies (given + as elements of ZZ, sorted small to large, if ``self`` is defined over the + rational field QQ, respectively as fractional ideals of the number field's + ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the + Archimedean (AKA infinite) places at which ``self`` ramifies (given by + real embeddings of the base field). EXAMPLES:: @@ -1185,18 +1190,22 @@ def ramified_places(self, inf=True): raise ValueError("inf must be a truth value") F = self.base_ring() + a = self._a + b = self._b - # For efficiency (and to not convert QQ into a number field manually), we handle F = QQ first + # For efficiency (and to not convert QQ into a number field manually), + # we handle the case F = QQ first if is_RationalField(F): - ram_fin = sorted([p for p in set([2]).union(prime_divisors(self._a.numerator()), - prime_divisors(self._a.denominator()), prime_divisors(self._b.numerator()), - prime_divisors(self._b.denominator())) if hilbert_symbol(self._a, self._b, p) == -1]) + ram_fin = sorted([p for p in set([2]).union( + prime_divisors(a.numerator()), prime_divisors(a.denominator()), + prime_divisors(b.numerator()), prime_divisors(b.denominator())) + if hilbert_symbol(a, b, p) == -1]) if not inf: return ram_fin - # The given quaternion algebra ramifies at the unique infinite place of QQ, by definition, - # if and only if it is definite + # The given quaternion algebra ramifies at the unique infinite place + # of QQ, by definition, if and only if it is definite if self.is_definite(): return ram_fin, QQ.places() @@ -1208,14 +1217,14 @@ def ramified_places(self, inf=True): raise ValueError("base field must be rational numbers or a number field") # Over the number field F, first compute the finite ramified places - ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(self._a), - F.primes_above(self._b)) if F.hilbert_symbol(self._a, self._b, p) == -1] + ram_fin = [p for p in set(F.primes_above(2)).union(F.primes_above(a), + F.primes_above(b)) if F.hilbert_symbol(a, b, p) == -1] if not inf: return ram_fin # At this point the infinite ramified places also need to be computed - return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(self._a, self._b, e) == -1] + return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(a, b, e) == -1] @cached_method def ramified_primes(self): @@ -1303,10 +1312,10 @@ def discriminant(self): def is_isomorphic(self, A) -> bool: """ - Checks whether ``self`` and ``A`` are isomorphic quaternion algebras. + Check whether ``self`` and ``A`` are isomorphic quaternion algebras. - Currently only implemented over a number field; motivated by Main Theorem 14.6.1 - in [Voi2021]_, noting that QQ has a unique infinite place. + Currently only implemented over a number field; motivated by Main + Theorem 14.6.1 in [Voi2021]_, noting that QQ has a unique infinite place. INPUT: @@ -1334,7 +1343,7 @@ def is_isomorphic(self, A) -> bool: F = self.base_ring() if F != A.base_ring(): - raise ValueError("both quaternion algebras must be defined over the same base ring") + raise ValueError("both quaternion algebras must be defined over the same ring") if is_RationalField(F): return self.ramified_places(inf=False) == A.ramified_places(inf=False) From 6319a5636a36842484280362e0b74e24d49af456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Mar 2024 09:37:22 +0100 Subject: [PATCH 090/507] Update rings.py issue instead of trac --- src/sage/categories/rings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 95fef4453c1..6dc83b6b746 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -589,7 +589,7 @@ def _ideal_class_(self, n=0): EXAMPLES: - Since :trac:`7797`, non-commutative rings have ideals as well:: + Since :issue:`7797`, non-commutative rings have ideals as well:: sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules sage: A._ideal_class_() # needs sage.combinat sage.modules @@ -646,7 +646,7 @@ def zero_ideal(self): TESTS: - Make sure that :trac:`13644` is fixed:: + Make sure that :issue:`13644` is fixed:: sage: # needs sage.rings.padics sage: K = Qp(3) From afe236396113102163e9d374d04fcb1ecb4c33b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Mar 2024 14:14:49 +0100 Subject: [PATCH 091/507] move ideals to the category of rngs (non-unital rings) --- .../finite_dimensional_algebra.py | 2 +- .../finite_dimensional_algebra_morphism.py | 4 +- src/sage/categories/rings.py | 114 ---------------- src/sage/categories/rngs.py | 124 +++++++++++++++++- 4 files changed, 125 insertions(+), 119 deletions(-) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py index a3b8742b4e0..f2d4600a35b 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py @@ -760,7 +760,7 @@ def quotient_map(self, ideal): EXAMPLES:: sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), - ....: Matrix([[0, 1], [0, 0]])]) + ....: Matrix([[0, 1], [0, 0]])], assume_associative=True) sage: q0 = A.quotient_map(A.zero_ideal()); q0 Morphism from Finite-dimensional algebra of degree 2 over Finite Field of size 3 diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py index d2114511ae6..bd5b1691a06 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py @@ -181,7 +181,8 @@ def inverse_image(self, I): EXAMPLES:: sage: A = FiniteDimensionalAlgebra(QQ, [Matrix([[1, 0], [0, 1]]), - ....: Matrix([[0, 1], [0, 0]])]) + ....: Matrix([[0, 1], [0, 0]])], + ....: assume_associative=True) sage: I = A.maximal_ideal() # needs sage.libs.pari sage: q = A.quotient_map(I) # needs sage.libs.pari sage: B = q.codomain() # needs sage.libs.pari @@ -191,6 +192,7 @@ def inverse_image(self, I): coker_I = I.basis_matrix().transpose().kernel().basis_matrix().transpose() return self.domain().ideal((self._matrix * coker_I).kernel().basis_matrix(), given_by_matrix=True) + class FiniteDimensionalAlgebraHomset(RingHomset_generic): """ Set of morphisms between two finite-dimensional algebras. diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 6dc83b6b746..227a3301f61 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -543,120 +543,6 @@ def __pow__(self, n): from sage.modules.free_module import FreeModule return FreeModule(self, n) - @cached_method - def ideal_monoid(self): - """ - The monoid of the ideals of this ring. - - .. NOTE:: - - The code is copied from the base class of rings. - This is since there are rings that do not inherit - from that class, such as matrix algebras. See - :issue:`7797`. - - EXAMPLES:: - - sage: # needs sage.modules - sage: MS = MatrixSpace(QQ, 2, 2) - sage: isinstance(MS, Ring) - False - sage: MS in Rings() - True - sage: MS.ideal_monoid() - Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices - over Rational Field - - Note that the monoid is cached:: - - sage: MS.ideal_monoid() is MS.ideal_monoid() # needs sage.modules - True - """ - try: - from sage.rings.ideal_monoid import IdealMonoid - return IdealMonoid(self) - except TypeError: - from sage.rings.noncommutative_ideals import IdealMonoid_nc - return IdealMonoid_nc(self) - - def _ideal_class_(self, n=0): - r""" - Return a callable object that can be used to create ideals in this - ring. - - The argument `n`, standing for the number of generators - of the ideal, is ignored. - - EXAMPLES: - - Since :issue:`7797`, non-commutative rings have ideals as well:: - - sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules - sage: A._ideal_class_() # needs sage.combinat sage.modules - - """ - from sage.rings.noncommutative_ideals import Ideal_nc - return Ideal_nc - - def principal_ideal(self, gen, coerce=True): - """ - Return the principal ideal generated by ``gen``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: R.principal_ideal(x+2*y) - Ideal (x + 2*y) of Multivariate Polynomial Ring in x, y over Integer Ring - """ - C = self._ideal_class_(1) - if coerce: - gen = self(gen) - return C(self, [gen]) - - @cached_method - def unit_ideal(self): - """ - Return the unit ideal of this ring. - - EXAMPLES:: - - sage: Zp(7).unit_ideal() # needs sage.rings.padics - Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 - """ - return self.principal_ideal(self.one(), coerce=False) - - @cached_method - def zero_ideal(self): - """ - Return the zero ideal of this ring (cached). - - EXAMPLES:: - - sage: ZZ.zero_ideal() - Principal ideal (0) of Integer Ring - sage: QQ.zero_ideal() - Principal ideal (0) of Rational Field - sage: QQ['x'].zero_ideal() - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - - The result is cached:: - - sage: ZZ.zero_ideal() is ZZ.zero_ideal() - True - - TESTS: - - Make sure that :issue:`13644` is fixed:: - - sage: # needs sage.rings.padics - sage: K = Qp(3) - sage: R. = K[] - sage: L. = K.extension(a^2-3) - sage: L.ideal(a) - Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 - """ - return self.principal_ideal(self.zero(), coerce=False) - def characteristic(self): """ Return the characteristic of this ring. diff --git a/src/sage/categories/rngs.py b/src/sage/categories/rngs.py index 1841b45d1d5..3d890a4af74 100644 --- a/src/sage/categories/rngs.py +++ b/src/sage/categories/rngs.py @@ -1,18 +1,20 @@ r""" Rngs """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) # 2012 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from sage.misc.cachefunc import cached_method from sage.categories.category_with_axiom import CategoryWithAxiom from sage.misc.lazy_import import LazyImport from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas + class Rngs(CategoryWithAxiom): """ The category of rngs. @@ -47,3 +49,119 @@ class Rngs(CategoryWithAxiom): _base_category_class_and_axiom = (MagmasAndAdditiveMagmas.Distributive.AdditiveAssociative.AdditiveCommutative.AdditiveUnital.Associative, "AdditiveInverse") Unital = LazyImport('sage.categories.rings', 'Rings', at_startup=True) + + class ParentMethods: + + @cached_method + def ideal_monoid(self): + """ + The monoid of the ideals of this ring. + + .. NOTE:: + + The code is copied from the base class of rings. + This is since there are rings that do not inherit + from that class, such as matrix algebras. See + :issue:`7797`. + + EXAMPLES:: + + sage: # needs sage.modules + sage: MS = MatrixSpace(QQ, 2, 2) + sage: isinstance(MS, Ring) + False + sage: MS in Rings() + True + sage: MS.ideal_monoid() + Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices + over Rational Field + + Note that the monoid is cached:: + + sage: MS.ideal_monoid() is MS.ideal_monoid() # needs sage.modules + True + """ + try: + from sage.rings.ideal_monoid import IdealMonoid + return IdealMonoid(self) + except TypeError: + from sage.rings.noncommutative_ideals import IdealMonoid_nc + return IdealMonoid_nc(self) + + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + ring. + + The argument `n`, standing for the number of generators + of the ideal, is ignored. + + EXAMPLES: + + Since :issue:`7797`, non-commutative rings have ideals as well:: + + sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules + sage: A._ideal_class_() # needs sage.combinat sage.modules + + """ + from sage.rings.noncommutative_ideals import Ideal_nc + return Ideal_nc + + def principal_ideal(self, gen, coerce=True): + """ + Return the principal ideal generated by ``gen``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: R.principal_ideal(x+2*y) + Ideal (x + 2*y) of Multivariate Polynomial Ring in x, y over Integer Ring + """ + C = self._ideal_class_(1) + if coerce: + gen = self(gen) + return C(self, [gen]) + + @cached_method + def unit_ideal(self): + """ + Return the unit ideal of this ring. + + EXAMPLES:: + + sage: Zp(7).unit_ideal() # needs sage.rings.padics + Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 + """ + return self.principal_ideal(self.one(), coerce=False) + + @cached_method + def zero_ideal(self): + """ + Return the zero ideal of this ring (cached). + + EXAMPLES:: + + sage: ZZ.zero_ideal() + Principal ideal (0) of Integer Ring + sage: QQ.zero_ideal() + Principal ideal (0) of Rational Field + sage: QQ['x'].zero_ideal() + Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field + + The result is cached:: + + sage: ZZ.zero_ideal() is ZZ.zero_ideal() + True + + TESTS: + + Make sure that :issue:`13644` is fixed:: + + sage: # needs sage.rings.padics + sage: K = Qp(3) + sage: R. = K[] + sage: L. = K.extension(a^2-3) + sage: L.ideal(a) + Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 + """ + return self.principal_ideal(self.zero(), coerce=False) From 9a518a662f0b176bbaedb736faa99e682a5bca4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Mar 2024 14:43:16 +0100 Subject: [PATCH 092/507] move back unit_ideal to cat. of rings --- src/sage/categories/rings.py | 12 ++++++++++++ src/sage/categories/rngs.py | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 227a3301f61..3fa7757d161 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -543,6 +543,18 @@ def __pow__(self, n): from sage.modules.free_module import FreeModule return FreeModule(self, n) + @cached_method + def unit_ideal(self): + """ + Return the unit ideal of this ring. + + EXAMPLES:: + + sage: Zp(7).unit_ideal() # needs sage.rings.padics + Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 + """ + return self.principal_ideal(self.one(), coerce=False) + def characteristic(self): """ Return the characteristic of this ring. diff --git a/src/sage/categories/rngs.py b/src/sage/categories/rngs.py index 3d890a4af74..1adf932cb1c 100644 --- a/src/sage/categories/rngs.py +++ b/src/sage/categories/rngs.py @@ -122,18 +122,6 @@ def principal_ideal(self, gen, coerce=True): gen = self(gen) return C(self, [gen]) - @cached_method - def unit_ideal(self): - """ - Return the unit ideal of this ring. - - EXAMPLES:: - - sage: Zp(7).unit_ideal() # needs sage.rings.padics - Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 - """ - return self.principal_ideal(self.one(), coerce=False) - @cached_method def zero_ideal(self): """ From a2c85642611ab051d574f031c160c6a9ec07335f Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Mon, 25 Mar 2024 18:00:41 +0100 Subject: [PATCH 093/507] Small doctest fix --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 7689248b5b5..59b8901b93e 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1179,7 +1179,7 @@ def ramified_places(self, inf=True): To: Real Field with 53 bits of precision Defn: a |--> -0.618033988749895]) - sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).ramified_places() # needs sage.symbolic + sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).ramified_places() # needs sage.symbolic ([], []) sage: QuaternionAlgebra(RR(2.),1).ramified_places() Traceback (most recent call last): From 914d1b883d2ac5fbd8c866fed154bda4a82e128c Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Mon, 25 Mar 2024 18:18:05 +0100 Subject: [PATCH 094/507] Error type correction --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 59b8901b93e..ccab3266648 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1187,7 +1187,7 @@ def ramified_places(self, inf=True): ValueError: base field must be rational numbers or a number field """ if not isinstance(inf, bool): - raise ValueError("inf must be a truth value") + raise TypeError("inf must be a truth value") F = self.base_ring() a = self._a From 9c324bf9a5a351e3279c91180c68c37fe3f430f3 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 13:40:02 +0100 Subject: [PATCH 095/507] Remove redundant copy of `.is_definite()` --- .../algebras/quatalg/quaternion_algebra.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 1b4972c373c..658f8c22869 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -930,29 +930,6 @@ def invariants(self): """ return self._a, self._b - def is_definite(self): - """ - Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the - unique Archimedean place of its base field QQ. This is the case if and only if both - invariants of ``self`` are negative. - - EXAMPLES:: - - sage: QuaternionAlgebra(QQ,-5,-2).is_definite() - True - sage: QuaternionAlgebra(1).is_definite() - False - - sage: QuaternionAlgebra(RR(2.),1).is_definite() - Traceback (most recent call last): - ... - ValueError: base field must be rational numbers - """ - if not is_RationalField(self.base_ring()): - raise ValueError("base field must be rational numbers") - a, b = self.invariants() - return a < 0 and b < 0 - def __eq__(self, other): """ Compare self and other. From 15f8fa5ccadffbaba5f5c7c35cf851c3523cf822 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 13:51:25 +0100 Subject: [PATCH 096/507] Some doctest refactoring Amend: Refactored `.is_definite()` --- .../algebras/quatalg/quaternion_algebra.py | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 658f8c22869..82401e1224e 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -403,8 +403,8 @@ def is_commutative(self) -> bool: def is_division_algebra(self) -> bool: """ - Check whether the quaternion algebra ``self`` is a division algebra, - i.e. whether every nonzero element in ``self`` is invertible. + Check whether the quaternion algebra is a division algebra, + i.e. whether every nonzero element in it is invertible. EXAMPLES:: @@ -432,7 +432,7 @@ def is_division_algebra(self) -> bool: def is_matrix_ring(self) -> bool: """ - Check whether the quaternion algebra ``self`` is isomorphic to the + Check whether the quaternion algebra is isomorphic to the 2x2 matrix ring over the base field. EXAMPLES:: @@ -1056,11 +1056,11 @@ def inner_product_matrix(self): return diagonal_matrix(self.base_ring(), [2, -2*a, -2*b, 2*a*b]) def is_definite(self): - """ - Check whether the quaternion algebra ``self`` is definite, i.e. whether - it ramifies at the unique Archimedean place of its base field `QQ`. + r""" + Check whether the given quaternion algebra is definite, i.e. whether + it ramifies at the unique Archimedean place of its base field `\QQ`. - A quaternion algebra over `QQ` is definite if and only if both of + A quaternion algebra over `\QQ` is definite if and only if both of its invariants are negative (see Exercise 2.4(c) in [Voi2021]_). EXAMPLES:: @@ -1121,13 +1121,13 @@ def is_totally_definite(self): @cached_method def ramified_places(self, inf=True): """ - Return the places of the base number field at which the quaternion - algebra``self`` ramifies. + Return the places of the base number field at which the given + quaternion algebra ramifies. - Note: The initial choice of primes (in the case F = QQ) respectively - of prime ideals (in the number field case) to check ramification for - is motivated by 12.4.12(a) in [Voi2021]_. The restriction to real - Archimedean embeddings is due to 14.5.8 in [Voi2021]_. + Note: The initial choice of primes (for the base field ``\\QQ``) + respectively of prime ideals (in the number field case) to check + ramification for is motivated by 12.4.12(a) in [Voi2021]_. The + restriction to real embeddings is due to 14.5.8 in [Voi2021]_. INPUT: @@ -1135,12 +1135,12 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which ``self`` ramifies (given - as elements of ZZ, sorted small to large, if ``self`` is defined over the - rational field QQ, respectively as fractional ideals of the number field's - ring of integers, otherwise) and, if ``inf`` is set to ``True``, also the - Archimedean (AKA infinite) places at which ``self`` ramifies (given by - real embeddings of the base field). + The non-Archimedean (AKA finite) places at which ``self`` ramifies + (given as elements of ZZ, sorted small to large, if ``self`` is + defined over the rational field QQ, respectively as fractional ideals + of the number field's ring of integers, otherwise) and, if ``inf`` is + set to ``True``, also the Archimedean (AKA infinite) places at which + ``self`` ramifies (given by real embeddings of the base field). EXAMPLES:: @@ -1229,13 +1229,15 @@ def ramified_places(self, inf=True): @cached_method def ramified_primes(self): """ - Return the (finite) primes of the base number field at which the quaternion algebra ``self`` ramifies. + Return the (finite) primes of the base number field at which + the given quaternion algebra ramifies. OUTPUT: - The list of finite primes at which ``self`` ramifies; given as integers, sorted - small to large, if ``self`` is defined over QQ, and as fractional ideals in the - ring of integers of the base number field otherwise. + The list of finite primes at which ``self`` ramifies; given as + integers, sorted small to large, if ``self`` is defined over `\\QQ`, + and as fractional ideals in the ring of integers of the base number + field otherwise. EXAMPLES:: @@ -1265,15 +1267,16 @@ def ramified_primes(self): @cached_method def discriminant(self): - """ - Return the discriminant of the quaternion algebra ``self``, i.e. the product of the - finite places it ramifies at. + r""" + Return the discriminant of the given quaternion algebra, i.e. the + product of the finite places it ramifies at. OUTPUT: - The discriminant of this quaternion algebra (which has to be defined over a number field), - as an element of ZZ if ``self`` is defined over QQ, and as a fractional ideal in the - ring of integers of the base number field otherwise. + The discriminant of this quaternion algebra (which has to be defined + over a number field), as an element of `\ZZ` if the quaternion algebra + is defined over `\QQ`, and as a fractional ideal in the ring of + integers of the base number field otherwise. EXAMPLES:: @@ -1312,10 +1315,10 @@ def discriminant(self): def is_isomorphic(self, A) -> bool: """ - Check whether ``self`` and ``A`` are isomorphic quaternion algebras. + Check whether the given quaternion algebra is isomorphic to ``A``. Currently only implemented over a number field; motivated by Main - Theorem 14.6.1 in [Voi2021]_, noting that QQ has a unique infinite place. + Theorem 14.6.1 in [Voi2021]_, noting that QQ has a unique real place. INPUT: From d09005c52d4933dbd1b4662e90b5e358ab7edfa1 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 14:17:53 +0100 Subject: [PATCH 097/507] More small refactoring Amend: Even more refactoring of `.is_definite()` and `.is_totally_definite()` Amend 2: Corrected backtick error --- .../algebras/quatalg/quaternion_algebra.py | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 82401e1224e..fd287b0f490 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -403,7 +403,7 @@ def is_commutative(self) -> bool: def is_division_algebra(self) -> bool: """ - Check whether the quaternion algebra is a division algebra, + Check whether this quaternion algebra is a division algebra, i.e. whether every nonzero element in it is invertible. EXAMPLES:: @@ -432,7 +432,7 @@ def is_division_algebra(self) -> bool: def is_matrix_ring(self) -> bool: """ - Check whether the quaternion algebra is isomorphic to the + Check whether this quaternion algebra is isomorphic to the 2x2 matrix ring over the base field. EXAMPLES:: @@ -1057,8 +1057,8 @@ def inner_product_matrix(self): def is_definite(self): r""" - Check whether the given quaternion algebra is definite, i.e. whether - it ramifies at the unique Archimedean place of its base field `\QQ`. + Check whether this quaternion algebra is definite, i.e. whether + it ramifies at the unique real place of its base field `\QQ`. A quaternion algebra over `\QQ` is definite if and only if both of its invariants are negative (see Exercise 2.4(c) in [Voi2021]_). @@ -1082,8 +1082,10 @@ def is_definite(self): def is_totally_definite(self): """ - Check whether the quaternion algebra ``self`` is totally definite, i.e. - whether it ramifies at all real Archimedean places of its base number field. + Check whether this quaternion algebra is totally definite. + + A quaternion algebra defined over a number field is totally definite + if it ramifies at all real places of its base field. EXAMPLES:: @@ -1120,11 +1122,11 @@ def is_totally_definite(self): @cached_method def ramified_places(self, inf=True): - """ - Return the places of the base number field at which the given + r""" + Return the places of the base number field at which this quaternion algebra ramifies. - Note: The initial choice of primes (for the base field ``\\QQ``) + Note: The initial choice of primes (for the base field `\QQ`) respectively of prime ideals (in the number field case) to check ramification for is motivated by 12.4.12(a) in [Voi2021]_. The restriction to real embeddings is due to 14.5.8 in [Voi2021]_. @@ -1135,12 +1137,13 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which ``self`` ramifies - (given as elements of ZZ, sorted small to large, if ``self`` is - defined over the rational field QQ, respectively as fractional ideals - of the number field's ring of integers, otherwise) and, if ``inf`` is - set to ``True``, also the Archimedean (AKA infinite) places at which - ``self`` ramifies (given by real embeddings of the base field). + The non-Archimedean (AKA finite) places at which the quaternion + algebra ramifies (given as elements of `\ZZ`, sorted small to + large, if the algebra is defined over the rational field `\QQ`, + respectively as fractional ideals of the number field's ring of + integers, otherwise) and, if ``inf`` is set to ``True``, also the + Archimedean (AKA infinite) places at which the quaternion algebra + ramifies (given by real embeddings of the base field). EXAMPLES:: @@ -1229,15 +1232,15 @@ def ramified_places(self, inf=True): @cached_method def ramified_primes(self): """ - Return the (finite) primes of the base number field at which - the given quaternion algebra ramifies. + Return the (finite) primes of the base number field at + which this quaternion algebra ramifies. OUTPUT: The list of finite primes at which ``self`` ramifies; given as - integers, sorted small to large, if ``self`` is defined over `\\QQ`, - and as fractional ideals in the ring of integers of the base number - field otherwise. + integers, sorted small to large, if ``self`` is defined over + `\\QQ`, and as fractional ideals in the ring of integers of the + base number field otherwise. EXAMPLES:: @@ -1268,7 +1271,7 @@ def ramified_primes(self): @cached_method def discriminant(self): r""" - Return the discriminant of the given quaternion algebra, i.e. the + Return the discriminant of this quaternion algebra, i.e. the product of the finite places it ramifies at. OUTPUT: @@ -1315,10 +1318,11 @@ def discriminant(self): def is_isomorphic(self, A) -> bool: """ - Check whether the given quaternion algebra is isomorphic to ``A``. + Check whether this quaternion algebra is isomorphic to ``A``. - Currently only implemented over a number field; motivated by Main - Theorem 14.6.1 in [Voi2021]_, noting that QQ has a unique real place. + Currently only implemented over a number field; motivated by + Main Theorem 14.6.1 in [Voi2021]_, noting that `\\QQ` has a + unique infinite place. INPUT: From 12f44a34de1799e11248b2eb7ffe6b61c7393899 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 28 Mar 2024 05:16:45 +0100 Subject: [PATCH 098/507] Implemented reviewer feedback - Rewrote some docstring descriptions - Broke apart examples into multiple blocks, with added flavor text - Modified docstring examples to emphasize certain features more - Refactored some larger descriptions to use bullet point lists Amend 1: Fixed missing space before NOTE Amend 2: Fixed indent of second line in bullet point list in `.discriminant()` --- .../algebras/quatalg/quaternion_algebra.py | 167 ++++++++++++------ 1 file changed, 116 insertions(+), 51 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index fd287b0f490..f2debb672cc 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -406,34 +406,46 @@ def is_division_algebra(self) -> bool: Check whether this quaternion algebra is a division algebra, i.e. whether every nonzero element in it is invertible. + Currently only implemented for quaternion algebras + defined over a number field. + EXAMPLES:: sage: QuaternionAlgebra(QQ,-5,-2).is_division_algebra() True - sage: QuaternionAlgebra(1).is_division_algebra() + sage: QuaternionAlgebra(2,9).is_division_algebra() False + By checking ramification, the methods correctly recognizes division + quaternion algebras over a number field even if they have trivial + discriminant:: + sage: K = QuadraticField(3) - sage: L = QuadraticField(-15) - sage: QuaternionAlgebra(K, -1, -1).is_division_algebra() - True - sage: QuaternionAlgebra(L, -1, -1).is_division_algebra() + sage: A = QuaternionAlgebra(K, -1, -1) + sage: A.discriminant() + Fractional ideal (1) + sage: A.is_division_algebra() True + The method is not implemented over arbitrary base rings yet:: + sage: QuaternionAlgebra(RR(2.),1).is_division_algebra() Traceback (most recent call last): ... - NotImplementedError: base field must be rational numbers or a number field + NotImplementedError: base ring must be rational numbers or a number field """ try: return self.ramified_places(inf=True) != ([], []) except ValueError: - raise NotImplementedError("base field must be rational numbers or a number field") + raise NotImplementedError("base ring must be rational numbers or a number field") def is_matrix_ring(self) -> bool: """ Check whether this quaternion algebra is isomorphic to the - 2x2 matrix ring over the base field. + 2x2 matrix ring over the base ring. + + Currently only implemented for quaternion algebras + defined over a number field. EXAMPLES:: @@ -442,22 +454,28 @@ def is_matrix_ring(self) -> bool: sage: QuaternionAlgebra(2,9).is_matrix_ring() True + By checking ramification, the method is able to recognize that + quaternion algebras (defined over a number field) with trivial + discriminant need not be matrix rings:: + sage: K = QuadraticField(3) - sage: L = QuadraticField(-15) - sage: QuaternionAlgebra(K, -1, -1).is_matrix_ring() - False - sage: QuaternionAlgebra(L, -1, -1).is_matrix_ring() + sage: A = QuaternionAlgebra(K, -1, -1) + sage: A.discriminant() + Fractional ideal (1) + sage: A.is_matrix_ring() False + The method is not implemented over arbitrary base rings yet:: + sage: QuaternionAlgebra(RR(2.),1).is_matrix_ring() Traceback (most recent call last): ... - NotImplementedError: base field must be rational numbers or a number field + NotImplementedError: base ring must be rational numbers or a number field """ try: return self.ramified_places(inf=True) == ([], []) except ValueError: - raise NotImplementedError("base field must be rational numbers or a number field") + raise NotImplementedError("base ring must be rational numbers or a number field") def is_exact(self) -> bool: """ @@ -1057,10 +1075,10 @@ def inner_product_matrix(self): def is_definite(self): r""" - Check whether this quaternion algebra is definite, i.e. whether - it ramifies at the unique real place of its base field `\QQ`. + Check whether this quaternion algebra is definite. - A quaternion algebra over `\QQ` is definite if and only if both of + A quaternion algebra over `\QQ` is definite if it ramifies at the + unique real place of `\QQ`, which happens if and only if both of its invariants are negative (see Exercise 2.4(c) in [Voi2021]_). EXAMPLES:: @@ -1070,6 +1088,8 @@ def is_definite(self): sage: QuaternionAlgebra(1).is_definite() False + The method does not make sense over an arbitrary base ring:: + sage: QuaternionAlgebra(RR(2.),1).is_definite() Traceback (most recent call last): ... @@ -1096,15 +1116,15 @@ def is_totally_definite(self): sage: QuaternionAlgebra(K, -1, -1).is_totally_definite() True - sage: L = QuadraticField(-15) - sage: QuaternionAlgebra(L, -1, -1).is_totally_definite() - True + We can also use number field elements as invariants:: sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) sage: QuaternionAlgebra(F, 2*a, F(-1)).is_totally_definite() False + The method does not make sense over an arbitrary base ring:: + sage: QuaternionAlgebra(RR(2.),1).is_totally_definite() Traceback (most recent call last): ... @@ -1117,8 +1137,8 @@ def is_totally_definite(self): if F not in NumberFields(): raise ValueError("base field must be rational numbers or a number field") - E = F.real_embeddings() - return [e for e in E if F.hilbert_symbol(self._a, self._b, e) == -1] == E + return all(F.hilbert_symbol(self._a, self._b, e) == -1 + for e in F.real_embeddings()) @cached_method def ramified_places(self, inf=True): @@ -1126,10 +1146,12 @@ def ramified_places(self, inf=True): Return the places of the base number field at which this quaternion algebra ramifies. - Note: The initial choice of primes (for the base field `\QQ`) - respectively of prime ideals (in the number field case) to check - ramification for is motivated by 12.4.12(a) in [Voi2021]_. The - restriction to real embeddings is due to 14.5.8 in [Voi2021]_. + .. NOTE:: + + The initial choice of primes (for the base field `\QQ`) + respectively of prime ideals (in the number field case) to check + ramification for is motivated by 12.4.12(a) in [Voi2021]_. The + restriction to real embeddings is due to 14.5.8 in [Voi2021]_. INPUT: @@ -1138,18 +1160,26 @@ def ramified_places(self, inf=True): OUTPUT: The non-Archimedean (AKA finite) places at which the quaternion - algebra ramifies (given as elements of `\ZZ`, sorted small to - large, if the algebra is defined over the rational field `\QQ`, - respectively as fractional ideals of the number field's ring of - integers, otherwise) and, if ``inf`` is set to ``True``, also the - Archimedean (AKA infinite) places at which the quaternion algebra - ramifies (given by real embeddings of the base field). + algebra ramifies, given as + + - elements of `\ZZ` (sorted small to large) if the algebra is + defined over `\QQ`, + + - fractional ideals in the ring of integers of the base number field, + otherwise. + + Additionally, if ``inf`` is set to ``True``, then the Archimedean + (AKA infinite) places at which the quaternion algebra ramifies are + also returned, given by real embeddings of the base field. EXAMPLES:: sage: QuaternionAlgebra(210,-22).ramified_places() ([2, 3, 5, 7], []) + For a definite quaternion algebra we get ramification at the + unique infinite place of `\QQ`:: + sage: QuaternionAlgebra(-1, -1).ramified_places() ([2], [Ring morphism: @@ -1157,6 +1187,15 @@ def ramified_places(self, inf=True): To: Real Field with 53 bits of precision Defn: 1 |--> 1.00000000000000]) + Extending the base field can resolve all ramification:: + + sage: F = QuadraticField(-1) + sage: QuaternionAlgebra(F, -1, -1).ramified_places() + ([], []) + + Extending the base field can resolve all ramification at finite places + while still leaving some ramification at infinite places:: + sage: K = QuadraticField(3) sage: QuaternionAlgebra(K, -1, -1).ramified_places() ([], @@ -1169,10 +1208,15 @@ def ramified_places(self, inf=True): To: Real Field with 53 bits of precision Defn: a |--> 1.73205080756888]) + Extending the base field can also get rid of ramification at infinite + places while still leaving some ramification at finite places:: + sage: L = QuadraticField(-15) sage: QuaternionAlgebra(L, -1, -1).ramified_places() ([Fractional ideal (2, 1/2*a + 1/2), Fractional ideal (2, 1/2*a - 1/2)], []) + We can also use number field elements as invariants:: + sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) sage: QuaternionAlgebra(F, 2*a, F(-1)).ramified_places() @@ -1182,8 +1226,8 @@ def ramified_places(self, inf=True): To: Real Field with 53 bits of precision Defn: a |--> -0.618033988749895]) - sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).ramified_places() # needs sage.symbolic - ([], []) + The method does not make sense over an arbitrary base ring:: + sage: QuaternionAlgebra(RR(2.),1).ramified_places() Traceback (most recent call last): ... @@ -1231,22 +1275,28 @@ def ramified_places(self, inf=True): @cached_method def ramified_primes(self): - """ + r""" Return the (finite) primes of the base number field at which this quaternion algebra ramifies. OUTPUT: - The list of finite primes at which ``self`` ramifies; given as - integers, sorted small to large, if ``self`` is defined over - `\\QQ`, and as fractional ideals in the ring of integers of the - base number field otherwise. + The list of finite primes at which ``self`` ramifies, given as + + - elements of `\ZZ` (sorted small to large) if the algebra is + defined over `\QQ`, + + - fractional ideals in the ring of integers of the base number field, + otherwise. EXAMPLES:: sage: QuaternionAlgebra(-58, -69).ramified_primes() [3, 23, 29] + Under field extensions, the number of ramified primes can increase + or decrease:: + sage: K = QuadraticField(3) sage: L = QuadraticField(-15) sage: QuaternionAlgebra(-1, -1).ramified_primes() @@ -1256,11 +1306,15 @@ def ramified_primes(self): sage: QuaternionAlgebra(L, -1, -1).ramified_primes() [Fractional ideal (2, 1/2*a + 1/2), Fractional ideal (2, 1/2*a - 1/2)] + We can also use number field elements as invariants:: + sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) sage: QuaternionAlgebra(F, 2*a, F(-1)).ramified_primes() [Fractional ideal (2)] + The method does not make sense over an arbitrary base ring:: + sage: QuaternionAlgebra(RR(2.),1).ramified_primes() Traceback (most recent call last): ... @@ -1271,15 +1325,19 @@ def ramified_primes(self): @cached_method def discriminant(self): r""" - Return the discriminant of this quaternion algebra, i.e. the - product of the finite places it ramifies at. + Return the discriminant of this quaternion algebra. + + The discriminant of a quaternion algebra over a number field is the + product of the finite places at which the algebra ramifies. OUTPUT: - The discriminant of this quaternion algebra (which has to be defined - over a number field), as an element of `\ZZ` if the quaternion algebra - is defined over `\QQ`, and as a fractional ideal in the ring of - integers of the base number field otherwise. + The discriminant of this quaternion algebra, as + + - an element of `\ZZ` if the algebra is defined over `\QQ`, + + - a fractional ideal in the ring of integers of the base number + field, otherwise. EXAMPLES:: @@ -1290,6 +1348,8 @@ def discriminant(self): sage: QuaternionAlgebra(-1, -1).discriminant() 2 + Some examples over number fields:: + sage: K = QuadraticField(3) sage: L = QuadraticField(-15) sage: QuaternionAlgebra(K, -1, -1).discriminant() @@ -1297,13 +1357,14 @@ def discriminant(self): sage: QuaternionAlgebra(L, -1, -1).discriminant() Fractional ideal (2) + We can also use number field elements as invariants:: + sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) sage: QuaternionAlgebra(F, 2*a, F(-1)).discriminant() Fractional ideal (2) - sage: QuaternionAlgebra(QQ[sqrt(2)], 3, 19).discriminant() # needs sage.symbolic - Fractional ideal (1) + The method does not make sense over an arbitrary base ring:: sage: QuaternionAlgebra(RR(2.),1).discriminant() Traceback (most recent call last): @@ -1320,9 +1381,9 @@ def is_isomorphic(self, A) -> bool: """ Check whether this quaternion algebra is isomorphic to ``A``. - Currently only implemented over a number field; motivated by - Main Theorem 14.6.1 in [Voi2021]_, noting that `\\QQ` has a - unique infinite place. + Currently only implemented for quaternion algebras defined over + a number field; based on Main Theorem 14.6.1 in [Voi2021]_, + noting that `\\QQ` has a unique infinite place. INPUT: @@ -1337,6 +1398,10 @@ def is_isomorphic(self, A) -> bool: sage: B.is_isomorphic(A) True + Checking ramification at both finite and infinite places, the method + correctly distinguishes isomorphism classes of quaternion algebras + that the discriminant can not distinguish:: + sage: K = QuadraticField(3) sage: A = QuaternionAlgebra(K, -1, -1) sage: B = QuaternionAlgebra(K, 1, -1) @@ -1349,7 +1414,7 @@ def is_isomorphic(self, A) -> bool: raise TypeError("A must be a quaternion algebra of the form (a,b)_K") F = self.base_ring() - if F != A.base_ring(): + if F is not A.base_ring(): raise ValueError("both quaternion algebras must be defined over the same ring") if is_RationalField(F): From aec353447f0fefd3efc15e9bf33f7350f292b8bd Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 28 Mar 2024 14:45:15 +0100 Subject: [PATCH 099/507] Small docstring modifications --- .../algebras/quatalg/quaternion_algebra.py | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index f2debb672cc..17d5d5e7475 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -416,7 +416,7 @@ def is_division_algebra(self) -> bool: sage: QuaternionAlgebra(2,9).is_division_algebra() False - By checking ramification, the methods correctly recognizes division + By checking ramification, the method correctly recognizes division quaternion algebras over a number field even if they have trivial discriminant:: @@ -1150,7 +1150,7 @@ def ramified_places(self, inf=True): The initial choice of primes (for the base field `\QQ`) respectively of prime ideals (in the number field case) to check - ramification for is motivated by 12.4.12(a) in [Voi2021]_. The + ramification for is based on 12.4.12(a) in [Voi2021]_. The restriction to real embeddings is due to 14.5.8 in [Voi2021]_. INPUT: @@ -1159,14 +1159,12 @@ def ramified_places(self, inf=True): OUTPUT: - The non-Archimedean (AKA finite) places at which the quaternion + The non-Archimedean (AKA finite) places at which this quaternion algebra ramifies, given as - - elements of `\ZZ` (sorted small to large) if the algebra is - defined over `\QQ`, + - elements of `\ZZ` (sorted small to large) if the base field is `\QQ`, - - fractional ideals in the ring of integers of the base number field, - otherwise. + - integral fractional ideals of the base number field, otherwise. Additionally, if ``inf`` is set to ``True``, then the Archimedean (AKA infinite) places at which the quaternion algebra ramifies are @@ -1193,8 +1191,8 @@ def ramified_places(self, inf=True): sage: QuaternionAlgebra(F, -1, -1).ramified_places() ([], []) - Extending the base field can resolve all ramification at finite places - while still leaving some ramification at infinite places:: + Extending the base field can also resolve all ramification at finite + places while still leaving some ramification at infinite places:: sage: K = QuadraticField(3) sage: QuaternionAlgebra(K, -1, -1).ramified_places() @@ -1215,7 +1213,7 @@ def ramified_places(self, inf=True): sage: QuaternionAlgebra(L, -1, -1).ramified_places() ([Fractional ideal (2, 1/2*a + 1/2), Fractional ideal (2, 1/2*a - 1/2)], []) - We can also use number field elements as invariants:: + We can use number field elements as invariants as well:: sage: x = polygen(ZZ, 'x') sage: F. = NumberField(x^2 - x - 1) @@ -1281,13 +1279,12 @@ def ramified_primes(self): OUTPUT: - The list of finite primes at which ``self`` ramifies, given as + The list of finite primes at which this quaternion algebra ramifies, + given as - - elements of `\ZZ` (sorted small to large) if the algebra is - defined over `\QQ`, + - elements of `\ZZ` (sorted small to large) if the base field is `\QQ`, - - fractional ideals in the ring of integers of the base number field, - otherwise. + - integral fractional ideals of the base number field, otherwise. EXAMPLES:: @@ -1332,12 +1329,11 @@ def discriminant(self): OUTPUT: - The discriminant of this quaternion algebra, as + The discriminant of this quaternion algebra, given as - an element of `\ZZ` if the algebra is defined over `\QQ`, - - a fractional ideal in the ring of integers of the base number - field, otherwise. + - an integral fractional ideal of the base number field, otherwise. EXAMPLES:: From c0b8a6572ade213efa3646d53dcbcc4d65288cd5 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 29 Mar 2024 00:59:55 +0100 Subject: [PATCH 100/507] Corrected `.is_totally_definite()`, moved .. NOTE - Added missing check that the base field of a totally definite algebra needs to be totally real - Moved note in `.ramified_places` below input and moved the interal info on initial choice of finite primes to code comments --- .../algebras/quatalg/quaternion_algebra.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 17d5d5e7475..b89110144d7 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1104,8 +1104,10 @@ def is_totally_definite(self): """ Check whether this quaternion algebra is totally definite. - A quaternion algebra defined over a number field is totally definite - if it ramifies at all real places of its base field. + A quaternion algebra defined over a number field is + totally definite if it ramifies at all Archimedean + places of its base field. In particular, the base number + field has to be totally real (see 14.5.8 in [Voi2021]_). EXAMPLES:: @@ -1137,8 +1139,12 @@ def is_totally_definite(self): if F not in NumberFields(): raise ValueError("base field must be rational numbers or a number field") - return all(F.hilbert_symbol(self._a, self._b, e) == -1 - for e in F.real_embeddings()) + # Since we need the list of real embeddings of the number field (instead + # of just the number of them), we avoid a call of the `is_totally_real()`- + # method by directly comparing the embedding list's length to the degree + E = F.real_embeddings() + return len(E) == F.degree() and all(F.hilbert_symbol(self._a, self._b, e) == -1 + for e in E) @cached_method def ramified_places(self, inf=True): @@ -1146,13 +1152,6 @@ def ramified_places(self, inf=True): Return the places of the base number field at which this quaternion algebra ramifies. - .. NOTE:: - - The initial choice of primes (for the base field `\QQ`) - respectively of prime ideals (in the number field case) to check - ramification for is based on 12.4.12(a) in [Voi2021]_. The - restriction to real embeddings is due to 14.5.8 in [Voi2021]_. - INPUT: - ``inf`` -- (default: ``True``) @@ -1170,6 +1169,11 @@ def ramified_places(self, inf=True): (AKA infinite) places at which the quaternion algebra ramifies are also returned, given by real embeddings of the base field. + .. NOTE:: + + Any Archimedean place at which a quaternion algebra ramifies + has to be real (see 14.5.8 in [Voi2021]_). + EXAMPLES:: sage: QuaternionAlgebra(210,-22).ramified_places() @@ -1238,6 +1242,10 @@ def ramified_places(self, inf=True): a = self._a b = self._b + # The initial choice of primes (for the base field QQ) respectively + # of prime ideals (in the number field case) to check ramification + # for is based on 12.4.12(a) in [Voi2021]_. + # For efficiency (and to not convert QQ into a number field manually), # we handle the case F = QQ first if is_RationalField(F): From da2a8050e4477f09c16b1de86065c2cce0b7af3d Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 29 Mar 2024 01:43:00 +0100 Subject: [PATCH 101/507] Added missing type descriptor for `inf` argument --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index b89110144d7..d6d245c2b89 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1154,7 +1154,7 @@ def ramified_places(self, inf=True): INPUT: - - ``inf`` -- (default: ``True``) + - ``inf`` -- bool (default: ``True``) OUTPUT: From 96aea2a02eb780c30eb254a0c5a35b04aae720e8 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 2 Apr 2024 00:22:00 +0200 Subject: [PATCH 102/507] remove UndeterminedCoefficientsRing --- src/sage/data_structures/stream.py | 255 +++++++---------------------- src/sage/rings/lazy_series_ring.py | 6 +- 2 files changed, 61 insertions(+), 200 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 3836985aaba..ede362c5d4c 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1247,211 +1247,70 @@ def iterate_coefficients(self): denom *= n -from sage.structure.parent import Parent -from sage.structure.element import Element, parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.fields import Fields -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing -class UndeterminedCoefficientsRingElement(Element): - def __init__(self, parent, v): - Element.__init__(self, parent) - self._p = v +class VariablePool(UniqueRepresentation): + """ + A class to keep track of used and unused variables in an + :cls:`InfinitePolynomialRing`. - def __bool__(self): - return bool(self._p) + INPUT: - def _repr_(self): - return repr(self._p) + - ``ring``, an :cls:`InfinitePolynomialRing`. + """ + def __init__(self, ring): + self._gen = ring.gen(0) # alternatively, make :cls:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. + self._pool = dict() # dict from variables actually used to indices of gens - def _add_(self, other): + def new_variable(self): """ + Return an unused variable. EXAMPLES:: - sage: from sage.data_structures.stream import UndeterminedCoefficientsRing - sage: R = UndeterminedCoefficientsRing(QQ) - sage: R(None) + 1 - FESDUMMY_... + 1 - """ - P = self.parent() - return P.element_class(P, self._p + other._p) + sage: from sage.data_structures.stream import VariablePool + sage: R. = InfinitePolynomialRing(QQ) + sage: P = VariablePool(R) + sage: P.new_variable() + a_0 - def _sub_(self, other): - """ - EXAMPLES:: + TESTS: - sage: from sage.data_structures.stream import UndeterminedCoefficientsRing - sage: R = UndeterminedCoefficientsRing(QQ) - sage: 1 - R(None) - -FESDUMMY_... + 1 - """ - P = self.parent() - return P.element_class(P, self._p - other._p) + Check, that we get a new pool for each + :cls:`InfinitePolynomialRing`:: - def _neg_(self): - """ - Return the negative of ``self``. - """ - P = self.parent() - return P.element_class(P, -self._p) + sage: R0. = InfinitePolynomialRing(QQ) + sage: P0 = VariablePool(R0) + sage: P0.new_variable() + b_0 - def _mul_(self, other): """ - EXAMPLES:: + for i in range(len(self._pool)+1): + if i not in self._pool.values(): + break + v = self._gen[i] + self._pool[v] = i + return v - sage: from sage.data_structures.stream import UndeterminedCoefficientsRing - sage: R = UndeterminedCoefficientsRing(QQ) - sage: R(None) * R(None) - FESDUMMY_...*FESDUMMY_... + def del_variable(self, v): """ - P = self.parent() - return P.element_class(P, self._p * other._p) - - def _div_(self, other): - P = self.parent() - return P.element_class(P, self._p / other._p) - - def numerator(self): - return self._p.numerator() + Remove ``v`` from the pool. - def variables(self): - """ EXAMPLES:: - sage: from sage.data_structures.stream import UndeterminedCoefficientsRing - sage: R = UndeterminedCoefficientsRing(QQ) - sage: R(None) / (R(None) + R(None)) - FESDUMMY_.../(FESDUMMY_... + FESDUMMY_...) - """ - return self._p.numerator().variables() + self._p.denominator().variables() - - def rational_function(self): - return self._p - - def is_constant(self): - return (self._p.numerator().is_constant() - and self._p.denominator().is_constant()) + sage: from sage.data_structures.stream import VariablePool + sage: R. = InfinitePolynomialRing(QQ) + sage: P = VariablePool(R) + sage: v = P.new_variable(); v + a_0 - def subs(self, in_dict=None, *args, **kwds): + sage: P.del_variable(v) + sage: v = P.new_variable(); v + a_0 """ - EXAMPLES:: - - sage: from sage.data_structures.stream import UndeterminedCoefficientsRing - sage: R = UndeterminedCoefficientsRing(QQ) - sage: p = R(None) + 1 - sage: v = p.variables()[0] - sage: q = R(None) + 1 - sage: (p/q).subs({v: 3}) - 4/(FESDUMMY_... + 1) - """ -# if isinstance(in_dict, dict): -# R = self._p.parent() -# in_dict = {ZZ(m) if m in ZZ else R(m): v for m, v in in_dict.items()} -# -# P = self.parent() -# return P.element_class(P, self._p.subs(in_dict, *args, **kwds)) - P = self.parent() - p_num = P._P(self._p.numerator()) - V_num = p_num.variables() - d_num = {P._P(v): c for v, c in in_dict.items() - if v in V_num} - num = p_num.subs(d_num) - p_den = P._P(self._p.denominator()) - V_den = p_den.variables() - d_den = {P._P(v): c for v, c in in_dict.items() - if v in V_den} - den = p_den.subs(d_den) - return P.element_class(P, P._PF(num, den)) - - -class UndeterminedCoefficientsFunctor(ConstructionFunctor): - rank = 0 - - def __init__(self): - Functor.__init__(self, Rings(), Rings()) - - def _apply_functor(self, R): - return UndeterminedCoefficientsRing(R) - - __hash__ = ConstructionFunctor.__hash__ - - def _repr_(self): - return "UndeterminedCoefficients" - - -class UndeterminedCoefficientsRing(UniqueRepresentation, Parent): - """ - Rational functions in unknowns over a base ring. - """ - # must not inherit from UniqueRepresentation, because we want a - # new set of variables for each system of equations - - _PREFIX = "FESDUMMY_" - @staticmethod - def __classcall_private__(cls, base_ring, *args, **kwds): - return super().__classcall__(cls, base_ring, *args, **kwds) - - def __init__(self, base_ring): - self._pool = dict() # dict from variables actually used to indices of gens - # we must start with at least two variables, to make PolynomialSequence work - self._P = PolynomialRing(base_ring, names=[self._PREFIX+str(i) for i in range(2)]) - self._PF = self._P.fraction_field() - Parent.__init__(self, base=base_ring, category=Fields()) - - def construction(self): - return (UndeterminedCoefficientsFunctor(), self.base_ring()) - - def polynomial_ring(self): - """ - .. WARNING:: - - This ring changes when new variables are requested. - """ - return self._P - - def _element_constructor_(self, x): - if x is None: - n = self._P.ngens() - for i in range(n): - if i not in self._pool.values(): - break - else: - names = self._P.variable_names() + (self._PREFIX+str(n),) # tuple(self._PREFIX+str(i) for i in range(n, 2*n)) - self._P = PolynomialRing(self._P.base_ring(), names) - self._PF = self._P.fraction_field() - i = n - v = self._P.gen(i) - self._pool[v] = i - return self.element_class(self, self._PF(v)) - - if x in self._PF: - return self.element_class(self, self._PF(x)) - - raise ValueError(f"{x} is not in {self}") - - def delete_variable(self, v): del self._pool[v] - def _coerce_map_from_(self, S): - """ - Return ``True`` if a coercion from ``S`` exists. - """ - if self._P.base_ring().has_coerce_map_from(S): - return True - return None - - def _coerce_map_from_base_ring(self): - """ - Return a coercion map from the base ring of ``self``. - """ - return self._generic_coerce_map(self._P.base_ring()) - - def _repr_(self): - return f"Undetermined coefficient ring over {self._P.base_ring()}" - - Element = UndeterminedCoefficientsRingElement - class Stream_uninitialized(Stream): r""" @@ -1567,9 +1426,11 @@ def define_implicitly(self, series, initial_values, equations, self._coefficient_ring = coefficient_ring self._base_ring = base_ring - self._P = UndeterminedCoefficientsRing(self._base_ring) + self._P = InfinitePolynomialRing(self._base_ring, names=["FESDUMMY"]) + self._PF = self._P.fraction_field() if self._coefficient_ring != self._base_ring: - self._U = self._coefficient_ring.change_ring(self._P) + self._U = self._coefficient_ring.change_ring(self._PF) + self._pool = VariablePool(self._P) self._uncomputed = True self._eqs = equations self._series = series @@ -1676,8 +1537,8 @@ def __getitem__(self, n): if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] - x = sum(self._P(None) * m - for m in self._terms_of_degree(n, self._P)) + x = sum(self._pool.new_variable() * m + for m in self._terms_of_degree(n, self._PF)) self._cache.append(x) return x @@ -1688,7 +1549,7 @@ def _subs_in_caches(self, var, val): INPUT: - - ``var``, a variable + - ``var``, a variable in ``self._P`` - ``val``, the value that should replace the variable """ for j, s in enumerate(self._input_streams): @@ -1704,18 +1565,18 @@ def _subs_in_caches(self, var, val): good = m for i0, i in enumerate(indices): c = s._cache[i] - if self._base_ring == self._coefficient_ring: - if c.parent() == self._P: - c = c.subs({var: val}) - if c.is_constant(): - c = self._base_ring(c.rational_function()) + if self._coefficient_ring == self._base_ring: + if c.parent() == self._PF: + c = self._PF(c.subs({var: val})) + if c.numerator().is_constant() and c.denominator().is_constant(): + c = self._base_ring(c) else: good = m - i0 - 1 else: if c.parent() == self._U: c = c.map_coefficients(lambda e: e.subs({var: val})) try: - c = c.map_coefficients(lambda e: self._base_ring(e.rational_function()), + c = c.map_coefficients(lambda e: self._base_ring(e), self._base_ring) except TypeError: good = m - i0 - 1 @@ -1743,7 +1604,7 @@ def _subs_in_caches(self, var, val): ao += 1 s._approximate_order = ao - self._P.delete_variable(var) + self._pool.del_variable(var) def _compute(self): """ @@ -1771,7 +1632,7 @@ def _compute(self): lcoeff = coeff.coefficients() for c in lcoeff: - c = self._P(c).numerator() + c = self._PF(c).numerator() V = c.variables() if not V: if len(self._eqs) == 1: @@ -1799,7 +1660,7 @@ def _compute(self): raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - eqs = PolynomialSequence(self._P.polynomial_ring(), coeffs) + eqs = PolynomialSequence([c.polynomial() for c in coeffs]) m1, v1 = eqs.coefficients_monomials() # there should be at most one entry in v1 of degree 0 for j, c in enumerate(v1): @@ -1820,7 +1681,7 @@ def _compute(self): for i, (var, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): val = self._base_ring(y) - self._subs_in_caches(var, val) + self._subs_in_caches(self._P(var), val) bad = False if bad: if len(self._eqs) == 1: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 516d3a63324..fa5b2540b2c 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -780,12 +780,12 @@ def define_implicitly(self, series, equations): sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + Laurent series examples:: @@ -872,7 +872,7 @@ def define_implicitly(self, series, equations): sage: B O(z^16) sage: C - O(z^22) + O(z^23) sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() From 60bc31c14d7ca2c25b3c8f86e47285db6c2f311c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 2 Apr 2024 14:35:58 +0200 Subject: [PATCH 103/507] use bad workaround for InfinitePolynomialRing bug --- src/sage/data_structures/stream.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index ede362c5d4c..26bc3b4b155 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1660,7 +1660,7 @@ def _compute(self): raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - eqs = PolynomialSequence([c.polynomial() for c in coeffs]) + eqs = PolynomialSequence(c.polynomial() for c in coeffs) m1, v1 = eqs.coefficients_monomials() # there should be at most one entry in v1 of degree 0 for j, c in enumerate(v1): @@ -1681,7 +1681,11 @@ def _compute(self): for i, (var, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): val = self._base_ring(y) - self._subs_in_caches(self._P(var), val) + # workaround for `var = self._P(var)` + var = next(self._P.gen(0)[i] + for i, v in enumerate(reversed(self._P._P.gens())) + if v == var) + self._subs_in_caches(var, val) bad = False if bad: if len(self._eqs) == 1: From df873611920fda448ad57d33c74edeed38f3fe1c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 2 Apr 2024 16:32:15 +0200 Subject: [PATCH 104/507] conversion is slower than direct computation --- src/sage/data_structures/stream.py | 6 ++++-- src/sage/rings/lazy_series_ring.py | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 26bc3b4b155..191b37ac710 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1568,8 +1568,10 @@ def _subs_in_caches(self, var, val): if self._coefficient_ring == self._base_ring: if c.parent() == self._PF: c = self._PF(c.subs({var: val})) - if c.numerator().is_constant() and c.denominator().is_constant(): - c = self._base_ring(c) + num = c.numerator() + den = c.denominator() + if num.is_constant() and den.is_constant(): + c = num.constant_coefficient() / den.constant_coefficient() else: good = m - i0 - 1 else: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index fa5b2540b2c..e74ede206fa 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -836,7 +836,6 @@ def define_implicitly(self, series, equations): sage: B -1 + O(z^7) - sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() sage: B = L.undefined() From b3fb7718e7120fc3f5972c6cb83418bf6d1cd6a4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 3 Apr 2024 08:32:36 +0200 Subject: [PATCH 105/507] fix doctests and error handling for undefined streams --- src/sage/combinat/species/species.py | 4 ++-- src/sage/data_structures/stream.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index 705dbd22494..ff0e470e829 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -376,7 +376,7 @@ def structures(self, labels, structure_class=None): sage: F.structures([1,2,3]).list() Traceback (most recent call last): ... - NotImplementedError + ValueError: Stream is not yet defined """ return StructuresWrapper(self, labels, structure_class) @@ -388,7 +388,7 @@ def isotypes(self, labels, structure_class=None): sage: F.isotypes([1,2,3]).list() Traceback (most recent call last): ... - NotImplementedError + ValueError: Stream is not yet defined """ return IsotypesWrapper(self, labels, structure_class=structure_class) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 191b37ac710..1384923cc09 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1303,11 +1303,11 @@ def del_variable(self, v): sage: R. = InfinitePolynomialRing(QQ) sage: P = VariablePool(R) sage: v = P.new_variable(); v - a_0 + a_1 sage: P.del_variable(v) sage: v = P.new_variable(); v - a_0 + a_1 """ del self._pool[v] @@ -1521,6 +1521,8 @@ def __getitem__(self, n): return ZZ.zero() # define_implicitly + if self._eqs is None: + raise ValueError("Stream is not yet defined") if self._good_cache[0] > n - self._approximate_order: return self._cache[n - self._approximate_order] From f70ebf56bd88cc77c173c084e83cb5081fa79c90 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 3 Apr 2024 11:11:43 +0200 Subject: [PATCH 106/507] fix bad markup (:cls: should be :class:) --- src/sage/data_structures/stream.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 1384923cc09..c3dd585ec3a 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1253,14 +1253,14 @@ def iterate_coefficients(self): class VariablePool(UniqueRepresentation): """ A class to keep track of used and unused variables in an - :cls:`InfinitePolynomialRing`. + :class:`InfinitePolynomialRing`. INPUT: - - ``ring``, an :cls:`InfinitePolynomialRing`. + - ``ring``, an :class:`InfinitePolynomialRing`. """ def __init__(self, ring): - self._gen = ring.gen(0) # alternatively, make :cls:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. + self._gen = ring.gen(0) # alternatively, make :class:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. self._pool = dict() # dict from variables actually used to indices of gens def new_variable(self): @@ -1278,7 +1278,7 @@ def new_variable(self): TESTS: Check, that we get a new pool for each - :cls:`InfinitePolynomialRing`:: + :class:`InfinitePolynomialRing`:: sage: R0. = InfinitePolynomialRing(QQ) sage: P0 = VariablePool(R0) From 5524bff2afba37702e53fce22004907450730eae Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 3 Apr 2024 13:24:32 +0200 Subject: [PATCH 107/507] remove workaround and fix InfinitePolynomialRing instead --- src/sage/data_structures/stream.py | 6 +----- .../rings/polynomial/infinite_polynomial_element.py | 11 ++++++++++- src/sage/rings/polynomial/infinite_polynomial_ring.py | 4 ++-- .../rings/polynomial/multi_polynomial_sequence.py | 7 ++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index c3dd585ec3a..f16c43d6f58 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1664,7 +1664,7 @@ def _compute(self): raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - eqs = PolynomialSequence(c.polynomial() for c in coeffs) + eqs = PolynomialSequence(coeffs) m1, v1 = eqs.coefficients_monomials() # there should be at most one entry in v1 of degree 0 for j, c in enumerate(v1): @@ -1685,10 +1685,6 @@ def _compute(self): for i, (var, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): val = self._base_ring(y) - # workaround for `var = self._P(var)` - var = next(self._P.gen(0)[i] - for i, v in enumerate(reversed(self._P._P.gens())) - if v == var) self._subs_in_caches(var, val) bad = False if bad: diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index b135404a020..1c6984aef87 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -578,9 +578,18 @@ def variables(self): """ if hasattr(self._p, 'variables'): - return tuple(self._p.variables()) + P = self.parent() + return tuple(InfinitePolynomial(P, v) for v in self._p.variables()) return () + @cached_method + def monomials(self): + P = self.parent() + return [InfinitePolynomial(P, m) for m in self._p.monomials()] + + def monomial_coefficient(self, mon): + return self._p.monomial_coefficient(mon._p) + @cached_method def max_index(self): r""" diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index dbb71289f89..a23b038731f 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -279,8 +279,8 @@ class InfinitePolynomialRingFactory(UniqueFactory): """ A factory for creating infinite polynomial ring elements. It - handles making sure that they are unique as well as handling - pickling. For more details, see + makes sure that they are unique as well as handling pickling. + For more details, see :class:`~sage.structure.factory.UniqueFactory` and :mod:`~sage.rings.polynomial.infinite_polynomial_ring`. diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index e9476061b57..97fd0574b4a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -170,6 +170,7 @@ from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing_sparse from sage.rings.quotient_ring import is_QuotientRing from sage.structure.sequence import Sequence_generic @@ -297,7 +298,7 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): except ImportError: BooleanMonomialMonoid = () - is_ring = lambda r: is_MPolynomialRing(r) or isinstance(r, BooleanMonomialMonoid) or (is_QuotientRing(r) and is_MPolynomialRing(r.cover_ring())) + is_ring = lambda r: is_MPolynomialRing(r) or isinstance(r, BooleanMonomialMonoid) or (is_QuotientRing(r) and is_MPolynomialRing(r.cover_ring())) or isinstance(r, InfinitePolynomialRing_sparse) if is_ring(arg1): ring, gens = arg1, arg2 @@ -380,7 +381,7 @@ def __init__(self, parts, ring, immutable=False, cr=False, cr_str=None): INPUT: - - ``part`` - a list of lists with polynomials + - ``parts`` - a list of lists with polynomials - ``ring`` - a multivariate polynomial ring @@ -414,7 +415,7 @@ def __init__(self, parts, ring, immutable=False, cr=False, cr_str=None): 2*a*b + 2*b*c + 2*c*d - b, b^2 + 2*a*c + 2*b*d - c] """ - Sequence_generic.__init__(self, sum(parts,tuple()), ring, check=False, immutable=immutable, + Sequence_generic.__init__(self, sum(parts, tuple()), ring, check=False, immutable=immutable, cr=cr, cr_str=cr_str, use_sage_types=True) self._ring = ring self._parts = parts From 205f3eacfbb5fbb5e5b9efd61fd87308ea596b53 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 3 Apr 2024 13:48:40 +0200 Subject: [PATCH 108/507] add documentation and tests, remove cached_method --- .../polynomial/infinite_polynomial_element.py | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 1c6984aef87..76bb926c176 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -582,12 +582,64 @@ def variables(self): return tuple(InfinitePolynomial(P, v) for v in self._p.variables()) return () - @cached_method def monomials(self): + """ + Return the list of monomials in self. The returned list is + decreasingly ordered by the term ordering of + ``self.parent()``. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: p = x[1]^3 + x[2] - 2*x[1]*x[3] + sage: p.monomials() + [x_3*x_1, x_2, x_1^3] + + sage: X. = InfinitePolynomialRing(QQ, order='deglex') + sage: p = x[1]^3 + x[2] - 2*x[1]*x[3] + sage: p.monomials() + [x_1^3, x_3*x_1, x_2] + """ P = self.parent() return [InfinitePolynomial(P, m) for m in self._p.monomials()] def monomial_coefficient(self, mon): + """ + Return the coefficient in the base ring of the monomial mon in + ``self``, where mon must have the same parent as self. + + This function contrasts with the function ``coefficient`` + which returns the coefficient of a monomial viewing this + polynomial in a polynomial ring over a base ring having fewer + variables. + + INPUT: + + - ``mon`` - a monomial + + OUTPUT: + + coefficient in base ring + + .. SEEALSO:: + + For coefficients in a base ring of fewer variables, + look at ``coefficient``. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: f = 2*x[0]*x[2] + 3*x[1]^2 + sage: c = f.monomial_coefficient(x[1]^2); c + 3 + sage: c.parent() + Rational Field + + sage: c = f.coefficient(x[2]); c + 2*x_0 + sage: c.parent() + Infinite polynomial ring in x over Rational Field + """ return self._p.monomial_coefficient(mon._p) @cached_method From b0b6e62b4dac42314b6aa445fcbf04bcebf19aa7 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 4 Apr 2024 16:04:46 +0200 Subject: [PATCH 109/507] optimize and clarify substitution --- src/sage/data_structures/stream.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index f16c43d6f58..bf9bf3f0278 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1249,6 +1249,7 @@ def iterate_coefficients(self): from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing +from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial class VariablePool(UniqueRepresentation): """ @@ -1554,6 +1555,20 @@ def _subs_in_caches(self, var, val): - ``var``, a variable in ``self._P`` - ``val``, the value that should replace the variable """ + def subs(c, var, val): + P = self._P.polynomial_ring() + num = P(c.numerator()._p).subs({P(var._p): val}) + den = P(c.denominator()._p).subs({P(var._p): val}) + return self._PF(InfinitePolynomial(self._P, num), + InfinitePolynomial(self._P, den)) + + def retract(c): + num = c.numerator() + den = c.denominator() + if num.is_constant() and den.is_constant(): + return num.constant_coefficient() / den.constant_coefficient() + return c + for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] if s._is_sparse: @@ -1569,19 +1584,14 @@ def _subs_in_caches(self, var, val): c = s._cache[i] if self._coefficient_ring == self._base_ring: if c.parent() == self._PF: - c = self._PF(c.subs({var: val})) - num = c.numerator() - den = c.denominator() - if num.is_constant() and den.is_constant(): - c = num.constant_coefficient() / den.constant_coefficient() - else: + c = retract(subs(c, var, val)) + if not c.parent() is self._base_ring: good = m - i0 - 1 else: if c.parent() == self._U: - c = c.map_coefficients(lambda e: e.subs({var: val})) + c = c.map_coefficients(lambda e: subs(e, var, val)) try: - c = c.map_coefficients(lambda e: self._base_ring(e), - self._base_ring) + c = c.map_coefficients(lambda e: retract(e), self._base_ring) except TypeError: good = m - i0 - 1 s._cache[i] = c From 4bef1dc0db9e38b0e3ffc68c7dc6efcb0477cc2d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 4 Apr 2024 17:33:00 +0200 Subject: [PATCH 110/507] simplify VariablePool, add documentation and doctests --- src/sage/data_structures/stream.py | 185 +++++++++++++++++++++++------ src/sage/rings/lazy_series_ring.py | 3 +- 2 files changed, 152 insertions(+), 36 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index bf9bf3f0278..720178a6abf 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -102,11 +102,10 @@ from sage.misc.lazy_import import lazy_import from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis -from sage.categories.rings import Rings +from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing +from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial from sage.misc.cachefunc import cached_method -from sage.categories.functor import Functor -from sage.categories.pushout import ConstructionFunctor -from collections import defaultdict lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1029,6 +1028,23 @@ def input_streams(self): r""" Return the list of streams which are used to compute the coefficients of ``self``, as provided. + + EXAMPLES: + + Only streams that appear in the closure are detected:: + + sage: from sage.data_structures.stream import Stream_function, Stream_exact + sage: f = Stream_exact([1,3,5], constant=7) + sage: g = Stream_function(lambda n: f[n]^2, False, 0) + sage: g.input_streams() + [] + + sage: def fun(): + ....: f = Stream_exact([1,3,5], constant=7) + ....: g = Stream_function(lambda n: f[n]^2, False, 0) + ....: return g.input_streams() + sage: fun() + [] """ closure = self.get_coefficient.__closure__ if closure is None: @@ -1247,10 +1263,6 @@ def iterate_coefficients(self): denom *= n -from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing -from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial - class VariablePool(UniqueRepresentation): """ A class to keep track of used and unused variables in an @@ -1261,8 +1273,18 @@ class VariablePool(UniqueRepresentation): - ``ring``, an :class:`InfinitePolynomialRing`. """ def __init__(self, ring): - self._gen = ring.gen(0) # alternatively, make :class:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. - self._pool = dict() # dict from variables actually used to indices of gens + """ + Inititialize the pool. + + EXAMPLES:: + + sage: from sage.data_structures.stream import VariablePool + sage: R. = InfinitePolynomialRing(QQ) + sage: P = VariablePool(R) + sage: TestSuite(P).run() + """ + self._gen = ring.gen(0) # alternatively, make :class:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. + self._pool = [] # list of variables actually used def new_variable(self): """ @@ -1287,12 +1309,11 @@ def new_variable(self): b_0 """ - for i in range(len(self._pool)+1): - if i not in self._pool.values(): - break - v = self._gen[i] - self._pool[v] = i - return v + for i in range(len(self._pool) + 1): + v = self._gen[i] + if v not in self._pool: + self._pool.append(v) + return v def del_variable(self, v): """ @@ -1310,7 +1331,7 @@ def del_variable(self, v): sage: v = P.new_variable(); v a_1 """ - del self._pool[v] + self._pool.remove(v) class Stream_uninitialized(Stream): @@ -1391,9 +1412,17 @@ def define(self, target): - ``target`` -- a stream + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: C.define(Stream_add(x, Stream_cauchy_mul(C, C, True), True)) + sage: C[6] + 42 """ self._target = target - self._n = self._approximate_order - 1 # the largest index of a coefficient we know + self._n = self._approximate_order - 1 # the largest index of a coefficient we know # we only need this if target does not have a dense cache self._cache = list() self._iter = self.iterate_coefficients() @@ -1412,6 +1441,17 @@ def define_implicitly(self, series, initial_values, equations, - ``coefficient_ring`` -- the ring containing the elements of the stream (after substitution) - ``terms_of_degree`` -- a function returning the list of terms of a given degree + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C[6] + 42 """ assert self._target is None @@ -1440,14 +1480,31 @@ def define_implicitly(self, series, initial_values, equations, @lazy_attribute def _input_streams(self): r""" - Return the list of streams which have a cache and ``self`` - depends on. + Return the list of streams which have a cache and an implicitly + defined ``self`` depends on. ``self`` is the first stream in this list. - All caches must have been created before this is called. - Does this mean that this should only be called after the - first invocation of `_compute`? + .. WARNING:: + + All caches must have been created before this is called. + Currently, the first invocation is via + :meth:`_good_cache` in :meth:`__getitem__`. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C._input_streams + [, + , + , + ] """ known = [self] todo = [self] @@ -1462,14 +1519,32 @@ def _input_streams(self): @lazy_attribute def _good_cache(self): r""" - The number of coefficients in each input stream - in the same - order - that are free of undetermined coefficients. + The number of coefficients in each :meth:`_input_streams` - in + the same order - that are free of undetermined coefficients. This is used in :meth:`_subs_in_caches` to only substitute items that may contain undetermined coefficients. - It might be better to share this across all uninitialized - series in one system. + .. TODO:: + + It might be an better to share this across all uninitialized + series in one system. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C._good_cache + [0, 0, 0, 0] + sage: C[1] + 1 + sage: C._good_cache + [1, 0, 1, 0] """ g = [] for c in self._input_streams: @@ -1495,7 +1570,12 @@ def __getitem__(self, n): EXAMPLES:: - sage: + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_cauchy_compose + sage: x = Stream_exact([1], order=1) + sage: A = Stream_uninitialized(1) + sage: A.define(Stream_add(x, Stream_cauchy_mul(x, Stream_cauchy_compose(A, A, True), True), True)) + sage: [A[n] for n in range(10)] + [0, 1, 1, 2, 6, 23, 104, 531, 2982, 18109] """ if n < self._approximate_order: return ZZ.zero() @@ -1554,6 +1634,18 @@ def _subs_in_caches(self, var, val): - ``var``, a variable in ``self._P`` - ``val``, the value that should replace the variable + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C[3] # implicit doctest + 2 """ def subs(c, var, val): P = self._P.polynomial_ring() @@ -1621,8 +1713,30 @@ def retract(c): self._pool.del_variable(var) def _compute(self): - """ - Solve the next equations, until the next variable is determined. + r""" + Determine the next equations by comparing coefficients, and solve + those which are linear. + + For each of the equations in the given list of equations + ``self._eqs``, we determine the first coefficient which is + non-zero. Among these, we only keep the coefficients which + are linear, i.e., whose total degree is one, and those which + are a single variable raised to a positive power, implying + that the variable itself must be zero. We then solve the + resulting linear system. If the system does not determine + any variable, we raise an error. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C[3] # implicit doctest + 2 """ # determine the next linear equations coeffs = [] @@ -1641,8 +1755,8 @@ def _compute(self): if self._base_ring == self._coefficient_ring: lcoeff = [coeff] else: - # TODO: it is a coincidence that this currently - # exists in all examples + # TODO: it is a coincidence that `coefficients` + # currently exists in all examples lcoeff = coeff.coefficients() for c in lcoeff: @@ -2107,7 +2221,7 @@ def order(self): sage: s.order() +Infinity """ - return self._approximate_order # == infinity + return self._approximate_order # == infinity def __eq__(self, other): """ @@ -2177,6 +2291,7 @@ class Stream_add(Stream_binaryCommutative): - ``left`` -- :class:`Stream` of coefficients on the left side of the operator - ``right`` -- :class:`Stream` of coefficients on the right side of the operator + - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse EXAMPLES:: @@ -2235,6 +2350,7 @@ class Stream_sub(Stream_binary): - ``left`` -- :class:`Stream` of coefficients on the left side of the operator - ``right`` -- :class:`Stream` of coefficients on the right side of the operator + - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse EXAMPLES:: @@ -2301,6 +2417,7 @@ class Stream_cauchy_mul(Stream_binary): - ``left`` -- :class:`Stream` of coefficients on the left side of the operator - ``right`` -- :class:`Stream` of coefficients on the right side of the operator + - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse EXAMPLES:: @@ -2774,7 +2891,7 @@ def get_coefficient(self, n): return sum((c * self.compute_product(n, la) for k in range(self._left._approximate_order, K) - if self._left[k] # necessary, because it might be int(0) + if self._left[k] # necessary, because it might be int(0) for la, c in self._left[k]), self._basis.zero()) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index e74ede206fa..d2470cc103f 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -942,7 +942,7 @@ def define_implicitly(self, series, equations): Permutations with two kinds of labels such that each cycle contains at least one element of each kind (defined - implicitely to have a test):: + implicitly to have a test):: sage: p = SymmetricFunctions(QQ).p() sage: S = LazySymmetricFunctions(p) @@ -2954,7 +2954,6 @@ def taylor(self, f): BR = R.base_ring() args = f.arguments() subs = {str(va): ZZ.zero() for va in args} - gens = R.gens() ell = len(subs) from sage.combinat.integer_vector import integer_vectors_nk_fast_iter from sage.arith.misc import factorial From 4c1682b0f1d32542e5dffc5535739f8794b8c86b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 4 Apr 2024 20:25:42 +0200 Subject: [PATCH 111/507] improve documentation of doctests --- src/sage/rings/lazy_series_ring.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index d2470cc103f..f053e5a1875 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -777,6 +777,9 @@ def define_implicitly(self, series, equations): We need to specify the initial values for the degree 1 and 2 components to get a unique solution in the previous example:: + sage: L. = LazyPowerSeriesRing(QQ["x,y,f1"].fraction_field()) + sage: L.base_ring().inject_variables() + Defining x, y, f1 sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F @@ -825,7 +828,7 @@ def define_implicitly(self, series, equations): sage: f[1] 0 - Some systems of two coupled functional equations:: + Some systems of coupled functional equations:: sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() @@ -877,17 +880,22 @@ def define_implicitly(self, series, equations): sage: A = L.undefined() sage: B = L.undefined() sage: C = L.undefined() - sage: D = L.undefined() - sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) - sage: B[2] # known bug, not tested + sage: L.define_implicitly([A, B, C], [B - C - 1, B*z + 2*C + 1, A + 2*C + 1]) + sage: A + 2*C + 1 + O(z^7) + + The following system does not determine `B`, but the solver + will inductively discover that each coefficient of `A` must + be zero. Therefore, asking for a coefficient of `B` will + loop forever:: sage: L. = LazyPowerSeriesRing(QQ) sage: A = L.undefined() sage: B = L.undefined() sage: C = L.undefined() - sage: L.define_implicitly([A, B, C], [B - C - 1, B*z + 2*C + 1, A + 2*C + 1]) - sage: A + 2*C + 1 - O(z^7) + sage: D = L.undefined() + sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) + sage: B[2] # known bug, not tested A bivariate example:: @@ -954,6 +962,7 @@ def define_implicitly(self, series, equations): sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) sage: A[:4] [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] + """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream From 74c1c4ddea47af6cd010a455b53a8f51b28c9a57 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sat, 6 Apr 2024 18:23:15 +0200 Subject: [PATCH 112/507] Implemented reviewer feedback --- src/sage/algebras/quatalg/quaternion_algebra.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index d6d245c2b89..be1a2a622b8 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1090,7 +1090,7 @@ def is_definite(self): The method does not make sense over an arbitrary base ring:: - sage: QuaternionAlgebra(RR(2.),1).is_definite() + sage: QuaternionAlgebra(RR(2.), 1).is_definite() Traceback (most recent call last): ... ValueError: base field must be rational numbers @@ -1127,7 +1127,7 @@ def is_totally_definite(self): The method does not make sense over an arbitrary base ring:: - sage: QuaternionAlgebra(RR(2.),1).is_totally_definite() + sage: QuaternionAlgebra(RR(2.), 1).is_totally_definite() Traceback (most recent call last): ... ValueError: base field must be rational numbers or a number field @@ -1144,7 +1144,7 @@ def is_totally_definite(self): # method by directly comparing the embedding list's length to the degree E = F.real_embeddings() return len(E) == F.degree() and all(F.hilbert_symbol(self._a, self._b, e) == -1 - for e in E) + for e in E) @cached_method def ramified_places(self, inf=True): @@ -1382,12 +1382,12 @@ def discriminant(self): return F.ideal(F.prod(self.ramified_places(inf=False))) def is_isomorphic(self, A) -> bool: - """ + r""" Check whether this quaternion algebra is isomorphic to ``A``. Currently only implemented for quaternion algebras defined over a number field; based on Main Theorem 14.6.1 in [Voi2021]_, - noting that `\\QQ` has a unique infinite place. + noting that `\QQ` has a unique infinite place. INPUT: From fdb99dcfa8fd516176ad4cd230d291274bf36f92 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 20:52:50 +0200 Subject: [PATCH 113/507] Use proper reference role MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/combinat/sf/dual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index aae10602f2d..207e411a2c6 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -292,7 +292,7 @@ def basis_name(self): Return the name of the basis of ``self``. This is used for output and, for the classical bases of - symmetric functions, to connect this basis with Symmetrica. + symmetric functions, to connect this basis with :ref:`Symmetrica `. EXAMPLES:: From acfc7f2f372f8643663ab66c39644d03462ba25f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 20:54:58 +0200 Subject: [PATCH 114/507] Remove wrong indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/data_structures/stream.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 720178a6abf..6dc5306015a 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1434,12 +1434,12 @@ def define_implicitly(self, series, initial_values, equations, INPUT: - - ``series`` -- a list of series - - ``equations`` -- a list of equations defining the series - - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - - ``base_ring`` -- the base ring - - ``coefficient_ring`` -- the ring containing the elements of the stream (after substitution) - - ``terms_of_degree`` -- a function returning the list of terms of a given degree + - ``series`` -- a list of series + - ``equations`` -- a list of equations defining the series + - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` + - ``base_ring`` -- the base ring + - ``coefficient_ring`` -- the ring containing the elements of the stream (after substitution) + - ``terms_of_degree`` -- a function returning the list of terms of a given degree EXAMPLES:: From b36cfea96a77082ddb92fae3f484091ac4c85438 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 20:57:01 +0200 Subject: [PATCH 115/507] Fix missing backticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/rings/polynomial/infinite_polynomial_element.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index be383e381f5..0ca5cdfed55 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -622,8 +622,9 @@ def variables(self): def monomials(self): """ - Return the list of monomials in self. The returned list is - decreasingly ordered by the term ordering of + Return the list of monomials in ``self``. + + The returned list is decreasingly ordered by the term ordering of ``self.parent()``. EXAMPLES:: From 4e569b5f7e7d8a3b0fabbd6f51cc11d04a9e13ca Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 20:58:08 +0200 Subject: [PATCH 116/507] Fix missing backticks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/rings/polynomial/infinite_polynomial_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 0ca5cdfed55..306ad66de36 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -570,7 +570,7 @@ def numerator(self): .. warning:: This is not the numerator of the rational function - defined by ``self``, which would always be self since ``self`` is a + defined by ``self``, which would always be ``self`` since it is a polynomial. EXAMPLES: From 891bfe4ecb2d4ce6ea9d3b2db41f4da945a19402 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:14:26 +0200 Subject: [PATCH 117/507] remove misleading sentence and whitespace --- src/sage/rings/polynomial/infinite_polynomial_element.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 306ad66de36..ac507fc2aa2 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -564,9 +564,6 @@ def numerator(self): r""" Return a numerator of ``self``, computed as ``self * self.denominator()``. - Note that some subclasses may implement its own numerator - function. - .. warning:: This is not the numerator of the rational function @@ -622,8 +619,8 @@ def variables(self): def monomials(self): """ - Return the list of monomials in ``self``. - + Return the list of monomials in ``self``. + The returned list is decreasingly ordered by the term ordering of ``self.parent()``. From 54b65c03fed045fc0f645401f22b98257d482055 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:47:12 +0200 Subject: [PATCH 118/507] break expected output of doctest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/combinat/sf/sfa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index d569b6ce1dd..30ced10112c 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -641,7 +641,8 @@ def corresponding_basis_over(self, R): sage: Sym = SymmetricFunctions(P) sage: mj = Sym.macdonald().J() sage: mj.corresponding_basis_over(Integers(13)['q','t']) - Symmetric Functions over Multivariate Polynomial Ring in q, t over Ring of integers modulo 13 in the Macdonald J basis + Symmetric Functions over Multivariate Polynomial Ring in q, t over + Ring of integers modulo 13 in the Macdonald J basis TESTS: From ba1d8fa7c87abc292c90f4cb96ecb818776773c3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:54:28 +0200 Subject: [PATCH 119/507] provide missing doctests --- src/sage/rings/lazy_series_ring.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index f053e5a1875..346f828089d 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -962,7 +962,6 @@ def define_implicitly(self, series, equations): sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) sage: A[:4] [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] - """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -2088,6 +2087,13 @@ def _terms_of_degree(self, n, R): Return the list consisting of a single element ``1`` in the given ring. + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: t = L._terms_of_degree(3, ZZ['x']); t + [1] + sage: t[0].parent() + Univariate Polynomial Ring in x over Integer Ring """ return [R.one()] @@ -2401,6 +2407,18 @@ def __init__(self, base_ring, names, sparse=True, category=None): category=category) def construction(self): + """ + Return a pair ``(F, R)``, where ``F`` is a + :class:`CompletionFunctor` and `R` is a ring, such that + ``F(R)`` returns ``self``. + + EXAMPLES:: + + sage: L = LazyPowerSeriesRing(ZZ, 't') + sage: L.construction() + (Completion[t, prec=+Infinity], + Sparse Univariate Polynomial Ring in t over Integer Ring) + """ from sage.categories.pushout import CompletionFunctor if self._arity == 1: return (CompletionFunctor(self._names[0], infinity), @@ -3813,6 +3831,14 @@ def _monomial(self, c, n): def _terms_of_degree(self, n, R): r""" Return the list consisting of a single element 1 in the base ring. + + EXAMPLES:: + + sage: L = LazyDirichletSeriesRing(ZZ, 'z') + sage: t = L._terms_of_degree(3, ZZ['x']); t + [1] + sage: t[0].parent() + Univariate Polynomial Ring in x over Integer Ring """ return [R.one()] From c62292d47183f8e7ce23d2c84440a53beb23e2c0 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:55:31 +0200 Subject: [PATCH 120/507] add missing colon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/rings/polynomial/infinite_polynomial_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index ac507fc2aa2..6ae25934f21 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -570,7 +570,7 @@ def numerator(self): defined by ``self``, which would always be ``self`` since it is a polynomial. - EXAMPLES: + EXAMPLES:: sage: X. = InfinitePolynomialRing(QQ) sage: p = 2/3*x[1] + 4/9*x[2] - 2*x[1]*x[3] From e419a73f7c4c4b8ebf6c53e0964ce11554ca37de Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:56:23 +0200 Subject: [PATCH 121/507] break expected output of doctest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/combinat/free_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index d70f3539cd4..eb5e8797084 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -500,9 +500,11 @@ def change_ring(self, R): True sage: T = F.tensor(F); T - Free module generated by {'a', 'b', 'c'} over Integer Ring # Free module generated by {'a', 'b', 'c'} over Integer Ring + Free module generated by {'a', 'b', 'c'} over Integer Ring + # Free module generated by {'a', 'b', 'c'} over Integer Ring sage: T.change_ring(QQ) - Free module generated by {'a', 'b', 'c'} over Rational Field # Free module generated by {'a', 'b', 'c'} over Rational Field + Free module generated by {'a', 'b', 'c'} over Rational Field + # Free module generated by {'a', 'b', 'c'} over Rational Field """ if R is self.base_ring(): return self From 716fca461196cb0593102160f0edfff9cdd682c8 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:56:56 +0200 Subject: [PATCH 122/507] add missing # needs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/rings/lazy_series_ring.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 346f828089d..f7fd96b3f59 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -724,6 +724,7 @@ def define_implicitly(self, series, equations): Some more examples over different rings:: + sage: # needs sage.symbolic sage: L. = LazyPowerSeriesRing(SR) sage: G = L.undefined(0) sage: L.define_implicitly([(G, [ln(2)])], [diff(G) - exp(-G(-z))]) From 6c536b04afab7b410c87e26bb8f3ef0855006d02 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:57:36 +0200 Subject: [PATCH 123/507] Remove wrong indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/data_structures/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 6dc5306015a..7f5dc1c3b77 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1410,7 +1410,7 @@ def define(self, target): INPUT: - - ``target`` -- a stream + - ``target`` -- a stream EXAMPLES:: From bd3eaa12bcea2663d98f3b02b1324c208f30b198 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:58:14 +0200 Subject: [PATCH 124/507] break expected output of doctest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/rings/lazy_series_ring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index f7fd96b3f59..1554069a845 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -3190,7 +3190,8 @@ def _terms_of_degree(self, n, R): s[] # s[2, 1], s[] # s[1, 1, 1]] sage: m[0].parent() - Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis # Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis + Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis + # Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis """ from sage.combinat.integer_vector import IntegerVectors from sage.misc.mrange import cartesian_product_iterator From 9ec2d3037f665032f1696dda3248b8b5442c4e7c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 21:58:53 +0200 Subject: [PATCH 125/507] Remove wrong indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- src/sage/data_structures/stream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 7f5dc1c3b77..65a6dd23f90 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1632,8 +1632,8 @@ def _subs_in_caches(self, var, val): INPUT: - - ``var``, a variable in ``self._P`` - - ``val``, the value that should replace the variable + - ``var``, a variable in ``self._P`` + - ``val``, the value that should replace the variable EXAMPLES:: From a782b712592dd1ca7f52fafaa97d2cba60cff22f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 22:06:54 +0200 Subject: [PATCH 126/507] use proper sphinx roles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Köppe --- .../polynomial/infinite_polynomial_element.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 6ae25934f21..729f0f88a28 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -641,26 +641,23 @@ def monomials(self): def monomial_coefficient(self, mon): """ - Return the coefficient in the base ring of the monomial mon in - ``self``, where mon must have the same parent as self. - - This function contrasts with the function ``coefficient`` + Return the base ring element that is the coefficient of ``mon`` in ``self``. + + This function contrasts with the function :meth:`coefficient`, which returns the coefficient of a monomial viewing this polynomial in a polynomial ring over a base ring having fewer variables. INPUT: - - ``mon`` - a monomial - - OUTPUT: + - ``mon`` -- a monomial of the parent of ``self`` - coefficient in base ring + OUTPUT: coefficient in base ring .. SEEALSO:: For coefficients in a base ring of fewer variables, - look at ``coefficient``. + look at :meth:`coefficient`. EXAMPLES:: From 2286ac999da4f86d3ce3675c8fc9010d4dc9911b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 7 Apr 2024 22:16:36 +0200 Subject: [PATCH 127/507] remove whitespace --- src/sage/combinat/free_module.py | 4 ++-- src/sage/combinat/sf/sfa.py | 2 +- src/sage/rings/lazy_series_ring.py | 2 +- src/sage/rings/polynomial/infinite_polynomial_element.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index eb5e8797084..2f153396fc0 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -500,10 +500,10 @@ def change_ring(self, R): True sage: T = F.tensor(F); T - Free module generated by {'a', 'b', 'c'} over Integer Ring + Free module generated by {'a', 'b', 'c'} over Integer Ring # Free module generated by {'a', 'b', 'c'} over Integer Ring sage: T.change_ring(QQ) - Free module generated by {'a', 'b', 'c'} over Rational Field + Free module generated by {'a', 'b', 'c'} over Rational Field # Free module generated by {'a', 'b', 'c'} over Rational Field """ if R is self.base_ring(): diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 30ced10112c..05abd332b08 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -641,7 +641,7 @@ def corresponding_basis_over(self, R): sage: Sym = SymmetricFunctions(P) sage: mj = Sym.macdonald().J() sage: mj.corresponding_basis_over(Integers(13)['q','t']) - Symmetric Functions over Multivariate Polynomial Ring in q, t over + Symmetric Functions over Multivariate Polynomial Ring in q, t over Ring of integers modulo 13 in the Macdonald J basis TESTS: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 1554069a845..9bf9b1df7cd 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -3190,7 +3190,7 @@ def _terms_of_degree(self, n, R): s[] # s[2, 1], s[] # s[1, 1, 1]] sage: m[0].parent() - Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis + Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis # Symmetric Functions over Univariate Polynomial Ring in x over Rational Field in the Schur basis """ from sage.combinat.integer_vector import IntegerVectors diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 729f0f88a28..bfa6fd9dbaf 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -642,7 +642,7 @@ def monomials(self): def monomial_coefficient(self, mon): """ Return the base ring element that is the coefficient of ``mon`` in ``self``. - + This function contrasts with the function :meth:`coefficient`, which returns the coefficient of a monomial viewing this polynomial in a polynomial ring over a base ring having fewer From 642629675a579712d36a51e2c54ef4418e035c3e Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Sun, 14 Apr 2024 01:03:07 +0200 Subject: [PATCH 128/507] Changed number field embedding target to `AA` - Avoids possible future issues caused by rounding precision - Seems to be more efficient Amend: Docstring fix --- .../algebras/quatalg/quaternion_algebra.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index bff96a9d8d9..caf8e1e28e3 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -55,6 +55,7 @@ from sage.rings.rational_field import is_RationalField, QQ from sage.rings.infinity import infinity from sage.rings.number_field.number_field_base import NumberField +from sage.rings.qqbar import AA from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.power_series_ring import PowerSeriesRing from sage.structure.category_object import normalize_names @@ -1235,7 +1236,7 @@ def is_totally_definite(self): # Since we need the list of real embeddings of the number field (instead # of just the number of them), we avoid a call of the `is_totally_real()`- # method by directly comparing the embedding list's length to the degree - E = F.real_embeddings() + E = F.embeddings(AA) return len(E) == F.degree() and all(F.hilbert_symbol(self._a, self._b, e) == -1 for e in E) @@ -1260,7 +1261,11 @@ def ramified_places(self, inf=True): Additionally, if ``inf`` is set to ``True``, then the Archimedean (AKA infinite) places at which the quaternion algebra ramifies are - also returned, given by real embeddings of the base field. + also returned, given as + + - the embeddings of `\QQ` into `\RR` if the base field is `\QQ`, or + + - the embeddings of the base number field into the Algebraic Real Field. .. NOTE:: @@ -1296,12 +1301,12 @@ def ramified_places(self, inf=True): ([], [Ring morphism: From: Number Field in a with defining polynomial x^2 - 3 with a = 1.732050807568878? - To: Real Field with 53 bits of precision - Defn: a |--> -1.73205080756888, + To: Algebraic Real Field + Defn: a |--> -1.732050807568878?, Ring morphism: From: Number Field in a with defining polynomial x^2 - 3 with a = 1.732050807568878? - To: Real Field with 53 bits of precision - Defn: a |--> 1.73205080756888]) + To: Algebraic Real Field + Defn: a |--> 1.732050807568878?]) Extending the base field can also get rid of ramification at infinite places while still leaving some ramification at finite places:: @@ -1318,8 +1323,8 @@ def ramified_places(self, inf=True): ([Fractional ideal (2)], [Ring morphism: From: Number Field in a with defining polynomial x^2 - x - 1 - To: Real Field with 53 bits of precision - Defn: a |--> -0.618033988749895]) + To: Algebraic Real Field + Defn: a |--> -0.618033988749895?]) The method does not make sense over an arbitrary base ring:: @@ -1370,7 +1375,7 @@ def ramified_places(self, inf=True): return ram_fin # At this point the infinite ramified places also need to be computed - return ram_fin, [e for e in F.real_embeddings() if F.hilbert_symbol(a, b, e) == -1] + return ram_fin, [e for e in F.embeddings(AA) if F.hilbert_symbol(a, b, e) == -1] @cached_method def ramified_primes(self): From 11c5cd93ff698493ddf35ffb4f705e3283f3d980 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 18 Apr 2024 14:15:41 +0200 Subject: [PATCH 129/507] dirty fix for composition, add doctests, one works only with rank of InfinitePolynomialFunctor lowered --- src/sage/rings/lazy_series.py | 8 +++++++- src/sage/rings/lazy_series_ring.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 6be1b4ab57d..56aeea83db6 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5268,16 +5268,22 @@ def coefficient(n): # The arity is at least 2 gv = min(h._coeff_stream._approximate_order for h in g) + gR = None def coefficient(n): + nonlocal gR r = R.zero() for i in range(n // gv + 1): c = coeff_stream[i] if c in self.base_ring(): c = P(c) r += c[n] - else: + elif c.parent().base_ring() is self.base_ring(): r += c(g)[n] + else: + if gR is None: + gR = [h.change_ring(c.parent().base_ring()) for h in g] + r += c(gR)[n] return r return P.element_class(P, Stream_function(coefficient, diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 6f2ecc6c357..80c333b4d08 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -807,6 +807,29 @@ def define_implicitly(self, series, equations): sage: g + A bivariate example:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: B = L.undefined() + sage: eq = y*B^2 + 1 - B(x, x-y) + sage: L.define_implicitly([B], [eq]) + sage: B + 1 + (x-y) + (2*x*y-2*y^2) + (4*x^2*y-7*x*y^2+3*y^3) + + (2*x^3*y+6*x^2*y^2-18*x*y^3+10*y^4) + + (30*x^3*y^2-78*x^2*y^3+66*x*y^4-18*y^5) + + (28*x^4*y^2-12*x^3*y^3-128*x^2*y^4+180*x*y^5-68*y^6) + O(x,y)^7 + + Knödel walks:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: F = L.undefined() + sage: eq = F(z, x)*(x^2*z-x+z) - (z - x*z^2 - x^2*z^2)*F(z, 0) + x + sage: L.define_implicitly([F], [eq]) + sage: F + 1 + (2*z^2+z*x) + (z^3+z^2*x) + (5*z^4+3*z^3*x+z^2*x^2) + + (5*z^5+4*z^4*x+z^3*x^2) + (15*z^6+10*z^5*x+4*z^4*x^2+z^3*x^3) + + O(z,x)^7 + TESTS:: sage: L. = LazyPowerSeriesRing(QQ) From 8e088caafb66c538f4750fc67017a57c56f13b0f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 2 May 2024 12:10:55 +0200 Subject: [PATCH 130/507] improve error messages --- src/sage/data_structures/stream.py | 123 +++++++++++++++++++++-------- src/sage/rings/lazy_series_ring.py | 119 +++++++++++++++++----------- 2 files changed, 163 insertions(+), 79 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 65a6dd23f90..0c829bcef89 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1284,9 +1284,9 @@ def __init__(self, ring): sage: TestSuite(P).run() """ self._gen = ring.gen(0) # alternatively, make :class:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. - self._pool = [] # list of variables actually used + self._pool = dict() # dict of variables actually used to names - def new_variable(self): + def new_variable(self, name=None): """ Return an unused variable. @@ -1312,7 +1312,10 @@ def new_variable(self): for i in range(len(self._pool) + 1): v = self._gen[i] if v not in self._pool: - self._pool.append(v) + if name is None: + self._pool[v] = v + else: + self._pool[v] = name return v def del_variable(self, v): @@ -1331,7 +1334,10 @@ def del_variable(self, v): sage: v = P.new_variable(); v a_1 """ - self._pool.remove(v) + del self._pool[v] + + def variables(self): + return self._pool class Stream_uninitialized(Stream): @@ -1361,7 +1367,7 @@ class Stream_uninitialized(Stream): sage: C[4] 0 """ - def __init__(self, approximate_order, true_order=False): + def __init__(self, approximate_order, true_order=False, name=None): """ Initialize ``self``. @@ -1379,6 +1385,7 @@ def __init__(self, approximate_order, true_order=False): self._approximate_order = approximate_order self._initializing = False self._is_sparse = False + self._name = name def input_streams(self): r""" @@ -1476,6 +1483,13 @@ def define_implicitly(self, series, initial_values, equations, self._eqs = equations self._series = series self._terms_of_degree = terms_of_degree + # currently name is used only for error messages + if self._name is None: + # if used for something global (e.g. an expression tree) + # need to make this unique + self._name = "series" + if len(self._series) > 1: + self._name += str(self._series.index(self)) @lazy_attribute def _input_streams(self): @@ -1620,8 +1634,13 @@ def __getitem__(self, n): if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] - x = sum(self._pool.new_variable() * m - for m in self._terms_of_degree(n, self._PF)) + if self._coefficient_ring == self._base_ring: + x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % n)) * m + for m in self._terms_of_degree(n, self._PF)) + else: + x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % m)) * m + for m in self._terms_of_degree(n, self._PF)) + self._cache.append(x) return x @@ -1740,7 +1759,7 @@ def _compute(self): """ # determine the next linear equations coeffs = [] - non_linear_coeffs = [] + all_coeffs = [] # only for the error message for i, eq in enumerate(self._eqs): while True: ao = eq._approximate_order @@ -1753,39 +1772,46 @@ def _compute(self): eq._approximate_order += 1 if self._base_ring == self._coefficient_ring: - lcoeff = [coeff] + lcoeff = [(ao, coeff)] else: # TODO: it is a coincidence that `coefficients` - # currently exists in all examples - lcoeff = coeff.coefficients() + # currently exists in all examples; + # the monomials are only needed for the error messages + lcoeff = list(zip(coeff.monomials(), coeff.coefficients())) + + all_coeffs.append(lcoeff) - for c in lcoeff: - c = self._PF(c).numerator() - V = c.variables() + for idx, c in lcoeff: + c_num = self._PF(c).numerator() + V = c_num.variables() if not V: if len(self._eqs) == 1: - raise ValueError(f"no solution as {coeff} != 0 in the equation at degree {eq._approximate_order}") - raise ValueError(f"no solution as {coeff} != 0 in equation {i} at degree {eq._approximate_order}") - if c.degree() <= 1: - coeffs.append(c) - elif c.is_monomial() and sum(1 for d in c.degrees() if d): + if self._base_ring == self._coefficient_ring: + raise ValueError(f"no solution as the coefficient in degree {idx} of the equation is {coeff} != 0") + raise ValueError(f"no solution as the coefficient of {idx} of the equation is {coeff} != 0") + raise ValueError(f"no solution as the coefficient of {idx} in equation {i} is {coeff} != 0") + if c_num.degree() <= 1: + coeffs.append(c_num) + elif c_num.is_monomial() and sum(1 for d in c_num.degrees() if d): # if we have a single variable, we can remove the # exponent - maybe we could also remove the # coefficient - are we computing in an integral # domain? - c1 = c.coefficients()[0] - v = self._P(c.variables()[0]) + c1 = c_num.coefficients()[0] + v = self._P(c_num.variables()[0]) coeffs.append(c1 * v) - else: - # nonlinear equations must not be discarded, we - # collect them to improve any error messages - non_linear_coeffs.append(c) if not coeffs: if len(self._eqs) == 1: - raise ValueError(f"there are no linear equations in degree {self._approximate_order}: {non_linear_coeffs}") - degrees = [eq._approximate_order for eq in self._eqs] - raise ValueError(f"there are no linear equations in degrees {degrees}: {non_linear_coeffs}") + raise ValueError(f"there are no linear equations:\n " + + "\n ".join(self._eq_str(idx, eq) + for idx, eq in all_coeffs[0])) + raise ValueError(f"there are no linear equations:\n" + + "\n".join(f"equation {i}:\n " + + "\n ".join(self._eq_str(idx, eq) + for idx, eq in eqs) + for i, eqs in enumerate(all_coeffs))) + # solve from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence eqs = PolynomialSequence(coeffs) @@ -1805,7 +1831,7 @@ def _compute(self): x = m.solve_right(b) k = m.right_kernel_matrix() # substitute - bad = True + bad = True # indicates whether we could not determine any coefficients for i, (var, y) in enumerate(zip(v, x)): if k.column(i).is_zero(): val = self._base_ring(y) @@ -1813,10 +1839,41 @@ def _compute(self): bad = False if bad: if len(self._eqs) == 1: - assert len(coeffs) + len(non_linear_coeffs) == 1 - raise ValueError(f"could not determine any coefficients using the equation in degree {self._eqs[0]._approximate_order}: {(coeffs + non_linear_coeffs)[0]}") - degrees = [eq._approximate_order for eq in self._eqs] - raise ValueError(f"could not determine any coefficients using the equations in degrees {degrees}: {coeffs + non_linear_coeffs}") + raise ValueError(f"could not determine any coefficients:\n " + + "\n ".join(self._eq_str(idx, eq) + for idx, eq in all_coeffs[0])) + + raise ValueError(f"could not determine any coefficients:\n" + + "\n".join(f"equation {i}:\n " + + "\n ".join(self._eq_str(idx, eq) + for idx, eq in eqs) + for i, eqs in enumerate(all_coeffs))) + + def _eq_str(self, idx, eq): + """ + Return a string describing the equation ``eq`` obtained by setting + the coefficient ``idx`` to zero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_cauchy_mul, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: C = Stream_uninitialized(0) + sage: eq = Stream_sub(C, Stream_cauchy_mul(C, C, True), True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C[3] # implicit doctest + Traceback (most recent call last): + ... + ValueError: there are no linear equations: + [0]: -series[0]^2 + series[0] == 0 + + """ + s = repr(eq) + d = self._pool.variables() + # we have to replace longer variables first + for v in sorted(d, key=lambda v: -len(str(v))): + s = s.replace(repr(v), d[v]) + return repr([idx]) + ": " + s + " == 0" def iterate_coefficients(self): """ diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 40d516e8c67..ae6e1951a54 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -93,6 +93,7 @@ from types import GeneratorType + class LazySeriesRing(UniqueRepresentation, Parent): """ Abstract base class for lazy series. @@ -620,7 +621,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No raise ValueError(f"unable to convert {x} into {self}") - def undefined(self, valuation=None): + def undefined(self, valuation=None, name=None): r""" Return an uninitialized series. @@ -654,7 +655,7 @@ def undefined(self, valuation=None): """ if valuation is None: valuation = self._minimal_valuation - coeff_stream = Stream_uninitialized(valuation) + coeff_stream = Stream_uninitialized(valuation, name=name) return self.element_class(self, coeff_stream) unknown = undefined @@ -784,12 +785,14 @@ def define_implicitly(self, series, equations): sage: F = L.undefined() sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F - + Laurent series examples:: @@ -804,7 +807,7 @@ def define_implicitly(self, series, equations): sage: g = L.undefined(-2) sage: L.define_implicitly([(g, [5])], [2+z*g(z^2) - g]) sage: g - + A bivariate example:: @@ -829,6 +832,45 @@ def define_implicitly(self, series, equations): + (5*z^5+4*z^4*x+z^3*x^2) + (15*z^6+10*z^5*x+4*z^4*x^2+z^3*x^3) + O(z,x)^7 + Bicolored rooted trees with black and white roots:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: B = L.undefined() + sage: L.define_implicitly([A, B], [A - x*exp(B), B - y*exp(A)]) + sage: A + x + x*y + (x^2*y+1/2*x*y^2) + (1/2*x^3*y+2*x^2*y^2+1/6*x*y^3) + + (1/6*x^4*y+3*x^3*y^2+2*x^2*y^3+1/24*x*y^4) + + (1/24*x^5*y+8/3*x^4*y^2+27/4*x^3*y^3+4/3*x^2*y^4+1/120*x*y^5) + + O(x,y)^7 + + sage: h = SymmetricFunctions(QQ).h() + sage: S = LazySymmetricFunctions(h) + sage: E = S(lambda n: h[n]) + sage: T = LazySymmetricFunctions(tensor([h, h])) + sage: X = tensor([h[1],h[[]]]) + sage: Y = tensor([h[[]],h[1]]) + sage: A = T.undefined() + sage: B = T.undefined() + sage: T.define_implicitly([A, B], [A - X*E(B), B - Y*E(A)]) + sage: A[:3] + [h[1] # h[], h[1] # h[1]] + + Permutations with two kinds of labels such that each cycle + contains at least one element of each kind (defined + implicitly to have a test):: + + sage: p = SymmetricFunctions(QQ).p() + sage: S = LazySymmetricFunctions(p) + sage: P = S(lambda n: sum(p[la] for la in Partitions(n))) + sage: T = LazySymmetricFunctions(tensor([p, p])) + sage: X = tensor([p[1],p[[]]]) + sage: Y = tensor([p[[]],p[1]]) + sage: A = T.undefined() + sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) + sage: A[:4] + [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] + TESTS:: sage: L. = LazyPowerSeriesRing(QQ) @@ -919,7 +961,7 @@ def define_implicitly(self, series, equations): sage: C = L.undefined() sage: D = L.undefined() sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) - sage: B[2] # known bug, not tested + sage: B[2] # not tested A bivariate example:: @@ -937,55 +979,38 @@ def define_implicitly(self, series, equations): sage: g z*q + z^2*q + z^3*q + (z^4*q+z^3*q^2) + (z^5*q+3*z^4*q^2) + O(z,q)^7 - The following does not work currently, because the equations + The following does not work, because the equations determining the coefficients come in bad order:: sage: L. = LazyPowerSeriesRing(QQ) - sage: M1 = L.undefined() - sage: M2 = L.undefined() - sage: eq1 = t*x*y*M2(0, 0, t) + (t - x*y)*M1(x, y, t) + x*y - t*M1(0, y, t) - sage: eq2 = (t*x-t)*M2(0, y, t) + (t - x*y)*M2(x, y, t) - sage: L.define_implicitly([M1, M2], [eq1, eq2]) - sage: M1[1] # known bug, not tested - - Bicolored rooted trees with black and white roots:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: A = L.undefined() - sage: B = L.undefined() - sage: L.define_implicitly([A, B], [A - x*exp(B), B - y*exp(A)]) - sage: A - x + x*y + (x^2*y+1/2*x*y^2) + (1/2*x^3*y+2*x^2*y^2+1/6*x*y^3) - + (1/6*x^4*y+3*x^3*y^2+2*x^2*y^3+1/24*x*y^4) - + (1/24*x^5*y+8/3*x^4*y^2+27/4*x^3*y^3+4/3*x^2*y^4+1/120*x*y^5) - + O(x,y)^7 - - sage: h = SymmetricFunctions(QQ).h() - sage: S = LazySymmetricFunctions(h) - sage: E = S(lambda n: h[n]) - sage: T = LazySymmetricFunctions(tensor([h, h])) - sage: X = tensor([h[1],h[[]]]) - sage: Y = tensor([h[[]],h[1]]) - sage: A = T.undefined() - sage: B = T.undefined() - sage: T.define_implicitly([A, B], [A - X*E(B), B - Y*E(A)]) - sage: A[:3] - [h[1] # h[], h[1] # h[1]] + sage: A = L.undefined(name="A") + sage: B = L.undefined(name="B") + sage: eq0 = t*x*y*B(0, 0, t) + (t - x*y)*A(x, y, t) + x*y - t*A(0, y, t) + sage: eq1 = (t*x-t)*B(0, y, t) + (t - x*y)*B(x, y, t) + sage: L.define_implicitly([A, B], [eq0, eq1]) + sage: A[1] + Traceback (most recent call last): + ... + ValueError: could not determine any coefficients: + equation 0: + [x*y*t]: A[x*y] - A[t] == 0 + equation 1: + [x*y*t]: B[x*y] - B[t] == 0 + [x*t^2]: B[x*t] + B[t] == 0 - Permutations with two kinds of labels such that each cycle - contains at least one element of each kind (defined - implicitly to have a test):: + Check the error message in the case of symmetric functions:: sage: p = SymmetricFunctions(QQ).p() sage: S = LazySymmetricFunctions(p) - sage: P = S(lambda n: sum(p[la] for la in Partitions(n))) - sage: T = LazySymmetricFunctions(tensor([p, p])) sage: X = tensor([p[1],p[[]]]) sage: Y = tensor([p[[]],p[1]]) - sage: A = T.undefined() - sage: T.define_implicitly([A], [P(X)*P(Y)*A - P(X+Y)]) - sage: A[:4] - [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] + sage: A = T.undefined(name="A") + sage: B = T.undefined(name="B") + sage: T.define_implicitly([A, B], [X*A - Y*B]) + sage: A + + """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -1575,6 +1600,7 @@ def _test_revert(self, **options): # we want to test at least 2 elements tester.assertGreater(count, 1, msg="only %s elements in %s.some_elements() have a compositional inverse" % (count, self)) + class LazyLaurentSeriesRing(LazySeriesRing): r""" The ring of lazy Laurent series. @@ -3476,6 +3502,7 @@ def some_elements(self): ###################################################################### + class LazySymmetricFunctions(LazyCompletionGradedAlgebra): """ The ring of lazy symmetric functions. From 85bcdf812011d420f490ab63360b282ec349178e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 2 May 2024 18:26:14 +0200 Subject: [PATCH 131/507] fix filling of cache, improve documentation --- src/sage/data_structures/stream.py | 64 +++++++++++++++++++++++++----- src/sage/rings/lazy_series_ring.py | 29 +++++++++++--- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 0c829bcef89..43c3a2b5051 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1582,6 +1582,48 @@ def __getitem__(self, n): - ``n`` -- integer; the index + This method handles uninitialized streams no matter whether + they are defined using :meth:`define` or + :meth:`define_implicitly`. + + In the first case, we rely on coefficients being computed + lazily. More precisely, the value of the requested + coefficient must only depend on the preceding coefficients. + + In the second case, we use a variable to represent the + undetermined coefficient. Let us consider the simplest case + where each term of the stream corresponds to an element of + the series, i.e., ``self._coefficient_ring == + self._base_ring``, and suppose that we are requesting the + first coefficient which has not yet been determined. + + The logic of this method is such that, when called while + ``self._uncomputed == True``, it only returns (without error) + once the value of the variable representing the coefficient + has been successfully determined. In this case the variable + is replaced by its value in all caches of the + :meth:`input_streams`. + + When retrieving the next non-vanishing terms of the given + equations (in :meth:`_compute`), this method + (:meth:`__getitem__`) will be called again (possibly also for + other coefficients), however with the flag ``self._uncomputed + == False``. This entails that a variable is created for the + requested coefficients. + + Note that, only when ``self._uncomputed == False``, elements + from the cache which contain undetermined coefficients (i.e., + variables) are returned. This is achieved by storing the + number of valid coefficients in the cache in + :meth:`_good_cache`. + + From these equations we select the linear ones (in + :meth:`_compute`). We then solve this system of linear + equations, and substitute back any uniquely determined + coefficients in the caches of all input streams (in + :meth:`_subs_in_caches`). We repeat, until the requested + coefficient has been determined. + EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_cauchy_compose @@ -1590,6 +1632,7 @@ def __getitem__(self, n): sage: A.define(Stream_add(x, Stream_cauchy_mul(x, Stream_cauchy_compose(A, A, True), True), True)) sage: [A[n] for n in range(10)] [0, 1, 1, 2, 6, 23, 104, 531, 2982, 18109] + """ if n < self._approximate_order: return ZZ.zero() @@ -1634,14 +1677,17 @@ def __getitem__(self, n): if len(self._cache) > n - self._approximate_order: return self._cache[n - self._approximate_order] - if self._coefficient_ring == self._base_ring: - x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % n)) * m - for m in self._terms_of_degree(n, self._PF)) - else: - x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % m)) * m - for m in self._terms_of_degree(n, self._PF)) + # it may happen, that a variable for a coefficient of higher + # degree is requested, so we have to fill in all the degrees + for n0 in range(len(self._cache) + self._approximate_order, n+1): + if self._coefficient_ring == self._base_ring: + x = (self._PF(self._pool.new_variable(self._name + "[%s]" % n0)) + * self._terms_of_degree(n0, self._PF)[0]) + else: + x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % m)) * m + for m in self._terms_of_degree(n0, self._PF)) + self._cache.append(x) - self._cache.append(x) return x def _subs_in_caches(self, var, val): @@ -1865,7 +1911,7 @@ def _eq_str(self, idx, eq): Traceback (most recent call last): ... ValueError: there are no linear equations: - [0]: -series[0]^2 + series[0] == 0 + coefficient [0]: -series[0]^2 + series[0] == 0 """ s = repr(eq) @@ -1873,7 +1919,7 @@ def _eq_str(self, idx, eq): # we have to replace longer variables first for v in sorted(d, key=lambda v: -len(str(v))): s = s.replace(repr(v), d[v]) - return repr([idx]) + ": " + s + " == 0" + return "coefficient " + repr([idx]) + ": " + s + " == 0" def iterate_coefficients(self): """ diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index ae6e1951a54..efb09c3c481 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -786,13 +786,13 @@ def define_implicitly(self, series, equations): sage: L.define_implicitly([F], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F + coefficient [3]: 6*series[3] + (-2*x - 2*y)*series[2] + (x*y)*series[1] == 0> sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) sage: F + coefficient [3]: 6*series[3] + (-2*x - 2*y)*series[2] + (x*y*f1) == 0> Laurent series examples:: @@ -993,10 +993,10 @@ def define_implicitly(self, series, equations): ... ValueError: could not determine any coefficients: equation 0: - [x*y*t]: A[x*y] - A[t] == 0 + coefficient [x*y*t]: A[x*y] - A[t] == 0 equation 1: - [x*y*t]: B[x*y] - B[t] == 0 - [x*t^2]: B[x*t] + B[t] == 0 + coefficient [x*y*t]: B[x*y] - B[t] == 0 + coefficient [x*t^2]: B[x*t] + B[t] == 0 Check the error message in the case of symmetric functions:: @@ -1009,7 +1009,24 @@ def define_implicitly(self, series, equations): sage: T.define_implicitly([A, B], [X*A - Y*B]) sage: A + coefficient [p[1] # p[1]]: -B[p[1] # p[]] + A[p[] # p[1]] == 0> + + An example we cannot solve because we only look at the next + non-vanishing equations:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: A = L.undefined() + sage: eq1 = diff(A, x) + diff(A, x, 2) + sage: eq2 = A + diff(A, x) + diff(A, x, 2) + sage: L.define_implicitly([A], [eq1, eq2]) + sage: A[1] + Traceback (most recent call last): + ... + ValueError: could not determine any coefficients: + equation 0: + coefficient [0]: 2*series[2] + series[1] == 0 + equation 1: + coefficient [0]: 2*series[2] + series[1] == 0 """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) From a4dbb419b03c70105ab2ddc7c1ae6a9fc85a55c0 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 3 May 2024 15:19:30 +0200 Subject: [PATCH 132/507] fix typo and implicit doctest -> indirect doctest --- src/sage/data_structures/stream.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 43c3a2b5051..99fc5647caf 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1274,7 +1274,7 @@ class VariablePool(UniqueRepresentation): """ def __init__(self, ring): """ - Inititialize the pool. + Initialize the pool. EXAMPLES:: @@ -1709,7 +1709,7 @@ def _subs_in_caches(self, var, val): sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) sage: eq = Stream_sub(C, D, True) sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) - sage: C[3] # implicit doctest + sage: C[3] # indirect doctest 2 """ def subs(c, var, val): @@ -1800,7 +1800,7 @@ def _compute(self): sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) sage: eq = Stream_sub(C, D, True) sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) - sage: C[3] # implicit doctest + sage: C[3] # indirect doctest 2 """ # determine the next linear equations @@ -1907,7 +1907,7 @@ def _eq_str(self, idx, eq): sage: C = Stream_uninitialized(0) sage: eq = Stream_sub(C, Stream_cauchy_mul(C, C, True), True) sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) - sage: C[3] # implicit doctest + sage: C[3] # indirect doctest Traceback (most recent call last): ... ValueError: there are no linear equations: From d855cc35a1bea69cadd6d1a2b60c873bdac73d7e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 3 May 2024 15:34:31 +0200 Subject: [PATCH 133/507] fix doctest --- src/sage/rings/lazy_series_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index efb09c3c481..ea46201ef06 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1001,7 +1001,7 @@ def define_implicitly(self, series, equations): Check the error message in the case of symmetric functions:: sage: p = SymmetricFunctions(QQ).p() - sage: S = LazySymmetricFunctions(p) + sage: T = LazySymmetricFunctions(tensor([p, p])) sage: X = tensor([p[1],p[[]]]) sage: Y = tensor([p[[]],p[1]]) sage: A = T.undefined(name="A") From e1a26e2407fb7267ebaf61192025e29b8e71596f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 7 May 2024 16:15:20 +0200 Subject: [PATCH 134/507] provide an option to look ahead in the equations, refactor --- src/sage/data_structures/stream.py | 260 ++++++++++++++++++----------- src/sage/rings/lazy_series.py | 4 +- src/sage/rings/lazy_series_ring.py | 31 +++- 3 files changed, 194 insertions(+), 101 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 99fc5647caf..cb81bb13f12 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1307,7 +1307,6 @@ def new_variable(self, name=None): sage: P0 = VariablePool(R0) sage: P0.new_variable() b_0 - """ for i in range(len(self._pool) + 1): v = self._gen[i] @@ -1337,6 +1336,19 @@ def del_variable(self, v): del self._pool[v] def variables(self): + """ + Return the dictionary mapping variables to names. + + EXAMPLES:: + + sage: from sage.data_structures.stream import VariablePool + sage: R. = InfinitePolynomialRing(QQ) + sage: P = VariablePool(R) + sage: P.new_variable("my new variable") + a_2 + sage: P.variables() + {a_0: a_0, a_1: a_1, a_2: 'my new variable'} + """ return self._pool @@ -1348,6 +1360,9 @@ class Stream_uninitialized(Stream): - ``approximate_order`` -- integer; a lower bound for the order of the stream + - ``true_order`` -- boolean; if the approximate order is the actual order + - ``name`` -- string; a name that refers to the undefined + stream in error messages Instances of this class are always dense. @@ -1435,7 +1450,8 @@ def define(self, target): self._iter = self.iterate_coefficients() def define_implicitly(self, series, initial_values, equations, - base_ring, coefficient_ring, terms_of_degree): + base_ring, coefficient_ring, terms_of_degree, + max_lookahead=1): r""" Define ``self`` via ``equations == 0``. @@ -1445,8 +1461,13 @@ def define_implicitly(self, series, initial_values, equations, - ``equations`` -- a list of equations defining the series - ``initial_values`` -- a list specifying ``self[0], self[1], ...`` - ``base_ring`` -- the base ring - - ``coefficient_ring`` -- the ring containing the elements of the stream (after substitution) - - ``terms_of_degree`` -- a function returning the list of terms of a given degree + - ``coefficient_ring`` -- the ring containing the elements of + the stream (after substitution) + - ``terms_of_degree`` -- a function returning the list of + terms of a given degree + - ``max_lookahead`` -- a positive integer specifying how many + elements beyond the approximate order of each equation to + extract linear equations from EXAMPLES:: @@ -1459,6 +1480,7 @@ def define_implicitly(self, series, initial_values, equations, sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) sage: C[6] 42 + """ assert self._target is None @@ -1490,6 +1512,7 @@ def define_implicitly(self, series, initial_values, equations, self._name = "series" if len(self._series) > 1: self._name += str(self._series.index(self)) + self._max_lookahead = max_lookahead @lazy_attribute def _input_streams(self): @@ -1680,11 +1703,13 @@ def __getitem__(self, n): # it may happen, that a variable for a coefficient of higher # degree is requested, so we have to fill in all the degrees for n0 in range(len(self._cache) + self._approximate_order, n+1): + # WARNING: coercing the new variable to self._PF slows + # down the multiplication enormously if self._coefficient_ring == self._base_ring: - x = (self._PF(self._pool.new_variable(self._name + "[%s]" % n0)) + x = (self._pool.new_variable(self._name + "[%s]" % n0) * self._terms_of_degree(n0, self._PF)[0]) else: - x = sum(self._PF(self._pool.new_variable(self._name + "[%s]" % m)) * m + x = sum(self._pool.new_variable(self._name + "[%s]" % m) * m for m in self._terms_of_degree(n0, self._PF)) self._cache.append(x) @@ -1726,6 +1751,15 @@ def retract(c): return num.constant_coefficient() / den.constant_coefficient() return c + def fix_cache(j, s, ao): + if s._cache[ao]: + if s._cache[ao] in self._coefficient_ring: + s._true_order = True + return False + del s._cache[ao] + self._good_cache[j] -= 1 + return True + for j, s in enumerate(self._input_streams): m = len(s._cache) - self._good_cache[j] if s._is_sparse: @@ -1756,117 +1790,110 @@ def retract(c): # fix approximate_order and true_order ao = s._approximate_order if s._is_sparse: - while ao in s._cache: - if s._cache[ao]: - if s._cache[ao] in self._coefficient_ring: - s._true_order = True - break - del s._cache[ao] - self._good_cache[j] -= 1 + while ao in s._cache and fix_cache(j, s, ao): ao += 1 else: - while s._cache: - if s._cache[0]: - if s._cache[0] in self._coefficient_ring: - s._true_order = True - break - del s._cache[0] - self._good_cache[j] -= 1 + while s._cache and fix_cache(j, s, 0): ao += 1 s._approximate_order = ao self._pool.del_variable(var) - def _compute(self): - r""" - Determine the next equations by comparing coefficients, and solve - those which are linear. - - For each of the equations in the given list of equations - ``self._eqs``, we determine the first coefficient which is - non-zero. Among these, we only keep the coefficients which - are linear, i.e., whose total degree is one, and those which - are a single variable raised to a positive power, implying - that the variable itself must be zero. We then solve the - resulting linear system. If the system does not determine - any variable, we raise an error. + def _collect_equations(self, offset): + """ + Return the equations obtained by setting the elements + ``eq._approximate_order + offset`` equal to zero, for each + ``eq`` in ``self._eqs``. EXAMPLES:: - sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_cauchy_mul, Stream_sub sage: terms_of_degree = lambda n, R: [R.one()] - sage: x = Stream_exact([1], order=1) - sage: C = Stream_uninitialized(1) - sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) - sage: eq = Stream_sub(C, D, True) + sage: C = Stream_uninitialized(0) + sage: eq = Stream_sub(C, Stream_cauchy_mul(C, C, True), True) sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) - sage: C[3] # indirect doctest - 2 + sage: C._uncomputed = False + sage: C._collect_equations(0) + ([], [[(0, -FESDUMMY_0^2 + FESDUMMY_0)]]) """ - # determine the next linear equations - coeffs = [] + lin_coeffs = [] all_coeffs = [] # only for the error message for i, eq in enumerate(self._eqs): while True: - ao = eq._approximate_order - coeff = eq[ao] - if coeff: + deg = eq._approximate_order + offset + elt = eq[deg] + if elt: break # it may or may not be the case that the # _approximate_order is advanced by __getitem__ - if eq._approximate_order == ao: - eq._approximate_order += 1 + # still, the following might be unnecessary + for d in range(eq._approximate_order, deg+1): + if not eq[d]: + eq._approximate_order += 1 if self._base_ring == self._coefficient_ring: - lcoeff = [(ao, coeff)] + elt_coeffs = [(deg, elt)] else: # TODO: it is a coincidence that `coefficients` # currently exists in all examples; # the monomials are only needed for the error messages - lcoeff = list(zip(coeff.monomials(), coeff.coefficients())) - - all_coeffs.append(lcoeff) + elt_coeffs = list(zip(elt.monomials(), elt.coefficients())) - for idx, c in lcoeff: - c_num = self._PF(c).numerator() - V = c_num.variables() + all_coeffs.append(elt_coeffs) + for idx, coeff in elt_coeffs: + coeff_num = self._PF(coeff).numerator() + V = coeff_num.variables() if not V: if len(self._eqs) == 1: if self._base_ring == self._coefficient_ring: - raise ValueError(f"no solution as the coefficient in degree {idx} of the equation is {coeff} != 0") - raise ValueError(f"no solution as the coefficient of {idx} of the equation is {coeff} != 0") - raise ValueError(f"no solution as the coefficient of {idx} in equation {i} is {coeff} != 0") - if c_num.degree() <= 1: - coeffs.append(c_num) - elif c_num.is_monomial() and sum(1 for d in c_num.degrees() if d): + raise ValueError(f"no solution as the coefficient in degree {idx} of the equation is {elt} != 0") + raise ValueError(f"no solution as the coefficient of {idx} of the equation is {elt} != 0") + raise ValueError(f"no solution as the coefficient of {idx} in equation {i} is {elt} != 0") + if coeff_num.degree() <= 1: + lin_coeffs.append(coeff_num) + elif coeff_num.is_monomial() and sum(1 for d in coeff_num.degrees() if d): # if we have a single variable, we can remove the # exponent - maybe we could also remove the # coefficient - are we computing in an integral # domain? - c1 = c_num.coefficients()[0] - v = self._P(c_num.variables()[0]) - coeffs.append(c1 * v) - - if not coeffs: - if len(self._eqs) == 1: - raise ValueError(f"there are no linear equations:\n " - + "\n ".join(self._eq_str(idx, eq) - for idx, eq in all_coeffs[0])) - raise ValueError(f"there are no linear equations:\n" - + "\n".join(f"equation {i}:\n " - + "\n ".join(self._eq_str(idx, eq) - for idx, eq in eqs) - for i, eqs in enumerate(all_coeffs))) - - # solve + c1 = coeff_num.coefficients()[0] + v = self._P(coeff_num.variables()[0]) + lin_coeffs.append(c1 * v) + return lin_coeffs, all_coeffs + + def _solve_linear_equations_and_subs(self, lin_coeffs): + """ + Return whether any of the variables is determined uniquely when + setting ``lin_coeffs`` equal to zero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C._uncomputed = False + sage: lin_coeffs, all_coeffs = C._collect_equations(0) + sage: C._cache + [FESDUMMY_1] + sage: lin_coeffs + [FESDUMMY_1 - 1] + sage: C._solve_linear_equations_and_subs(lin_coeffs) + True + sage: C._cache + [1] + """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - eqs = PolynomialSequence(coeffs) + eqs = PolynomialSequence(lin_coeffs) m1, v1 = eqs.coefficients_monomials() # there should be at most one entry in v1 of degree 0 for j, c in enumerate(v1): if c.degree() == 0: b = -m1.column(j) - m = m1.matrix_from_columns([i for i in range(len(v1)) if i != j]) + m = m1.matrix_from_columns(i for i in range(len(v1)) if i != j) v = [c for i, c in enumerate(v1) if i != j] break else: @@ -1875,25 +1902,70 @@ def _compute(self): m = m1 v = list(v1) x = m.solve_right(b) - k = m.right_kernel_matrix() - # substitute - bad = True # indicates whether we could not determine any coefficients - for i, (var, y) in enumerate(zip(v, x)): - if k.column(i).is_zero(): - val = self._base_ring(y) + k = m.right_kernel_matrix(basis="computed").transpose() + good = False + for sol, row, var in zip(x, k, v): + if row.is_zero(): + val = self._base_ring(sol) self._subs_in_caches(var, val) - bad = False - if bad: - if len(self._eqs) == 1: - raise ValueError(f"could not determine any coefficients:\n " - + "\n ".join(self._eq_str(idx, eq) - for idx, eq in all_coeffs[0])) + good = True + return good + + def _compute(self): + r""" + Determine the next equations by comparing coefficients, and solve + those which are linear. + For each of the equations in the given list of equations + ``self._eqs``, we determine the first coefficient which is + non-zero. Among these, we only keep the coefficients which + are linear, i.e., whose total degree is one, and those which + are a single variable raised to a positive power, implying + that the variable itself must be zero. We then solve the + resulting linear system. If the system does not determine + any variable, we raise an error. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_uninitialized, Stream_exact, Stream_cauchy_mul, Stream_add, Stream_sub + sage: terms_of_degree = lambda n, R: [R.one()] + sage: x = Stream_exact([1], order=1) + sage: C = Stream_uninitialized(1) + sage: D = Stream_add(x, Stream_cauchy_mul(C, C, True), True) + sage: eq = Stream_sub(C, D, True) + sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) + sage: C[3] # indirect doctest + 2 + """ + # determine the next linear equations + lin_coeffs = [] + all_coeffs = [] # only for the error message + bad = True # indicates whether we could not determine any coefficients + for offset in range(self._max_lookahead): + new_lin_coeffs, new_all_coeffs = self._collect_equations(offset) + lin_coeffs.extend(new_lin_coeffs) + all_coeffs.extend(new_all_coeffs) + if lin_coeffs and self._solve_linear_equations_and_subs(lin_coeffs): + return + + if len(self._eqs) == 1: + eq_str = "\n ".join(self._eq_str(idx, eq) + for idx, eq in all_coeffs[0]) + if lin_coeffs: + raise ValueError(f"could not determine any coefficients:\n " + + eq_str) + raise ValueError(f"there are no linear equations:\n " + + eq_str) + + eqs_str = "\n".join(f"equation {i}:\n " + + "\n ".join(self._eq_str(idx, eq) + for idx, eq in eqs) + for i, eqs in enumerate(all_coeffs)) + if lin_coeffs: raise ValueError(f"could not determine any coefficients:\n" - + "\n".join(f"equation {i}:\n " - + "\n ".join(self._eq_str(idx, eq) - for idx, eq in eqs) - for i, eqs in enumerate(all_coeffs))) + + eqs_str) + raise ValueError(f"there are no linear equations:\n" + + eqs_str) def _eq_str(self, idx, eq): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 8665ae4559e..9a15cf75ee6 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -4547,7 +4547,7 @@ def integral(self, variable=None, *, constants=None): constants for the integrals of ``self`` (the last constant corresponds to the first integral) - If the first argument is a list, then this method iterprets it as + If the first argument is a list, then this method interprets it as integration constants. If it is a positive integer, the method interprets it as the number of times to integrate the function. If ``variable`` is not the variable of the Laurent series, then @@ -5711,7 +5711,7 @@ def integral(self, variable=None, *, constants=None): specified; the integration constant is taken to be `0`. Now we assume the series is univariate. If the first argument is a - list, then this method iterprets it as integration constants. If it + list, then this method interprets it as integration constants. If it is a positive integer, the method interprets it as the number of times to integrate the function. If ``variable`` is not the variable of the power series, then the coefficients are integrated with respect diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index ea46201ef06..80d9febd025 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -627,7 +627,10 @@ def undefined(self, valuation=None, name=None): INPUT: - - ``valuation`` -- integer; a lower bound for the valuation of the series + - ``valuation`` -- integer; a lower bound for the valuation + of the series + - ``name`` -- string; a name that refers to the undefined + stream in error messages Power series can be defined recursively (see :meth:`sage.rings.lazy_series.LazyModuleElement.define` for @@ -652,6 +655,7 @@ def undefined(self, valuation=None, name=None): sage: f.define(z^-1 + z^2*f^2) sage: f z^-1 + 1 + 2*z + 5*z^2 + 14*z^3 + 42*z^4 + 132*z^5 + O(z^6) + """ if valuation is None: valuation = self._minimal_valuation @@ -673,10 +677,18 @@ def _terms_of_degree(self, n, R): If ``self`` is a lazy symmetric function, this is the list of basis elements of total degree ``n``. + + EXAMPLES:: + + sage: # needs sage.modules + sage: s = SymmetricFunctions(ZZ).s() + sage: L = LazySymmetricFunctions(s) + sage: m = L._terms_of_degree(3, ZZ); m + [s[3], s[2, 1], s[1, 1, 1]] """ raise NotImplementedError - def define_implicitly(self, series, equations): + def define_implicitly(self, series, equations, max_lookahead=1): r""" Define series by solving functional equations. @@ -684,8 +696,11 @@ def define_implicitly(self, series, equations): - ``series`` -- list of undefined series or pairs each consisting of a series and its initial values - - ``equations`` -- list of equations defining the series + - ``max_lookahead``-- (default: ``1``); a positive integer + specifying how many elements beyond the currently known + (i.e., approximate) order of each equation to extract + linear equations from EXAMPLES:: @@ -1028,6 +1043,12 @@ def define_implicitly(self, series, equations): equation 1: coefficient [0]: 2*series[2] + series[1] == 0 + sage: A = L.undefined() + sage: eq1 = diff(A, x) + diff(A, x, 2) + sage: eq2 = A + diff(A, x) + diff(A, x, 2) + sage: L.define_implicitly([A], [eq1, eq2], max_lookahead=2) + sage: A + O(x^7) """ s = [a[0]._coeff_stream if isinstance(a, (tuple, list)) else a._coeff_stream @@ -1040,7 +1061,8 @@ def define_implicitly(self, series, equations): f.define_implicitly(s, ic, eqs, self.base_ring(), self._internal_poly_ring.base_ring(), - self._terms_of_degree) + self._terms_of_degree, + max_lookahead=max_lookahead) class options(GlobalOptions): r""" @@ -1121,7 +1143,6 @@ def one(self): sage: L = LazySymmetricFunctions(m) # needs sage.modules sage: L.one() # needs sage.modules m[] - """ R = self.base_ring() coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=0) From 245f32c2de12f53e390bbf99e30b50e3760e9898 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 8 May 2024 11:07:47 +0200 Subject: [PATCH 135/507] free variables in VariablePool on destruction of a Stream_uninitialized --- src/sage/data_structures/stream.py | 62 +++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index cb81bb13f12..dae09a85d10 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1286,7 +1286,7 @@ def __init__(self, ring): self._gen = ring.gen(0) # alternatively, make :class:`InfinitePolynomialGen` inherit from `UniqueRepresentation`. self._pool = dict() # dict of variables actually used to names - def new_variable(self, name=None): + def new_variable(self, data=None): """ Return an unused variable. @@ -1311,10 +1311,7 @@ def new_variable(self, name=None): for i in range(len(self._pool) + 1): v = self._gen[i] if v not in self._pool: - if name is None: - self._pool[v] = v - else: - self._pool[v] = name + self._pool[v] = data return v def del_variable(self, v): @@ -1337,7 +1334,7 @@ def del_variable(self, v): def variables(self): """ - Return the dictionary mapping variables to names. + Return the dictionary mapping variables to data. EXAMPLES:: @@ -1347,7 +1344,7 @@ def variables(self): sage: P.new_variable("my new variable") a_2 sage: P.variables() - {a_0: a_0, a_1: a_1, a_2: 'my new variable'} + {a_0: None, a_1: None, a_2: 'my new variable'} """ return self._pool @@ -1402,6 +1399,45 @@ def __init__(self, approximate_order, true_order=False, name=None): self._is_sparse = False self._name = name + def __del__(self): + """ + Remove variables from the pool on destruction. + + TESTS:: + + sage: import gc + sage: L. = LazyPowerSeriesRing(ZZ) + sage: A = L.undefined(name="A") + sage: B = L.undefined(name="B") + sage: eq0 = t*x*y*B(0, 0, t) + (t - x*y)*A(x, y, t) + x*y - t*A(0, y, t) + sage: eq1 = (t*x-t)*B(0, y, t) + (t - x*y)*B(x, y, t) + sage: L.define_implicitly([A, B], [eq0, eq1], max_lookahead=2) + sage: A[1] + 0 + sage: pool = A._coeff_stream._pool + sage: len(pool.variables()) + 17 + sage: del A + sage: del B + sage: del eq0 + sage: del eq1 + sage: gc.collect() + ... + sage: len(pool.variables()) + 0 + """ + if hasattr(self, '_pool'): + # self._good_cache[0] is a lower bound + if self._coefficient_ring == self._base_ring: + for c in self._cache[self._good_cache[0]:]: + if c.parent() is self._PF: + self._pool.del_variable(c.numerator()) + else: + for c in self._cache[self._good_cache[0]:]: + for c0 in c.coefficients(): + if c0.parent() is self._PF: + self._pool.del_variable(c0.numerator()) + def input_streams(self): r""" Return the list of streams which are used to compute the @@ -1590,8 +1626,8 @@ def _good_cache(self): else: vals = c._cache i = 0 - for v in vals: - if v not in self._coefficient_ring: + for val in vals: + if val not in self._coefficient_ring: break i += 1 g.append(i) @@ -1987,10 +2023,10 @@ def _eq_str(self, idx, eq): """ s = repr(eq) - d = self._pool.variables() - # we have to replace longer variables first - for v in sorted(d, key=lambda v: -len(str(v))): - s = s.replace(repr(v), d[v]) + p = self._pool.variables() + # we have to replace variables with longer names first + for v in sorted(p, key=lambda v: -len(str(v))): + s = s.replace(repr(v), p[v]) return "coefficient " + repr([idx]) + ": " + s + " == 0" def iterate_coefficients(self): From d64c9688d1e41d5415c623f0c9c257865a13eaef Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 8 May 2024 16:00:52 +0200 Subject: [PATCH 136/507] remove duplicate example, cache _terms_of_degree --- src/sage/rings/lazy_series_ring.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 80d9febd025..33bc6ae5a8e 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -978,14 +978,6 @@ def define_implicitly(self, series, equations, max_lookahead=1): sage: L.define_implicitly([(A, [0,0,0]), (B, [0,0]), (C, [0,0]), (D, [0,0])], [C^2 + D^2, A + B + C + D, A*D]) sage: B[2] # not tested - A bivariate example:: - - sage: R. = LazyPowerSeriesRing(QQ) - sage: g = R.undefined() - sage: R.define_implicitly([g], [g - (z*q + z*g*~(1-g))]) - sage: g - z*q + z^2*q + z^3*q + (z^4*q+z^3*q^2) + (z^5*q+3*z^4*q^2) + O(z,q)^7 - A bivariate example involving composition of series:: sage: R. = LazyPowerSeriesRing(QQ) @@ -2559,6 +2551,7 @@ def _monomial(self, c, n): return L(c) * L.gen() ** n return L(c) + @cached_method def _terms_of_degree(self, n, R): r""" Return the list of monomials of degree ``n`` in the polynomial @@ -3251,6 +3244,7 @@ def _monomial(self, c, n): L = self._laurent_poly_ring return L(c) + @cached_method def _terms_of_degree(self, n, R): r""" Return the list of basis elements of degree ``n``. From 4ceac4c462655578d61670813d419aaf67200a93 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 08:59:41 +0200 Subject: [PATCH 137/507] replace list() with [] --- src/sage/data_structures/stream.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index dae09a85d10..2f2716349c8 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -295,7 +295,7 @@ def __init__(self, is_sparse, true_order): if self._is_sparse: self._cache = dict() # cache of known coefficients else: - self._cache = list() + self._cache = [] self._iter = self.iterate_coefficients() def is_nonzero(self): @@ -384,7 +384,7 @@ def __setstate__(self, d): """ self.__dict__ = d if not self._is_sparse: - self._cache = list() + self._cache = [] self._iter = self.iterate_coefficients() def __getitem__(self, n): @@ -1482,7 +1482,7 @@ def define(self, target): self._target = target self._n = self._approximate_order - 1 # the largest index of a coefficient we know # we only need this if target does not have a dense cache - self._cache = list() + self._cache = [] self._iter = self.iterate_coefficients() def define_implicitly(self, series, initial_values, equations, From 87ec8e47f74fcf843c7da4c2d7a931c764d734a9 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 09:17:46 +0200 Subject: [PATCH 138/507] improve doc formatting Co-authored-by: Travis Scrimshaw --- src/sage/data_structures/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 2f2716349c8..0d24d589670 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1270,7 +1270,7 @@ class VariablePool(UniqueRepresentation): INPUT: - - ``ring``, an :class:`InfinitePolynomialRing`. + - ``ring`` -- :class:`InfinitePolynomialRing` """ def __init__(self, ring): """ From 2355a3263ed7e5bf7f7b316b7eb3fd905d426be2 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 09:30:38 +0200 Subject: [PATCH 139/507] remove trailing blank lines in docstrings --- src/sage/data_structures/stream.py | 11 ----------- src/sage/rings/lazy_series.py | 10 ---------- src/sage/rings/lazy_series_ring.py | 4 ---- 3 files changed, 25 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 0d24d589670..2195d03d3e5 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -135,7 +135,6 @@ class Stream(): However, keep in mind that (trivially) this initialization code is not executed if ``_approximate_order`` is set to a value before it is accessed. - """ def __init__(self, true_order): """ @@ -561,7 +560,6 @@ def __ne__(self, other): True sage: g != f True - """ # TODO: more cases, in particular mixed implementations, # could be detected @@ -824,7 +822,6 @@ def __eq__(self, other): sage: t = Stream_exact([2], order=-1, degree=5, constant=1) sage: s == t False - """ return (isinstance(other, type(self)) and self._degree == other._degree @@ -1008,7 +1005,6 @@ class Stream_function(Stream_inexact): sage: f = Stream_function(lambda n: n, True, 0) sage: f[4] 4 - """ def __init__(self, function, is_sparse, approximate_order, true_order=False): """ @@ -1516,7 +1512,6 @@ def define_implicitly(self, series, initial_values, equations, sage: C.define_implicitly([C], [], [eq], QQ, QQ, terms_of_degree) sage: C[6] 42 - """ assert self._target is None @@ -1691,7 +1686,6 @@ def __getitem__(self, n): sage: A.define(Stream_add(x, Stream_cauchy_mul(x, Stream_cauchy_compose(A, A, True), True), True)) sage: [A[n] for n in range(10)] [0, 1, 1, 2, 6, 23, 104, 531, 2982, 18109] - """ if n < self._approximate_order: return ZZ.zero() @@ -2020,7 +2014,6 @@ def _eq_str(self, idx, eq): ... ValueError: there are no linear equations: coefficient [0]: -series[0]^2 + series[0] == 0 - """ s = repr(eq) p = self._pool.variables() @@ -2997,7 +2990,6 @@ class Stream_plethysm(Stream_binary): sage: r2 = Stream_plethysm(f, g, True, p, include=[]) # needs sage.modules sage: r_s - sum(r2[n] for n in range(2*(r_s.degree()+1))) # needs sage.modules (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] - """ def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): r""" @@ -3213,7 +3205,6 @@ def stretched_power_restrict_degree(self, i, m, d): ....: if sum(mu.size() for mu in m) == 12}) sage: A == B # long time True - """ # TODO: we should do lazy binary powering here while len(self._powers) < m: @@ -3573,7 +3564,6 @@ class Stream_cauchy_invert(Stream_unary): sage: g = Stream_cauchy_invert(f) sage: [g[i] for i in range(10)] [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] - """ def __init__(self, series, approximate_order=None): """ @@ -3817,7 +3807,6 @@ class Stream_map_coefficients(Stream_unary): sage: g = Stream_map_coefficients(f, lambda n: -n, True) sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] - """ def __init__(self, series, function, is_sparse, approximate_order=None, true_order=False): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 9a15cf75ee6..80ec20b26c2 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -576,7 +576,6 @@ def map_coefficients(self, f): sage: f = z + z^2 + z^3 sage: f.map_coefficients(lambda c: c + 1) 2*z + 2*z^2 + 2*z^3 - """ P = self.parent() coeff_stream = self._coeff_stream @@ -2701,7 +2700,6 @@ def arcsinh(self): sage: L. = LazyLaurentSeriesRing(SR); x = var("x") # needs sage.symbolic sage: asinh(z)[0:6] == asinh(x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic True - """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) @@ -2739,7 +2737,6 @@ def arctanh(self): sage: L. = LazyLaurentSeriesRing(SR); x = var("x") # needs sage.symbolic sage: atanh(z)[0:6] == atanh(x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic True - """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) @@ -2774,7 +2771,6 @@ def hypergeometric(self, a, b): sage: L. = LazyLaurentSeriesRing(SR); x = var("x") # needs sage.symbolic sage: z.hypergeometric([1,1],[1])[0:6] == hypergeometric([1,1],[1], x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic True - """ from .lazy_series_ring import LazyLaurentSeriesRing from sage.arith.misc import rising_factorial @@ -4126,7 +4122,6 @@ def __call__(self, g): sage: g = L([2]) sage: f(g) 0 - """ # Find a good parent for the result from sage.structure.element import get_coercion_model @@ -4807,7 +4802,6 @@ def polynomial(self, degree=None, name=None): sage: L.zero().polynomial() 0 - """ S = self.parent() @@ -4977,7 +4971,6 @@ def _im_gens_(self, codomain, im_gens, base_map=None): sage: f = 1/(1+x*q-t) sage: f._im_gens_(S, [s, x*s], base_map=cc) 1 + 2*x*s + 4*x^2*s^2 + 8*x^3*s^3 + 16*x^4*s^4 + 32*x^5*s^5 + 64*x^6*s^6 + O(s^7) - """ if base_map is None: return codomain(self(*im_gens)) @@ -6642,7 +6635,6 @@ def revert(self): - Andrew Gainer-Dewar - Martin Rubey - """ P = self.parent() if P._arity != 1: @@ -7169,7 +7161,6 @@ def arithmetic_product(self, *args): sage: L = LazySymmetricFunctions(s) # needs sage.modules sage: L(s([2])).arithmetic_product(s([1,1,1])) # needs sage.modules s[2, 2, 1, 1] + s[3, 1, 1, 1] + s[3, 2, 1] + s[3, 3] - """ if len(args) != self.parent()._arity: raise ValueError("arity must be equal to the number of arguments provided") @@ -7294,7 +7285,6 @@ def symmetric_function(self, degree=None): 0 sage: f4.symmetric_function(0) # needs lrcalc_python s[] - """ S = self.parent() R = S._laurent_poly_ring diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 33bc6ae5a8e..20b7933d79f 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -655,7 +655,6 @@ def undefined(self, valuation=None, name=None): sage: f.define(z^-1 + z^2*f^2) sage: f z^-1 + 1 + 2*z + 5*z^2 + 14*z^3 + 42*z^4 + 132*z^5 + O(z^6) - """ if valuation is None: valuation = self._minimal_valuation @@ -2751,7 +2750,6 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No Traceback (most recent call last): ... ValueError: coefficients must be homogeneous polynomials of the correct degree - """ if valuation is not None: if valuation < 0: @@ -3509,7 +3507,6 @@ def some_elements(self): sage: L = S.formal_series_ring() sage: L.some_elements()[:4] [0, S[], 2*S[] + 2*S[1] + (3*S[1,1]), 2*S[1] + (3*S[1,1])] - """ elt = self.an_element() elts = [self.zero(), self.one(), elt] @@ -3662,7 +3659,6 @@ def __init__(self, base_ring, names, sparse=True, category=None): sage: TestSuite(L).run() # needs sage.symbolic sage: LazyDirichletSeriesRing.options._reset() # reset the options - """ if base_ring.characteristic() > 0: raise ValueError("positive characteristic not allowed for Dirichlet series") From e0cb0f37c66afd36011d443e469ab980580d8ff8 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 09:31:30 +0200 Subject: [PATCH 140/507] use list comprehension Co-authored-by: Travis Scrimshaw --- src/sage/data_structures/stream.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 2195d03d3e5..7ed5602c9b9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1045,12 +1045,8 @@ def input_streams(self): closure = self.get_coefficient.__closure__ if closure is None: return [] - l = [] - for cell in closure: - content = cell.cell_contents - if isinstance(content, Stream): - l.append(content) - return l + return [cell.cell_contents for cell in closure + if isinstance(cell.cell_contents, Stream)] def __hash__(self): """ From 688e91a7179e689d6c440486494d5860b8a89dce Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 09:54:55 +0200 Subject: [PATCH 141/507] cosmetic fixes and replace else: if...: with elif: --- src/sage/data_structures/stream.py | 24 +++++++++++------------- src/sage/rings/lazy_series.py | 9 ++++----- src/sage/rings/lazy_series_ring.py | 7 +++---- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 7ed5602c9b9..6108b7568d9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1802,15 +1802,14 @@ def fix_cache(j, s, ao): if self._coefficient_ring == self._base_ring: if c.parent() == self._PF: c = retract(subs(c, var, val)) - if not c.parent() is self._base_ring: - good = m - i0 - 1 - else: - if c.parent() == self._U: - c = c.map_coefficients(lambda e: subs(e, var, val)) - try: - c = c.map_coefficients(lambda e: retract(e), self._base_ring) - except TypeError: + if c.parent() is not self._base_ring: good = m - i0 - 1 + elif c.parent() == self._U: + c = c.map_coefficients(lambda e: subs(e, var, val)) + try: + c = c.map_coefficients(lambda e: retract(e), self._base_ring) + except TypeError: + good = m - i0 - 1 s._cache[i] = c self._good_cache[j] += good # fix approximate_order and true_order @@ -1966,7 +1965,6 @@ def _compute(self): # determine the next linear equations lin_coeffs = [] all_coeffs = [] # only for the error message - bad = True # indicates whether we could not determine any coefficients for offset in range(self._max_lookahead): new_lin_coeffs, new_all_coeffs = self._collect_equations(offset) lin_coeffs.extend(new_lin_coeffs) @@ -1978,9 +1976,9 @@ def _compute(self): eq_str = "\n ".join(self._eq_str(idx, eq) for idx, eq in all_coeffs[0]) if lin_coeffs: - raise ValueError(f"could not determine any coefficients:\n " + raise ValueError("could not determine any coefficients:\n " + eq_str) - raise ValueError(f"there are no linear equations:\n " + raise ValueError("there are no linear equations:\n " + eq_str) eqs_str = "\n".join(f"equation {i}:\n " @@ -1988,9 +1986,9 @@ def _compute(self): for idx, eq in eqs) for i, eqs in enumerate(all_coeffs)) if lin_coeffs: - raise ValueError(f"could not determine any coefficients:\n" + raise ValueError("could not determine any coefficients:\n" + eqs_str) - raise ValueError(f"there are no linear equations:\n" + raise ValueError("there are no linear equations:\n" + eqs_str) def _eq_str(self, idx, eq): diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 80ec20b26c2..15de66cff25 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -851,12 +851,11 @@ def shift(self, n): coeff_stream = Stream_exact(init_coeff, constant=self._coeff_stream._constant, order=valuation, degree=degree) + elif (P._minimal_valuation is not None + and P._minimal_valuation > self._coeff_stream._approximate_order + n): + coeff_stream = Stream_truncated(self._coeff_stream, n, P._minimal_valuation) else: - if (P._minimal_valuation is not None - and P._minimal_valuation > self._coeff_stream._approximate_order + n): - coeff_stream = Stream_truncated(self._coeff_stream, n, P._minimal_valuation) - else: - coeff_stream = Stream_shift(self._coeff_stream, n) + coeff_stream = Stream_shift(self._coeff_stream, n) return P.element_class(P, coeff_stream) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 20b7933d79f..d7512dcdd81 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -2872,11 +2872,10 @@ def y(n): coeff_stream = Stream_function(y, self._sparse, valuation) else: coeff_stream = Stream_iterator(map(R, _skip_leading_zeros(x)), valuation) + elif callable(x): + coeff_stream = Stream_function(lambda i: BR(x(i)), self._sparse, valuation) else: - if callable(x): - coeff_stream = Stream_function(lambda i: BR(x(i)), self._sparse, valuation) - else: - coeff_stream = Stream_iterator(map(BR, _skip_leading_zeros(x)), valuation) + coeff_stream = Stream_iterator(map(BR, _skip_leading_zeros(x)), valuation) return self.element_class(self, coeff_stream) raise ValueError(f"unable to convert {x} into a lazy Taylor series") From b8ecc2856ce40faf77db705a8dbce5f6f76ae9c6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 13:10:09 +0200 Subject: [PATCH 142/507] use PolynomialRing explicitly --- src/sage/rings/polynomial/multi_polynomial.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 37247f36571..0cd77f09e6d 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -416,7 +416,7 @@ cdef class MPolynomial(CommutativePolynomial): del Z[ind] # Make polynomial ring over all variables except var. - S = R.base_ring()[tuple(Z)] + S = PolynomialRing(R.base_ring(), Z) ring = S[var] if not self: return ring(0) From b3105a8db43371318d2a62862e11883d443fca05 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 10 May 2024 13:10:57 +0200 Subject: [PATCH 143/507] naive implementations of is_prime_field and fraction_field for symmetric functions --- src/sage/combinat/sf/sfa.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index d2d9d6385e5..e181a71cd7e 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -412,6 +412,17 @@ def is_integral_domain(self, proof=True): """ return self.base_ring().is_integral_domain() + def fraction_field(self): + if not self.is_integral_domain(): + raise TypeError("self must be an integral domain.") + if hasattr(self, "__fraction_field") and self.__fraction_field is not None: + return self.__fraction_field + else: + import sage.rings.fraction_field + K = sage.rings.fraction_field.FractionField_generic(self) + self.__fraction_field = K + return self.__fraction_field + def is_field(self, proof=True): """ Return whether ``self`` is a field. (It is not.) @@ -1848,6 +1859,9 @@ def __init__(self, Sym, basis_name=None, prefix=None, graded=True): _print_style = 'lex' + def is_prime_field(self): + return False + # Todo: share this with ncsf and over algebras with basis indexed by word-like elements def __getitem__(self, c): r""" From a814c2098f5a29420099d9a327a172b45b2f6518 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 12 May 2024 11:29:40 +0200 Subject: [PATCH 144/507] add (currently failing) example --- src/sage/rings/lazy_series_ring.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index d7512dcdd81..257b7d943dd 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -885,6 +885,15 @@ def define_implicitly(self, series, equations, max_lookahead=1): sage: A[:4] [p[] # p[], 0, p[1] # p[1], p[1] # p[1, 1] + p[1, 1] # p[1]] + The Frobenius character of labelled Dyck words:: + + sage: h = SymmetricFunctions(QQ).h() + sage: L. = LazyPowerSeriesRing(h.fraction_field()) + sage: D = L.undefined() + sage: s1 = L.sum(lambda n: h[n]*t^(n+1)*u^(n-1), 1) + sage: L.define_implicitly([D], [u*D - u - u*s1*D - t*(D - D(t, 0))]) + sage: D + TESTS:: sage: L. = LazyPowerSeriesRing(QQ) From 124c7df339d13630cfaca5c4ad26feb6bd69cf2a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 15 May 2024 15:56:14 +0200 Subject: [PATCH 145/507] work harder putting the coefficients into the correct parent --- src/sage/rings/lazy_series.py | 8 +++++--- src/sage/rings/lazy_series_ring.py | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 15de66cff25..2a0fc0a0083 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -5267,15 +5267,17 @@ def coefficient(n): r = R.zero() for i in range(n // gv + 1): c = coeff_stream[i] - if c in self.base_ring(): + if c.parent() == self.base_ring(): c = P(c) r += c[n] elif c.parent().base_ring() is self.base_ring(): r += c(g)[n] else: if gR is None: - gR = [h.change_ring(c.parent().base_ring()) for h in g] - r += c(gR)[n] + S = c.parent().base_ring() + gR = [h.change_ring(S).map_coefficients(S) for h in g] + s = c(gR)[n] + r += s return r return P.element_class(P, Stream_function(coefficient, diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 41b0b22c976..cbb41861301 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -893,6 +893,9 @@ def define_implicitly(self, series, equations, max_lookahead=1): sage: s1 = L.sum(lambda n: h[n]*t^(n+1)*u^(n-1), 1) sage: L.define_implicitly([D], [u*D - u - u*s1*D - t*(D - D(t, 0))]) sage: D + h[] + h[1]*t^2 + ((h[1,1]+h[2])*t^4+h[2]*t^3*u) + + ((h[1,1,1]+3*h[2,1]+h[3])*t^6+(2*h[2,1]+h[3])*t^5*u+h[3]*t^4*u^2) + + O(t,u)^7 TESTS:: From 5a67fa52be228928a13e3058e708eb96597bd1bb Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 29 May 2024 09:20:42 +0200 Subject: [PATCH 146/507] introduce a functorial construction CoefficientRing --- src/sage/data_structures/stream.py | 107 ++++++++++++++++-- src/sage/modules/free_module_element.pyx | 4 +- src/sage/rings/lazy_series.py | 16 +-- src/sage/rings/lazy_series_ring.py | 14 +-- .../polynomial/infinite_polynomial_ring.py | 2 +- .../polynomial/multi_polynomial_element.py | 6 +- 6 files changed, 116 insertions(+), 33 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 6108b7568d9..9035d2574a7 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -95,6 +95,7 @@ # **************************************************************************** from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ from sage.rings.infinity import infinity from sage.arith.misc import divisors from sage.misc.misc_c import prod @@ -105,6 +106,8 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial +from sage.rings.fraction_field import FractionField_generic +from sage.rings.fraction_field_element import FractionFieldElement from sage.misc.cachefunc import cached_method lazy_import('sage.combinat.sf.sfa', ['_variables_recursive', '_raise_variables']) @@ -1340,6 +1343,84 @@ def variables(self): """ return self._pool +from sage.categories.functor import Functor +from sage.categories.pushout import ConstructionFunctor +from sage.categories.fields import Fields +from sage.categories.integral_domains import IntegralDomains +from sage.categories.quotient_fields import QuotientFields + +class CoefficientRingFunctor(ConstructionFunctor): + rank = 0 + + def __init__(self): + Functor.__init__(self, IntegralDomains(), Fields()) + + def _apply_functor(self, R): + return CoefficientRing(R) + +class CoefficientRingElement(FractionFieldElement): + pass + +class CoefficientRing(UniqueRepresentation, FractionField_generic): + def __init__(self, base_ring): + """ + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRing + sage: PF = CoefficientRing(ZZ) + sage: S. = PF[] + sage: c = PF.gen(0) + sage: p = c * q + sage: p + FESDUMMY_0*q + sage: p.parent() + Multivariate Polynomial Ring in q, t over CoefficientRing over Integer Ring + + sage: p = c + q + sage: p + q + FESDUMMY_0 + sage: p.parent() + Multivariate Polynomial Ring in q, t over CoefficientRing over Integer Ring + + sage: L. = LazyPowerSeriesRing(ZZ) + sage: p = a + c + sage: p.parent() + Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Integer Ring + + sage: S. = ZZ[] + sage: PF = CoefficientRing(S) + sage: L. = LazyPowerSeriesRing(S) + sage: c = PF.gen(0) + sage: p = a + c + sage: p.parent() + Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Multivariate Polynomial Ring in q, t over Integer Ring + + sage: PF = CoefficientRing(ZZ) + sage: S. = PF[] + sage: L. = LazyPowerSeriesRing(ZZ) + sage: s = (q + PF.gen(0)*t) + sage: g = (a, b-a) + sage: s(g) + ((-FESDUMMY_0+1)*a+FESDUMMY_0*b) + sage: s(g).parent() + Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Integer Ring + """ + B = InfinitePolynomialRing(base_ring, names=["FESDUMMY"]) + FractionField_generic.__init__(self, B, + element_class=CoefficientRingElement, + category=QuotientFields()) + + def _repr_(self): + return "CoefficientRing over %s" % self.base_ring() + + def gen(self, i): + return self._element_class(self, self._R.gen()[i]) + + def construction(self): + return (CoefficientRingFunctor(), + self.base_ring()) + class Stream_uninitialized(Stream): r""" @@ -1523,9 +1604,13 @@ def define_implicitly(self, series, initial_values, equations, self._coefficient_ring = coefficient_ring self._base_ring = base_ring - self._P = InfinitePolynomialRing(self._base_ring, names=["FESDUMMY"]) - self._PF = self._P.fraction_field() - if self._coefficient_ring != self._base_ring: + # self._P = InfinitePolynomialRing(self._base_ring, names=["FESDUMMY"]) + # self._PF = CoefficientRing(self._P, CoefficientRing.Element) + self._PF = CoefficientRing(self._base_ring) + self._P = self._PF.base() + if self._coefficient_ring == self._base_ring: + self._U = self._PF + else: self._U = self._coefficient_ring.change_ring(self._PF) self._pool = VariablePool(self._P) self._uncomputed = True @@ -1733,10 +1818,11 @@ def __getitem__(self, n): # down the multiplication enormously if self._coefficient_ring == self._base_ring: x = (self._pool.new_variable(self._name + "[%s]" % n0) - * self._terms_of_degree(n0, self._PF)[0]) + * self._terms_of_degree(n0, self._P)[0]) else: x = sum(self._pool.new_variable(self._name + "[%s]" % m) * m - for m in self._terms_of_degree(n0, self._PF)) + for m in self._terms_of_degree(n0, self._P)) + x = self._U(x) self._cache.append(x) return x @@ -2537,7 +2623,10 @@ def get_coefficient(self, n): sage: [h.get_coefficient(i) for i in range(10)] [0, 2, 6, 12, 20, 30, 42, 56, 72, 90] """ - return self._left[n] + self._right[n] + l = self._left[n] + r = self._right[n] + m = l + r + return m # self._left[n] + self._right[n] class Stream_sub(Stream_binary): @@ -4366,8 +4455,10 @@ def __getitem__(self, n): sage: [f2[i] for i in range(-1, 4)] [0, 2, 6, 12, 20] """ - return (ZZ.prod(range(n + 1, n + self._shift + 1)) - * self._series[n + self._shift]) + c1 = self._series[n + self._shift] + c2 = ZZ.prod(range(n + 1, n + self._shift + 1)) + p = c1 * c2 + return p def __hash__(self): """ diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index c93c71f195e..50bbf3091d1 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4516,7 +4516,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): (-2/3, 0, 2, 2/3*pi) """ if right._parent is self._parent._base: - v = [(x)._mul_(right) for x in self._entries] + v = [x._mul_(right) for x in self._entries] else: v = [x * right for x in self._entries] return self._new_c(v) @@ -4967,7 +4967,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): cdef dict v = {} if right: for i, a in self._entries.iteritems(): - prod = (a)._mul_(right) + prod = a._mul_(right) if prod: v[i] = prod return self._new_c(v) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 2a0fc0a0083..57f74f4092c 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3691,7 +3691,7 @@ def log(self): raise ValueError("can only compose with a positive valuation series") # WARNING: d_self need not be a proper element of P, e.g. for # multivariate power series - d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], + d_self = Stream_function(lambda n: R(n + 1) * coeff_stream[n + 1], P.is_sparse(), 0) coeff_stream_inverse = Stream_cauchy_invert(coeff_stream) # d_self and coeff_stream_inverse always commute @@ -5260,24 +5260,18 @@ def coefficient(n): # The arity is at least 2 gv = min(h._coeff_stream._approximate_order for h in g) - gR = None def coefficient(n): - nonlocal gR r = R.zero() for i in range(n // gv + 1): c = coeff_stream[i] - if c.parent() == self.base_ring(): + B = c.parent() + if B is ZZ or B is QQ or B == self.base_ring() or B == self.base_ring().fraction_field(): c = P(c) r += c[n] - elif c.parent().base_ring() is self.base_ring(): - r += c(g)[n] else: - if gR is None: - S = c.parent().base_ring() - gR = [h.change_ring(S).map_coefficients(S) for h in g] - s = c(gR)[n] - r += s + d = c(g) + r += d[n] return r return P.element_class(P, Stream_function(coefficient, diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index cbb41861301..56360ca571b 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -480,7 +480,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if coefficients is not None and (x is not None and (not isinstance(x, int) or x)): raise ValueError("coefficients must be None if x is provided") - BR = self.base_ring() + BR = self._internal_poly_ring.base_ring() # this is the ring containing the elements of the stream if isinstance(constant, (tuple, list)): constant, degree = constant if isinstance(degree, (tuple, list)): @@ -559,7 +559,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if isinstance(stream, Stream_zero): return self.zero() elif isinstance(stream, Stream_exact): - BR = self.base_ring() if x.parent()._arity != 1: # Special case for constant series if stream._degree == 1: @@ -793,7 +792,7 @@ def define_implicitly(self, series, equations, max_lookahead=1): We need to specify the initial values for the degree 1 and 2 components to get a unique solution in the previous example:: - sage: L. = LazyPowerSeriesRing(QQ["x,y,f1"].fraction_field()) + sage: L. = LazyPowerSeriesRing(QQ['x','y','f1'].fraction_field()) sage: L.base_ring().inject_variables() Defining x, y, f1 sage: F = L.undefined() @@ -2775,7 +2774,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No valuation = 0 R = self._laurent_poly_ring - BR = self.base_ring() + BR = self._internal_poly_ring.base_ring() # this is the ring containing the elements of the stream if x is None: assert degree is None coeff_stream = Stream_uninitialized(valuation) @@ -2825,11 +2824,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if isinstance(x, LazyPowerSeries): stream = x._coeff_stream if isinstance(stream, Stream_exact): - if self._arity == 1: - BR = self.base_ring() - else: - BR = self._laurent_poly_ring coeffs = [BR(val) for val in stream._initial_coefficients] + constant = BR(stream._constant) valuation = stream._approximate_order for i, c in enumerate(coeffs): if c: @@ -2841,7 +2837,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No coeffs = [] return self(coeffs, degree=stream._degree, - constant=self.base_ring()(stream._constant), + constant=constant, valuation=valuation) return self.element_class(self, stream) diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index 99c6266fedc..8f4155fed78 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -946,7 +946,7 @@ def _element_constructor_(self, x): if not hasattr(x, 'variables'): try: return sage_eval(repr(x), self.gens_dict()) - except (TypeError, ValueError, SyntaxError): + except (TypeError, ValueError, SyntaxError, NameError): raise ValueError(f"cannot convert {x} into an element of {self}") # direct conversion will only be used if the underlying polynomials are libsingular. diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index d2355aa257b..6a33f9291d3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -177,9 +177,11 @@ def __call__(self, *x, **kwds): K = x[0].parent() except AttributeError: K = self.parent().base_ring() - y = K(0) + y = K.zero() for m, c in self.element().dict().items(): - y += c * prod(v ** e for v, e in zip(x, m) if e) + p = prod(v ** e for v, e in zip(x, m) if e) + cp = c * p + y += cp return y def _richcmp_(self, right, op): From 20dd68694897b8c32d4c7efad2fc6f61d1a050e3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 3 Jun 2024 10:57:21 +0200 Subject: [PATCH 147/507] do not coerce in InfinitePolynomialRing_sparse._element_constructor_ --- src/sage/rings/polynomial/infinite_polynomial_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index 8f4155fed78..4c5f9a4f51d 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -931,7 +931,7 @@ def _element_constructor_(self, x): if isinstance(self._base, MPolynomialRing_polydict): x = sage_eval(repr(), next(self.gens_dict())) else: - x = self._base.coerce(x) + x = self._base(x) # remark: Conversion to self._P (if applicable) # is done in InfinitePolynomial() return InfinitePolynomial(self, x) From bba061126a5b526f62823c8f3184d8370ed90fb9 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 7 Jun 2024 17:46:11 +0200 Subject: [PATCH 148/507] bad hack to make symmetric functions work --- src/sage/data_structures/stream.py | 9 +++++++++ src/sage/rings/lazy_series_ring.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 9035d2574a7..04a402d3828 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1405,6 +1405,15 @@ def __init__(self, base_ring): ((-FESDUMMY_0+1)*a+FESDUMMY_0*b) sage: s(g).parent() Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Integer Ring + + sage: S = SymmetricFunctions(QQ).h().fraction_field() + sage: PF = CoefficientRing(S) + sage: L. = LazyPowerSeriesRing(S) + sage: c = PF.gen(0) + sage: p = a + c + sage: p.parent() + Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Fraction Field of Symmetric Functions over Rational Field in the homogeneous basis + """ B = InfinitePolynomialRing(base_ring, names=["FESDUMMY"]) FractionField_generic.__init__(self, B, diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 56360ca571b..62ad5edbd1e 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -2783,7 +2783,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No try: # Try to build stuff using the polynomial ring constructor x = R(x) - except (TypeError, ValueError): + except (TypeError, ValueError, AttributeError): pass if isinstance(constant, (tuple, list)): constant, degree = constant @@ -2792,7 +2792,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No raise ValueError("constant must be zero for multivariate Taylor series") constant = BR(constant) - if x in R: + if parent(x) == R: if not x and not constant: coeff_stream = Stream_zero() else: From 187988a4dd20b82bf294a9bc86cc9818879b2056 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 25 Jun 2024 10:50:48 +0200 Subject: [PATCH 149/507] Removed duplicate of `.is_definite()` --- .../algebras/quatalg/quaternion_algebra.py | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index f3394d8b731..3c2e3ee5b8c 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1078,29 +1078,6 @@ def invariants(self): """ return self._a, self._b - def is_definite(self): - """ - Checks whether the quaternion algebra ``self`` is definite, i.e. whether it ramifies at the - unique Archimedean place of its base field QQ. This is the case if and only if both - invariants of ``self`` are negative. - - EXAMPLES:: - - sage: QuaternionAlgebra(QQ,-5,-2).is_definite() - True - sage: QuaternionAlgebra(1).is_definite() - False - - sage: QuaternionAlgebra(RR(2.),1).is_definite() - Traceback (most recent call last): - ... - ValueError: base field must be rational numbers - """ - if not isinstance(self.base_ring(), RationalField): - raise ValueError("base field must be rational numbers") - a, b = self.invariants() - return a < 0 and b < 0 - def __eq__(self, other): """ Compare self and other. @@ -1248,7 +1225,7 @@ def is_definite(self): ... ValueError: base field must be rational numbers """ - if not is_RationalField(self.base_ring()): + if not isinstance(self.base_ring(), RationalField): raise ValueError("base field must be rational numbers") a, b = self.invariants() return a < 0 and b < 0 From e783d8bdf01b5e3b0385fac7d48cb326c72347e1 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 25 Jun 2024 17:13:21 +0200 Subject: [PATCH 150/507] Replace instances of `is_RationalField` with according `isinstance`-calls --- src/sage/algebras/quatalg/quaternion_algebra.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 3c2e3ee5b8c..6be502c63ec 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1263,7 +1263,7 @@ def is_totally_definite(self): ValueError: base field must be rational numbers or a number field """ F = self.base_ring() - if is_RationalField(F): + if isinstance(F, RationalField): return self.is_definite() if F not in NumberFields(): @@ -1382,7 +1382,7 @@ def ramified_places(self, inf=True): # For efficiency (and to not convert QQ into a number field manually), # we handle the case F = QQ first - if is_RationalField(F): + if isinstance(F, RationalField): ram_fin = sorted([p for p in set([2]).union( prime_divisors(a.numerator()), prime_divisors(a.denominator()), prime_divisors(b.numerator()), prime_divisors(b.denominator())) @@ -1510,7 +1510,7 @@ def discriminant(self): ValueError: base field must be rational numbers or a number field """ F = self.base_ring() - if is_RationalField(F): + if isinstance(F, RationalField): return ZZ.prod(self.ramified_places(inf=False)) return F.ideal(F.prod(self.ramified_places(inf=False))) @@ -1555,7 +1555,7 @@ def is_isomorphic(self, A) -> bool: if F is not A.base_ring(): raise ValueError("both quaternion algebras must be defined over the same ring") - if is_RationalField(F): + if isinstance(F, RationalField): return self.ramified_places(inf=False) == A.ramified_places(inf=False) try: From 27bec1f05008abfaaa64d266b8e5c580a46c7ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 11 Jul 2024 09:50:04 +0200 Subject: [PATCH 151/507] disactivate coerce keyword --- src/sage/categories/finite_dimensional_algebras_with_basis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 9acac762396..8a64643552c 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -554,6 +554,7 @@ def principal_ideal(self, a, side='left', *args, **opts): - :meth:`peirce_summand` """ + opts.pop("coerce", None) return self.submodule([(a * b if side == 'right' else b * a) for b in self.basis()], *args, **opts) From 26513ef87407d3a578bb7dd52e7c4c494b8cf001 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 23 Jul 2024 21:22:59 +0200 Subject: [PATCH 152/507] undo wrong and unnecessary changes from before Travis found the real error --- src/sage/data_structures/stream.py | 13 +++---------- src/sage/modules/free_module_element.pyx | 4 ++-- .../rings/polynomial/multi_polynomial_element.py | 6 ++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 903ee532f0c..bf39a5c6d51 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1613,8 +1613,6 @@ def define_implicitly(self, series, initial_values, equations, self._coefficient_ring = coefficient_ring self._base_ring = base_ring - # self._P = InfinitePolynomialRing(self._base_ring, names=["FESDUMMY"]) - # self._PF = CoefficientRing(self._P, CoefficientRing.Element) self._PF = CoefficientRing(self._base_ring) self._P = self._PF.base() if self._coefficient_ring == self._base_ring: @@ -2632,10 +2630,7 @@ def get_coefficient(self, n): sage: [h.get_coefficient(i) for i in range(10)] [0, 2, 6, 12, 20, 30, 42, 56, 72, 90] """ - l = self._left[n] - r = self._right[n] - m = l + r - return m # self._left[n] + self._right[n] + return self._left[n] + self._right[n] class Stream_sub(Stream_binary): @@ -4464,10 +4459,8 @@ def __getitem__(self, n): sage: [f2[i] for i in range(-1, 4)] [0, 2, 6, 12, 20] """ - c1 = self._series[n + self._shift] - c2 = ZZ.prod(range(n + 1, n + self._shift + 1)) - p = c1 * c2 - return p + return (ZZ.prod(range(n + 1, n + self._shift + 1)) + * self._series[n + self._shift]) def __hash__(self): """ diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index e14fb59fc1c..d71d22ac1f7 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4562,7 +4562,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): (-2/3, 0, 2, 2/3*pi) """ if right._parent is self._parent._base: - v = [x._mul_(right) for x in self._entries] + v = [(x)._mul_(right) for x in self._entries] else: v = [x * right for x in self._entries] return self._new_c(v) @@ -5012,7 +5012,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): cdef dict v = {} if right: for i, a in self._entries.iteritems(): - prod = a._mul_(right) + prod = (a)._mul_(right) if prod: v[i] = prod return self._new_c(v) diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 08e04c07daa..91fcd38631b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -177,11 +177,9 @@ def __call__(self, *x, **kwds): K = x[0].parent() except AttributeError: K = self.parent().base_ring() - y = K.zero() + y = K(0) for m, c in self.element().dict().items(): - p = prod(v ** e for v, e in zip(x, m) if e) - cp = c * p - y += cp + y += c * prod(v ** e for v, e in zip(x, m) if e) return y def _richcmp_(self, right, op): From 49f28fd2ac2a13218d29f7172177693eae604c58 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 24 Jul 2024 11:35:04 +0200 Subject: [PATCH 153/507] provide docstrings and doctests --- src/sage/data_structures/stream.py | 95 +++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index bf39a5c6d51..7059cc35523 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -95,14 +95,18 @@ # **************************************************************************** from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ from sage.rings.infinity import infinity from sage.arith.misc import divisors from sage.misc.misc_c import prod from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import lazy_import from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter +from sage.categories.fields import Fields +from sage.categories.functor import Functor +from sage.categories.integral_domains import IntegralDomains from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis +from sage.categories.pushout import ConstructionFunctor +from sage.categories.quotient_fields import QuotientFields from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial @@ -1343,25 +1347,49 @@ def variables(self): """ return self._pool -from sage.categories.functor import Functor -from sage.categories.pushout import ConstructionFunctor -from sage.categories.fields import Fields -from sage.categories.integral_domains import IntegralDomains -from sage.categories.quotient_fields import QuotientFields class CoefficientRingFunctor(ConstructionFunctor): + r""" + A construction functor for the :class:`CoefficientRing`. + + The functor maps the integral domain of coefficients to the field + of unknown coefficients. + """ rank = 0 def __init__(self): + r""" + Initialize the functor. + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRingFunctor + sage: CoefficientRingFunctor() + CoefficientRingFunctor + """ Functor.__init__(self, IntegralDomains(), Fields()) def _apply_functor(self, R): + r""" + Apply the functor to an integral domain. + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRingFunctor + sage: CoefficientRingFunctor()(ZZ) # indirect doctest + CoefficientRing over Integer Ring + """ return CoefficientRing(R) + class CoefficientRingElement(FractionFieldElement): pass + class CoefficientRing(UniqueRepresentation, FractionField_generic): + r""" + The class of unknown coefficients in a stream. + """ def __init__(self, base_ring): """ @@ -1412,8 +1440,10 @@ def __init__(self, base_ring): sage: c = PF.gen(0) sage: p = a + c sage: p.parent() - Multivariate Lazy Taylor Series Ring in a, b over CoefficientRing over Fraction Field of Symmetric Functions over Rational Field in the homogeneous basis - + Multivariate Lazy Taylor Series Ring in a, b + over CoefficientRing + over Fraction Field of Symmetric Functions + over Rational Field in the homogeneous basis """ B = InfinitePolynomialRing(base_ring, names=["FESDUMMY"]) FractionField_generic.__init__(self, B, @@ -1421,14 +1451,51 @@ def __init__(self, base_ring): category=QuotientFields()) def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRing + sage: CoefficientRing(ZZ["q"]) + CoefficientRing over Univariate Polynomial Ring in q over Integer Ring + """ return "CoefficientRing over %s" % self.base_ring() def gen(self, i): + r""" + Return the ``n``-th generator of ``self``. + + The name of the generator is not to be relied on. + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRing + sage: PF = CoefficientRing(ZZ["q"]) + sage: PF.gen(0) + FESDUMMY_0 + """ return self._element_class(self, self._R.gen()[i]) def construction(self): - return (CoefficientRingFunctor(), - self.base_ring()) + r""" + Return a pair ``(F, R)``, where ``F`` is a + :class:`CoefficientRingFunctor` and `R` is an integral + domain, such that ``F(R)`` returns ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.stream import CoefficientRing + sage: PF = CoefficientRing(ZZ["q"]) + sage: F, R = PF.construction() + sage: F, R + (CoefficientRingFunctor, + Univariate Polynomial Ring in q over Integer Ring) + + sage: F(R) + CoefficientRing over Univariate Polynomial Ring in q over Integer Ring + """ + return (CoefficientRingFunctor(), self.base_ring()) class Stream_uninitialized(Stream): @@ -2082,7 +2149,7 @@ def _compute(self): raise ValueError("could not determine any coefficients:\n" + eqs_str) raise ValueError("there are no linear equations:\n" - + eqs_str) + + eqs_str) def _eq_str(self, idx, eq): """ @@ -2959,9 +3026,9 @@ def get_coefficient(self, n): sage: f = Stream_function(lambda n: n, True, 1) sage: g = Stream_function(lambda n: n^2, True, 1) sage: h = Stream_cauchy_compose(f, g, True) - sage: h[5] # indirect doctest + sage: h[5] # indirect doctest 527 - sage: [h[i] for i in range(10)] # indirect doctest + sage: [h[i] for i in range(10)] # indirect doctest [0, 1, 6, 28, 124, 527, 2172, 8755, 34704, 135772] """ fv = self._left._approximate_order @@ -3382,7 +3449,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_rmul sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) - sage: h = Stream_rmul(f, 3, True) # indirect doctest + sage: h = Stream_rmul(f, 3, True) # indirect doctest sage: h._approximate_order 2 sage: [h[i] for i in range(5)] From 32a8f35753f0678ce2b3397ea73c3875a24a9122 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 25 Jul 2024 14:52:46 +0200 Subject: [PATCH 154/507] (hacky) fix division for symmetric functions --- src/sage/combinat/sf/sfa.py | 43 ++++++++++++++++++++++++++++++ src/sage/data_structures/stream.py | 6 +---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 9292fd709e7..e592a2b11ab 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -418,6 +418,15 @@ def is_integral_domain(self, proof=True): return self.base_ring().is_integral_domain() def fraction_field(self): + r""" + Return the fraction field of ``self``. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: s.fraction_field() + Fraction Field of Symmetric Functions over Rational Field in the Schur basis + """ if not self.is_integral_domain(): raise TypeError("self must be an integral domain.") if hasattr(self, "__fraction_field") and self.__fraction_field is not None: @@ -3099,6 +3108,40 @@ class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): m[1, 1, 1] + m[2, 1] + m[3] sage: m.set_print_style('lex') """ + def __truediv__(self, x): + r""" + Return the quotient of ``self`` by ``other``. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: s[1]/(1+s[1]) + s[1]/(s[] + s[1]) + + sage: s[1]/2 + 1/2*s[1] + + TESTS:: + + sage: (s[1]/2).parent() + Symmetric Functions over Rational Field in the Schur basis + """ + from sage.categories.modules import _Fields + B = self.base_ring() + try: + bx = B(x) + except TypeError: + f = self.parent().fraction_field() + return f(self, x) + F = self.parent() + D = self._monomial_coefficients + + if B not in _Fields: + return type(self)(F, {k: c._divide_if_possible(x) + for k, c in D.items()}) + + return ~bx * self + def factor(self): """ Return the factorization of this symmetric function. diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 7059cc35523..f001abe8fe5 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1382,10 +1382,6 @@ def _apply_functor(self, R): return CoefficientRing(R) -class CoefficientRingElement(FractionFieldElement): - pass - - class CoefficientRing(UniqueRepresentation, FractionField_generic): r""" The class of unknown coefficients in a stream. @@ -1447,7 +1443,7 @@ def __init__(self, base_ring): """ B = InfinitePolynomialRing(base_ring, names=["FESDUMMY"]) FractionField_generic.__init__(self, B, - element_class=CoefficientRingElement, + element_class=FractionFieldElement, category=QuotientFields()) def _repr_(self): From 3eb493515c9fcf6d35bc76bd1fc54b209aa369f2 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 12 Sep 2024 10:58:49 +0200 Subject: [PATCH 155/507] rewrite some methods, add documentation --- src/sage/modules/free_module.py | 122 ++++- .../modules/free_module_pseudohomspace.py | 371 +++++++------ .../modules/free_module_pseudomorphism.py | 490 ++++++++++++------ 3 files changed, 648 insertions(+), 335 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index df629169b2a..a78ca3cd3e7 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3112,40 +3112,122 @@ def hom(self, im_gens, codomain=None, **kwds): codomain = R**n return super().hom(im_gens, codomain, **kwds) - def PseudoHom(self, twist=None, codomain=None): + def PseudoHom(self, twist, codomain=None): r""" - Create the Pseudo Hom space corresponding to given twist data. + Return the Pseudo Hom space corresponding to given data. + + INPUT: + + - ``twist`` -- the twisting morphism or the twisting derivation + + - ``codomain`` (default: ``None``) -- the codomain of the pseudo + morphisms; if ``None``, the codomain is the same than the domain EXAMPLES:: - sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist); PHS - Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z2 of size 5^2 to Vector space of dimension 2 over Finite Field in z2 of size 5^2 - Twisted by the morphism Frobenius endomorphism z2 |--> z2^5 on Finite Field in z2 of size 5^2 + sage: F = GF(25) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 + sage: M.PseudoHom(Frob) + Set of Pseudoendomorphisms (twisted by z2 |--> z2^5) of Vector space of dimension 2 over Finite Field in z2 of size 5^2 + .. SEEALSO:: + + :meth:`pseudohom` """ from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace - return FreeModulePseudoHomspace(self, codomain=codomain, twist=twist) + if codomain is None: + codomain = self + return FreeModulePseudoHomspace(self, codomain, twist) - def pseudohom(self, morphism, twist=None, codomain=None, **kwds): + def pseudohom(self, f, twist, codomain=None, side="left"): r""" - Create a pseudomorphism defined by a given morphism and twist. - Let A be a ring and M a free module over A. Let \theta: A \to A + Return the pseudohomomorphism corresponding to the given data. + + We recall that, given two `R`-modules `M` and `M'` together with + a ring homomorphism `\theta: R \to R` and a `\theta`-derivation, + `\delta: R \to R` (that is, a map such that: + `\delta(xy) = \theta(x)\delta(y) + \delta(x)y`), a + pseudomorphism `f : M \to M'` is a additive map such that + + .. MATH: + + f(\lambda x) = \theta(\lambda) f(x) + \delta(\lambda) x + + When `\delta` is nonzero, this requires that `M` coerces into `M'`. + + INPUT: + + - ``f`` -- a matrix (or any other data) defining the + pseudomorphism + + - ``twist`` -- the twisting morphism or the twisting derivation + + - ``codomain`` (default: ``None``) -- the codomain of the pseudo + morphisms; if ``None``, the codomain is the same than the domain + + - ``side`` (default: ``left``) -- side of the vectors acted on by + the matrix EXAMPLES:: - sage: F = GF(25); M = F^2; twist = F.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2], [0, 1]], twist, side="right"); ph - Free module pseudomorphism defined as left-multiplication by the matrix - [1 2] - [0 1] - twisted by the morphism Frobenius endomorphism z2 |--> z2^5 on Finite Field in z2 of size 5^2 - Domain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 - Codomain: Vector space of dimension 2 over Finite Field in z2 of size 5^2 + sage: K. = GF(5^5) + sage: Frob = K.frobenius_endomorphism() + sage: V = K^2; W = K^3 + sage: f = V.pseudohom([[1, z, z^2], [1, z^2, z^4]], Frob, codomain=W) + sage: f + Free module pseudomorphism (twisted by z |--> z^5) defined by the matrix + [ 1 z z^2] + [ 1 z^2 z^4] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^5 + Codomain: Vector space of dimension 3 over Finite Field in z of size 5^5 + + We check that `f` is indeed semi-linear:: + + sage: v = V([z+1, z+2]) + sage: f(z*v) + (2*z^2 + z + 4, z^4 + 2*z^3 + 3*z^2 + z, 4*z^4 + 2*z^2 + 3*z + 2) + sage: z^5 * f(v) + (2*z^2 + z + 4, z^4 + 2*z^3 + 3*z^2 + z, 4*z^4 + 2*z^2 + 3*z + 2) + + An example with a derivation:: + + sage: R. = ZZ[] + sage: d = R.derivation() + sage: M = R^2 + sage: Nabla = M.pseudohom([[1, t], [t^2, t^3]], d, side='right') + sage: Nabla + Free module pseudomorphism (twisted by d/dt) defined as left-multiplication by the matrix + [ 1 t] + [t^2 t^3] + Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in t over Integer Ring + Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in t over Integer Ring + + sage: v = M([1,1]) + sage: Nabla(v) + (t + 1, t^3 + t^2) + sage: Nabla(t*v) + (t^2 + t + 1, t^4 + t^3 + 1) + sage: Nabla(t*v) == t * Nabla(v) + v + True + + If the twisting derivation is not zero, the domain must + coerce into the codomain + + sage: N = R^3 + sage: M.pseudohom([[1, t, t^2], [1, t^2, t^4]], d, codomain=N) + Traceback (most recent call last): + ... + ValueError: the domain does not coerce into the codomain + + .. SEEALSO:: + + :meth:`PseudoHom` """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism - side = kwds.get("side", "left") - return FreeModulePseudoMorphism(self.PseudoHom(twist=twist, codomain=codomain), morphism, side) + parent = self.PseudoHom(twist, codomain) + return FreeModulePseudoMorphism(parent, f, side) + def inner_product_matrix(self): """ diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 65af841f37b..93377f58183 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -3,11 +3,12 @@ AUTHORS: - - Yossef Musleh (2024-02): initial version + - Xavier Caruso, Yossef Musleh (2024-09): initial version """ # **************************************************************************** -# Copyright (C) 2024 Yossef Musleh +# Copyright (C) 2024 Xavier Caruso +# Yossef Musleh # # Distributed under the terms of the GNU General Public License (GPL) # @@ -20,16 +21,19 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -import sage.categories.homset -from sage.structure.element import is_Matrix -from sage.matrix.constructor import matrix, identity_matrix + +from sage.structure.unique_representation import UniqueRepresentation + +from sage.categories.homset import HomsetWithBase from sage.matrix.matrix_space import MatrixSpace -from sage.categories.morphism import Morphism -from sage.misc.lazy_import import lazy_import +from sage.structure.sequence import Sequence +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism + -lazy_import('sage.rings.derivation', 'RingDerivation') +class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): + Element = FreeModulePseudoMorphism -class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): r""" This class implements the space of Pseudomorphisms with a fixed twist. @@ -38,219 +42,256 @@ class FreeModulePseudoHomspace(sage.categories.homset.HomsetWithBase): TESTS:: - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) + sage: F = GF(125) + sage: M = F^2 + sage: Frob = F.frobenius_endomorphism() + sage: PHS = M.PseudoHom(Frob) sage: h = PHS([[1, 2], [1, 1]]) sage: e = M((4*F.gen()^2 + F.gen() + 2, 4*F.gen()^2 + 4*F.gen() + 4)) sage: h(e) (z3, 2*z3^2 + 3*z3 + 3) """ - def __init__(self, domain, codomain=None, twist=None): + @staticmethod + def __classcall_private__(cls, domain, codomain, twist): r""" Constructs the space of pseudomorphisms with a given twist. INPUT: - - ``domain`` - the domain of the pseudomorphism; a free module - - ``codomain`` - the codomain of the pseudomorphism; a free - module (default: None) + - ``domain`` -- a free module, the domain of this pseudomorphism - - ``twist`` - a twisting morphism, this is either a morphism or - a derivation (default: None) + - ``codomain`` -- a free module, the codomain of this pseudomorphism - EXAMPLES:: + - ``twist`` -- a twisting morphism or a twisting derivation + + TESTS:: + + sage: F = GF(125) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 + sage: H = M.PseudoHom(Frob) + sage: type(H) + + + sage: # Testsuite(H).run() + + """ + ring = domain.base_ring() + if codomain.base_ring() is not ring: + raise ValueError("the domain and the codomain must be defined over the same ring") + ore = OrePolynomialRing(ring, twist, names='x') + if isinstance(ore, OrePolynomialRing) and ore._derivation is not None: + if not codomain.has_coerce_map_from(domain): + raise ValueError("the domain does not coerce into the codomain") + return cls.__classcall__(cls, domain, codomain, ore) + + def __init__(self, domain, codomain, ore): + r""" + Initialize this pseudohom space. + + INPUT: + + - ``domain`` -- a free module, the domain of this pseudomorphism + + - ``codomain`` -- a free module, the codomain of this pseudomorphism + + - ``ore`` -- the underlying Ore polynomial ring + + TESTS:: + + sage: F = GF(125) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 + sage: M.PseudoHom(Frob) + Set of Pseudoendomorphisms (twisted by z3 |--> z3^5) of Vector space of dimension 2 over Finite Field in z3 of size 5^3 - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist); PHS - Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z3 of size 5^3 to Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 """ self._domain = domain - self._codomain = domain - if codomain is not None: - self._codomain = codomain - super().__init__(self._domain, self._codomain, category=None) - self.base_homspace = self._domain.Hom(self._codomain) - self.twist = twist - self.twist_morphism = None - self.derivation = None - if twist is None: - return - if (twist.domain() is not self.domain().coordinate_ring() - or twist.codomain() is not self.codomain().coordinate_ring()): - raise TypeError("twisting morphism domain/codomain do not match\ - coordinate rings of the modules") - elif isinstance(twist, Morphism): - self.twist_morphism = twist - elif isinstance(twist, RingDerivation): - self.derivation = twist + self._codomain = codomain + super().__init__(domain, codomain, category=None) + self._ore = ore + if isinstance(ore, OrePolynomialRing): + self._morphism = ore._morphism + self._derivation = ore._derivation else: - raise TypeError("twist is not a ring morphism or derivation") + self._morphism = self._derivation = None + ring = ore.base_ring() + self._matrix_space = MatrixSpace(ring, domain.dimension(), codomain.dimension()) - def __call__(self, A, **kwds): + def _element_constructor_(self, f, side="left"): r""" - Coerce a matrix or free module homomorphism into a pseudomorphism. + Return the element of this parent constructed from the + given data. - INPUTS: - - ``A`` - either a matrix defining the morphism or a free module - morphism + TESTS:: - EXAMPLES:: + sage: F. = GF(5^3) + sage: Frob = F.frobenius_endomorphism() + sage: V = F^2 + sage: H = V.PseudoHom(Frob) + + sage: H([[1, z], [z, z^2]]) + Free module pseudomorphism (twisted by z |--> z^5) defined by the matrix + [ 1 z] + [ z z^2] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) - sage: h = PHS([[1, 2], [1, 1]]); h - Free module pseudomorphism defined by the matrix - [1 2] - [1 1] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - return self._element_constructor_(A, **kwds) + return self.element_class(self, f, side) - def __repr__(self): + def __reduce__(self): r""" - Returns the string representation of the pseudomorphism space. - - EXAMPLES:: + TESTS:: - sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() - sage: PHS = M.PseudoHom(frob); PHS - Set of Pseudomorphisms from Vector space of dimension 2 over Finite Field in z3 of size 7^3 to Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 + sage: F = GF(125) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 + sage: H = M.PseudoHom(Frob) + sage: loads(dumps(M)) is M + True """ - r = "Set of Pseudomorphisms from {} to {} {} {}" - morph = "" - if self.twist_morphism is not None: - morph = "\nTwisted by the morphism {}" - morph = morph.format(self.twist_morphism.__repr__()) - deriv = "" - if self.derivation is not None: - deriv = "\nTwisted by the derivation {}" - deriv = deriv.format(self.derivation.__repr__()) - return r.format(self.domain(), self.codomain(), morph, deriv) - - def _element_constructor_(self, A, **kwds): + if self._derivation is None: + twist = self._morphism + else: + twist = self._derivation + return FreeModulePseudoHomspace, (self.domain(), self.codomain(), twist) + + def _repr_twist(self): r""" - Coerce a matrix or free module homomorphism into a pseudomorphism. + Return a string representation of the twisting morphisms. - INPUTS: - - ``A`` - either a matrix defining the morphism or a free module - morphism + This is a helper method. TESTS:: - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) - sage: h = PHS._element_constructor_([[1, 2], [1, 1]]); h - Free module pseudomorphism defined by the matrix - [1 2] - [1 1] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + sage: F. = GF(5^3) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 - :: + sage: M.PseudoHom(Frob)._repr_twist() + 'twisted by z |--> z^5' + + sage: M.PseudoHom(Frob^3)._repr_twist() + 'untwisted' - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) - sage: morph = M.hom(matrix([[1, 2], [1, 1]])) - sage: phi = PHS._element_constructor_(morph, side="right"); phi - Free module pseudomorphism defined as left-multiplication by the matrix - [1 2] - [1 1] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ - from . import free_module_pseudomorphism as pseudo - side = kwds.get("side", "left") - if not self.codomain().base_ring().has_coerce_map_from( - self.domain().base_ring()) and not A.is_zero(): - raise TypeError("nontrivial morphisms require a coercion map" - "from the base ring of the domain to the base ring of the" - "codomain") - return pseudo.FreeModulePseudoMorphism(self, A, side=side) - - def _matrix_space(self): + s = "" + if self._morphism is not None: + s += self._morphism._repr_short() + if self._derivation is not None: + if s != "": + s += " and " + s += self._derivation._repr_() + if s == "": + return "untwisted" + else: + return "twisted by " + s + + def _repr_(self): r""" - Return the full matrix space of the underlying morphisms. + Returns a string representation of this pseudomorphism space. EXAMPLES:: - sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() - sage: PHS = M.PseudoHom(frob) - sage: PHS._matrix_space() - Full MatrixSpace of 2 by 2 dense matrices over Finite Field in z3 of size 7^3 + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: V.PseudoHom(Frob) # indirect doctest + Set of Pseudoendomorphisms (twisted by z3 |--> z3^7) of Vector space of dimension 2 over Finite Field in z3 of size 7^3 + + :: + + sage: V.PseudoHom(Frob, codomain=Fq^3) # indirect doctest + Set of Pseudomorphism (twisted by z3 |--> z3^7) from Vector space of dimension 2 over Finite Field in z3 of size 7^3 to Vector space of dimension 3 over Finite Field in z3 of size 7^3 + + :: + + sage: A. = QQ[] + sage: d = A.derivation() + sage: M = A^3 + sage: M.PseudoHom(d) + Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in t over Rational Field + """ - return self.base_homspace._matrix_space() + twist = self._repr_twist() + if self.domain() is self.codomain(): + return "Set of Pseudoendomorphisms (%s) of %s" % (twist, self.domain()) + else: + return "Set of Pseudomorphism (%s) from %s to %s" % (twist, self.domain(), self.codomain()) - def basis(self, side="left"): + def ore_ring(self, var='x'): r""" - Return a basis for the underlying matrix space. + Return the underlying Ore polynomial ring. + + INPUT: + + - ``var`` (default: ``x``) -- a string, the name of + tha variable EXAMPLES:: - sage: Fq = GF(343); M = Fq^2; frob = Fq.frobenius_endomorphism() - sage: PHS = M.PseudoHom(frob) - sage: PHS.basis() - (Vector space morphism represented by the matrix: - [1 0] - [0 0] - Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, - Vector space morphism represented by the matrix: - [0 1] - [0 0] - Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, - Vector space morphism represented by the matrix: - [0 0] - [1 0] - Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, - Vector space morphism represented by the matrix: - [0 0] - [0 1] - Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3) + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: H = V.PseudoHom(Frob) + + sage: H.ore_ring() + Ore Polynomial Ring in x over Finite Field in z of size 7^3 twisted by z |--> z^7 + + sage: H.ore_ring('y') + Ore Polynomial Ring in y over Finite Field in z of size 7^3 twisted by z |--> z^7 + """ - return self.base_homspace.basis(side) + return self._ore.change_var(var) - def identity(self): + def matrix_space(self): r""" - Return the pseudomorphism corresponding to the identity transformation + Return the matrix space used for representing the + pseudomorphism in this space. EXAMPLES:: - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) - sage: PHS.identity() - Free module pseudomorphism defined by the matrix - [1 0] - [0 1] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: W = Fq^3 + sage: H = V.PseudoHom(Frob, codomain=W) + sage: H.matrix_space() + Full MatrixSpace of 2 by 3 dense matrices over Finite Field in z of size 7^3 + """ - return self(self.base_homspace.identity()) + return self._matrix_space - def zero(self): + def basis(self, side="left"): r""" - Return the zero pseudomorphism. This corresponds to the zero matrix. + Return a basis for the underlying matrix space. EXAMPLES:: - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(twist) - sage: PHS.zero() - Free module pseudomorphism defined by the matrix + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: PHS = V.PseudoHom(Frob) + sage: PHS.basis() + [Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix + [1 0] + [0 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix + [0 1] + [0 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [0 0] + [1 0] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [0 0] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + [0 1] + Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3] + """ - return self(self.base_homspace.zero()) + return Sequence(self(mat) for mat in self._matrix_space.basis()) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 0051aaf9528..e32a6f1bb8b 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -3,11 +3,12 @@ AUTHORS: - - Yossef Musleh (2024-02): initial version + - Xavier Caruso, Yossef Musleh (2024-09): initial version """ -#################################################################################### -# Copyright (C) 2024 Yossef Musleh +# **************************************************************************** +# Copyright (C) 2024 Xavier Caruso +# Yossef Musleh # # Distributed under the terms of the GNU General Public License (GPL) # @@ -21,243 +22,432 @@ # http://www.gnu.org/licenses/ #################################################################################### -import sage.modules.free_module as free_module from sage.categories.morphism import Morphism -from sage.modules import free_module_homspace, matrix_morphism from sage.structure.richcmp import rich_to_bool, richcmp -from sage.structure.sequence import Sequence -from sage.structure.all import parent -from sage.misc.lazy_import import lazy_import from sage.modules.free_module_morphism import FreeModuleMorphism -from sage.modules.free_module_homspace import FreeModuleHomspace, is_FreeModuleHomspace -from sage.matrix.constructor import matrix -from sage.matrix.matrix_space import MatrixSpace -lazy_import('sage.rings.derivation', 'RingDerivation') class FreeModulePseudoMorphism(Morphism): r""" - Let `M, M'` be free modules over a ring `R`, `\theta: R \to R` a ring - homomorphism, and `\delta: R \to R` a `\theta`-derivation, which is a map - such that: + Let `M, M'` be modules over a ring `R`, `\theta: R \to R` a + ring homomorphism, and `\delta: R \to R` a `\theta`-derivation, + which is a map such that: - `\delta(xy) = \theta(x)\delta(y) + \delta(x)y`. + .. MATH: - Then a pseudomorphism `f : M to M` is a map such that + \delta(xy) = \theta(x)\delta(y) + \delta(x)y. - `f(x + y) = f(x) + f(y)` - `f(\lambda x) = `\theta(\lambda)f(x) + \delta(\lambda)x` + A pseudomorphism `f : M \to M` is an additive map such that - The pair `(\theta, \delta)` may be referred to as the *twist* of - the morphism. + .. MATH: - TESTS:: + f(\lambda x) = \theta(\lambda)f(x) + \delta(\lambda) x - sage: V = ZZ^2 - sage: f = V.pseudohom([V.1, -2*V.0]); f - Free module pseudomorphism defined by the matrix - [ 0 1] - [-2 0] - Domain: Ambient free module of rank 2 over the principal ideal domain Integer Ring - Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring - sage: f(V((1, 2))) - (-4, 1) + The map `\theta` (resp. `\delta`) is referred to as the + twisting endomorphism (resp. the twisting derivation) of `f`. - :: + TESTS:: - sage: P. = ZZ[]; deriv = P.derivation() + sage: P. = ZZ[] + sage: d = P.derivation() sage: M = P^2 - sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right"); f - Free module pseudomorphism defined as left-multiplication by the matrix + sage: f = M.pseudohom([[1, 2*x], [x, 1]], d); f + Free module pseudomorphism (twisted by d/dx) defined by the matrix [ 1 2*x] [ x 1] - twisted by the derivation d/dx Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring sage: e = M((2*x^2 + 3*x + 1, x^3 + 7*x + 4)) sage: f(e) - (2*x^4 + 16*x^2 + 15*x + 4, 3*x^3 + 6*x^2 + 8*x + 11) - sage: f = M.pseudohom([[1, 2], [1, 1]], deriv) + (x^4 + 9*x^2 + 11*x + 4, 5*x^3 + 9*x^2 + 9*x + 11) + sage: f = M.pseudohom([[1, 2], [1, 1]], d) sage: f(e) (x^3 + 2*x^2 + 14*x + 8, x^3 + 7*x^2 + 13*x + 13) :: - sage: Fq = GF(343); M = Fq^3; N = Fq^2; frob = Fq.frobenius_endomorphism(); z = Fq.gen() - sage: phi = M.pseudohom([[2, 3, 1], [1, 4, 6]], frob, N, side="right"); phi - Free module pseudomorphism defined as left-multiplication by the matrix + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: M = Fq^3 + sage: N = Fq^2 + sage: phi = M.pseudohom([[2, 3, 1], [1, 4, 6]], Frob, codomain=N, side="right") + sage: phi + Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix [2 3 1] [1 4 6] - twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 - Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - sage: elem = (4*z^2 + 4*z + 3, 2, z + 5) - sage: phi(elem) - (2*z3 + 1, 6*z3^2 + 4*z3 + 5) + Domain: Vector space of dimension 3 over Finite Field in z of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3 + sage: v = (4*z^2 + 4*z + 3, 2, z + 5) + sage: phi(v) + (2*z + 1, 6*z^2 + 4*z + 5) + """ - def __init__(self, pseudohomspace, base_morphism, side="left"): + def __init__(self, parent, f, side): """ Constructs a pseudomorphism of free modules. INPUT: - - ``pseudohomspace`` - the parent space of pseudomorphisms, - containing - - ``base_morphism`` - either a morphism or a matrix defining a - morphism + - ``parent`` -- the parent space of pseudomorphisms - - side -- side of the vectors acted on by the matrix - (default: ``"left"``) + - ``f`` -- a pseudomorphism or a matrix defining this + pseudomorphism - EXAMPLES:: + - ``side`` -- side of the vectors acted on by the matrix + + TESTS:: - sage: F = GF(25); M = F^3; twist = F.frobenius_endomorphism(5) - sage: phi = M.pseudohom(matrix(F,3,[1..9]), twist) - sage: type(phi) - + sage: F. = GF(5^3) + sage: Frob = F.frobenius_endomorphism() + sage: M = F^2 + sage: H = M.PseudoHom(Frob) + sage: H + Set of Pseudoendomorphisms (twisted by z |--> z^5) of Vector space of dimension 2 over Finite Field in z of size 5^3 + + The attribute ``f`` can be a matrix:: + + sage: mat = matrix(F, 2, [1, z, z^2, z^3]) + sage: f = H(mat) + sage: f + Free module pseudomorphism (twisted by z |--> z^5) defined by the matrix + [ 1 z] + [ z^2 2*z + 2] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 + + sage: type(f) + + + or a pseudomorphism with the same parent:: + + sage: H(f) + Free module pseudomorphism (twisted by z |--> z^5) defined by the matrix + [ 1 z] + [ z^2 2*z + 2] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 + + When the twisting morphism and the twisting derivation are both trivial, + pseudomorphisms are just linear applications and coercion between those + works:: + + sage: id = End(F).identity() + sage: g = M.hom(mat) + sage: M.PseudoHom(id)(g) + Free module pseudomorphism (untwisted) defined by the matrix + [ 1 z] + [ z^2 2*z + 2] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 + + An example with ``side=right``: + + sage: M.pseudohom(mat, Frob, side="right") + Free module pseudomorphism (twisted by z |--> z^5) defined as left-multiplication by the matrix + [ 1 z] + [ z^2 2*z + 2] + Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 :: - sage: F = GF(125); M = F^2; twist = F.frobenius_endomorphism() - sage: morph = M.hom(matrix([[1,2],[0,1]])) - sage: phi = M.pseudohom(morph, twist, side="right"); phi - Free module pseudomorphism defined as left-multiplication by the matrix - [1 2] - [0 1] - twisted by the morphism Frobenius endomorphism z3 |--> z3^5 on Finite Field in z3 of size 5^3 - Domain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 5^3 + sage: M.pseudohom(mat, Frob, side="middle") + Traceback (most recent call last): + ... + ValueError: the side must be either 'left' or 'right' + """ - Morphism.__init__(self, pseudohomspace) - dom = pseudohomspace.domain() - codom = pseudohomspace.codomain() - rows = dom.dimension() - cols = codom.dimension() - if side == "right": - rows = codom.dimension() - cols = dom.dimension() - matrix_space = MatrixSpace(dom.coordinate_ring(), rows, cols) - if isinstance(base_morphism, FreeModuleMorphism): - self._base_matrix = matrix_space(base_morphism.matrix()) + Morphism.__init__(self, parent) + dom = parent.domain() + codom = parent.codomain() + if side != "left" and side != "right": + raise ValueError("the side must be either 'left' or 'right'") + matrix_space = parent.matrix_space() + if ((isinstance(f, FreeModulePseudoMorphism) and f.parent() is parent) + or (isinstance(f, FreeModuleMorphism) + and f.domain() is dom and f.codomain() is codom + and parent._morphism is None and parent._derivation is None)): + if f.side() == 'right': + self._matrix = f.matrix().transpose() + else: + self._matrix = f.matrix() else: - self._base_matrix = matrix_space(base_morphism) - self.derivation = pseudohomspace.derivation - self.twist_morphism = pseudohomspace.twist_morphism - self.side = side + if side == "right": + self._matrix = matrix_space.transposed(f).transpose() + else: + self._matrix = matrix_space(f) + self._morphism = parent._morphism + self._derivation = parent._derivation + self._side = side def _call_(self, x): r""" - Return the result of applying a pseudomorphism to an element of the - free module. + Return the result of applying this pseudomorphism to ``x``. TESTS:: - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, Fq.gen(), 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) - sage: ph(e) - (z3^2 + 6*z3 + 2, z3^2 + 2*z3 + 1, 2*z3^2 + 4*z3) + sage: Fq. = GF(7^3) + sage: M = Fq^3 + sage: Frob = Fq.frobenius_endomorphism() + sage: f = M.pseudohom([[1, z, 3], [0, 1, 1], [2, 1, 1]], Frob) + sage: e = M((3*z^2 + 5*z + 2, 6*z^2 + 2*z + 2, z + 4)) + sage: f(e) + (3*z^2 + 4*z + 4, 6*z^2 + 5*z + 6, 6*z^2 + 5*z + 3) + + :: + + sage: g = M.pseudohom([[1, z, 3], [0, 1, 1], [2, 1, 1]], Frob, side="right") + sage: g(e) + (z^2 + 6*z + 2, z^2 + 2*z + 1, 2*z^2 + 4*z) + """ - if self.domain().is_ambient(): + D = self.domain() + C = self.codomain() + if D.is_ambient(): x = x.element() else: - x = self.domain().coordinate_vector(x) - C = self.codomain() - if self.twist_morphism is None: + x = D.coordinate_vector(x) + if self._morphism is None: x_twist = x else: - x_twist = self.domain()(list(map(self.twist_morphism, x))) - if self.side == "left": - v = x_twist * self._base_matrix - else: - v = self._base_matrix * x_twist - if self.derivation is not None: - v += self.domain()(list(map(self.derivation, x))) + x_twist = D(list(map(self._morphism, x))) + v = x_twist * self._matrix + if self._derivation is not None: + v += D(list(map(self._derivation, x))) if not C.is_ambient(): v = C.linear_combination_of_basis(v) return C._element_constructor_(v) - def __repr__(self): + def _repr_(self): r""" Return the string representation of a pseudomorphism. TESTS:: - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1,1,1],[2,2,2],[3,3,3]], frob); ph - Free module pseudomorphism defined by the matrix + sage: Fq. = GF(7^3) + sage: M = Fq^3 + sage: Frob = Fq.frobenius_endomorphism() + + sage: f = M.pseudohom([[1,1,1], [2,2,2], [3,3,3]], Frob) + sage: f # indirect doctest + Free module pseudomorphism (twisted by z |--> z^7) defined by the matrix [1 1 1] [2 2 2] [3 3 3] - twisted by the morphism Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 - Domain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 3 over Finite Field in z3 of size 7^3 + Domain: Vector space of dimension 3 over Finite Field in z of size 7^3 + Codomain: Vector space of dimension 3 over Finite Field in z of size 7^3 + + sage: g = M.pseudohom([[1,1,1], [2,2,2], [3,3,3]], Frob, side="right") + sage: g # indirect doctest + Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix + [1 1 1] + [2 2 2] + [3 3 3] + Domain: Vector space of dimension 3 over Finite Field in z of size 7^3 + Codomain: Vector space of dimension 3 over Finite Field in z of size 7^3 """ - r = "Free module pseudomorphism defined {}by the "\ - "matrix\n{!r}{}{}\nDomain: {}\nCodomain: {}" - act = "" - if self.side == "right": - act = "as left-multiplication " - morph = "" - if self.twist_morphism is not None: - morph = "\ntwisted by the morphism {}" - morph = morph.format(self.twist_morphism.__repr__()) - deriv = "" - if self.derivation is not None: - deriv = "\ntwisted by the derivation {}" - deriv = deriv.format(self.derivation.__repr__()) - return r.format(act, self.matrix(), morph, deriv, - self.domain(), self.codomain()) + twist = self.parent()._repr_twist() + s = "Free module pseudomorphism (%s) defined " % twist + if self._side == "right": + s += "as left-multiplication " + s += "by the matrix\n%s\n" % self.matrix() + s += "Domain: %s\n" % self.domain() + s += "Codomain: %s" % self.codomain() + return s def matrix(self): r""" - Return the underlying matrix of a pseudomorphism. + Return the underlying matrix of this pseudomorphism. - If a pseudomorphism `f` on free module `M` has matrix m acting on - the left on elements `v \in M`, with twisting morphism `\theta`. - Then we have + It is defined as the matrix `M` whose lines (resp. columns if + ``side`` is ``right``) are the coordinates of the images of + the distinguished basis of the domain. - `f(v) = m*\theta(v)` + EXAMPLES:: - where `\theta` acts of the coefficients of `v` in terms of the basis - for `m`. + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: M = Fq^3 + sage: f = M.pseudohom([[1, z, 3], [0, 1, z^2], [z+1, 1, 1]], Frob) + sage: f.matrix() + [ 1 z 3] + [ 0 1 z^2] + [z + 1 1 1] + + sage: e1, e2, e3 = M.basis() + sage: f(e1) + (1, z, 3) + sage: f(e2) + (0, 1, z^2) + sage: f(e3) + (z + 1, 1, 1) - EXAMPLES:: + TESTS:: - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: e = M((3*Fq.gen()^2 + 5*Fq.gen() + 2, 6*Fq.gen()^2 + 2*Fq.gen() + 2, Fq.gen() + 4)) - sage: ph.matrix() - [1 2 3] - [0 1 1] - [2 1 1] - sage: ph(e) == ph.matrix()*vector([frob(c) for c in e]) + sage: v = M.random_element() + sage: f(v) == vector([Frob(c) for c in v]) * f.matrix() True + """ - return self._base_matrix + if self._side == "left": + return self._matrix + else: + return self._matrix.transpose() def twisting_derivation(self): r""" - Return the twisting derivation of the pseudomorphism. + Return the twisting derivation of the pseudomorphism + (or ``None`` if the twisting derivation is zero). EXAMPLES:: - sage: P. = ZZ[]; deriv = P.derivation(); M = P^2 - sage: f = M.pseudohom([[1, 2*x], [x, 1]], deriv, side="right") + sage: P. = ZZ[] + sage: d = P.derivation() + sage: M = P^2 + sage: f = M.pseudohom([[1, 2*x], [x, 1]], d) sage: f.twisting_derivation() d/dx + + :: + + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: f = V.pseudohom([[1, z], [0, z^2]], Frob) + sage: f.twisting_derivation() + """ - return self.derivation + return self._derivation def twisting_morphism(self): r""" - Return the twisting homomorphism of the pseudomorphism. + Return the twisting morphism of the pseudomorphism + (or ``None`` if the twisting morphism is the identity). + + EXAMPLES:: + + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: f = V.pseudohom([[1, z], [0, z^2]], Frob) + sage: f.twisting_morphism() + Frobenius endomorphism z |--> z^7 on Finite Field in z of size 7^3 + + :: + + sage: P. = ZZ[] + sage: d = P.derivation() + sage: M = P^2 + sage: f = M.pseudohom([[1, 2*x], [x, 1]], d) + sage: f.twisting_morphism() + + """ + return self._morphism + + def side(self): + """ + Return the side of vectors acted on, relative to the matrix. EXAMPLES:: - sage: Fq = GF(343); M = Fq^3; frob = Fq.frobenius_endomorphism() - sage: ph = M.pseudohom([[1, 2, 3], [0, 1, 1], [2, 1, 1]], frob, side="right") - sage: ph.twisting_morphism() - Frobenius endomorphism z3 |--> z3^7 on Finite Field in z3 of size 7^3 + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + + sage: m = matrix(2, [1, z, z^2, z^3]) + sage: h1 = V.pseudohom(m, Frob) + sage: h1.side() + 'left' + sage: h1([1, 0]) + (1, z) + + sage: h2 = V.pseudohom(m, Frob, side="right") + sage: h2.side() + 'right' + sage: h2([1, 0]) + (1, z^2) + """ + + return self._side + + def side_switch(self): + """ + Return the same morphism, acting on vectors on the opposite side. + + EXAMPLES:: + + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + + sage: m = matrix(2, [1, z, z^2, z^3]) + sage: h1 = V.pseudohom(m, Frob) + sage: h1 + Free module pseudomorphism (twisted by z |--> z^7) defined by the matrix + [ 1 z] + [ z^2 z^2 + 3] + Domain: Vector space of dimension 2 over Finite Field in z of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3 + + sage: h2 = h1.side_switch() + sage: h2 + Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix + [ 1 z^2] + [ z z^2 + 3] + Domain: Vector space of dimension 2 over Finite Field in z of size 7^3 + Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3 + + We check that ``h1`` and ``h2`` are the same:: + + sage: v = V.random_element() + sage: h1(v) == h2(v) + True + """ - return self.twist_morphism + if self._side == "left": + side = "right" + mat = self._matrix.transpose() + else: + side = "left" + mat = self._matrix + return self.parent()(mat, side) + + + def __eq__(self, other): + r""" + Compare this morphism with ``other``. + + TESTS:: + + sage: Fq. = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: m = random_matrix(Fq, 2) + + sage: f = V.pseudohom(m, Frob) + sage: g = V.pseudohom(m.transpose(), Frob, side="right") + sage: f == g + True + + sage: g = V.pseudohom(m.transpose(), Frob) + sage: f == g + False + + sage: g = V.pseudohom(m, Frob^2) + sage: f == g + False + + sage: g = V.pseudohom(m, Frob^3) + sage: h = V.hom(m) + sage: g == h + True + + """ + if isinstance(other, FreeModuleMorphism): + try: + other = self.parent()(other) + except ValueError: + return False + if isinstance(other, FreeModulePseudoMorphism): + return self.parent() is other.parent() and self._matrix == other._matrix + From 133b35ca9dda0dacef383be708424055add91df3 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 12 Sep 2024 11:02:32 +0200 Subject: [PATCH 156/507] remove useless import --- src/sage/modules/free_module.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index a78ca3cd3e7..bb8c8ca4484 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -213,8 +213,6 @@ richcmp_not_equal, ) from sage.structure.sequence import Sequence -from sage.categories.morphism import Morphism -from sage.matrix.constructor import matrix ############################################################################### From 1cbbc0b27f8af0e6600328a0bf025d8de398479d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 19 Sep 2024 11:01:02 +0200 Subject: [PATCH 157/507] include doctests in documentation --- src/doc/en/reference/modules/index.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 93a337db04c..a67fffc92d3 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -92,6 +92,15 @@ Morphisms sage/modules/matrix_morphism +Pseudomorphisms +--------------- + +.. toctree:: + :maxdepth: 1 + + sage/modules/free_module_pseudohomspace + sage/modules/free_module_pseudomorphism + Vectors ------- From 4b88e009867e9a0cbe2ca8d8bb2aa8a78165f29f Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 19 Sep 2024 11:14:17 +0200 Subject: [PATCH 158/507] colon missing --- src/sage/modules/free_module_pseudomorphism.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index e32a6f1bb8b..54b40ba0024 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -140,7 +140,7 @@ def __init__(self, parent, f, side): Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 - An example with ``side=right``: + An example with ``side=right``:: sage: M.pseudohom(mat, Frob, side="right") Free module pseudomorphism (twisted by z |--> z^5) defined as left-multiplication by the matrix From db5ae0ae676fa9f2ed8180b7dad85e7d6ee6a1f2 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 23 Sep 2024 18:03:55 +0200 Subject: [PATCH 159/507] move repr_twist to OrePolynomialRing --- .../modules/free_module_pseudohomspace.py | 42 ++++------------- .../modules/free_module_pseudomorphism.py | 10 ++-- .../rings/polynomial/ore_polynomial_ring.py | 47 +++++++++++++++---- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 93377f58183..14762e86078 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -63,7 +63,7 @@ def __classcall_private__(cls, domain, codomain, twist): - ``codomain`` -- a free module, the codomain of this pseudomorphism - - ``twist`` -- a twisting morphism or a twisting derivation + - ``twist`` -- a twisting morphism/derivation or a Ore polynomial ring TESTS:: @@ -80,7 +80,12 @@ def __classcall_private__(cls, domain, codomain, twist): ring = domain.base_ring() if codomain.base_ring() is not ring: raise ValueError("the domain and the codomain must be defined over the same ring") - ore = OrePolynomialRing(ring, twist, names='x') + if isinstance(twist, OrePolynomialRing): + ore = twist + if ore.base_ring() is not ring: + raise ValueError("base rings do not match") + else: + ore = OrePolynomialRing(ring, twist, names='x') if isinstance(ore, OrePolynomialRing) and ore._derivation is not None: if not codomain.has_coerce_map_from(domain): raise ValueError("the domain does not coerce into the codomain") @@ -158,37 +163,6 @@ def __reduce__(self): twist = self._derivation return FreeModulePseudoHomspace, (self.domain(), self.codomain(), twist) - def _repr_twist(self): - r""" - Return a string representation of the twisting morphisms. - - This is a helper method. - - TESTS:: - - sage: F. = GF(5^3) - sage: Frob = F.frobenius_endomorphism() - sage: M = F^2 - - sage: M.PseudoHom(Frob)._repr_twist() - 'twisted by z |--> z^5' - - sage: M.PseudoHom(Frob^3)._repr_twist() - 'untwisted' - - """ - s = "" - if self._morphism is not None: - s += self._morphism._repr_short() - if self._derivation is not None: - if s != "": - s += " and " - s += self._derivation._repr_() - if s == "": - return "untwisted" - else: - return "twisted by " + s - def _repr_(self): r""" Returns a string representation of this pseudomorphism space. @@ -215,7 +189,7 @@ def _repr_(self): Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in t over Rational Field """ - twist = self._repr_twist() + twist = self._ore._repr_twist() if self.domain() is self.codomain(): return "Set of Pseudoendomorphisms (%s) of %s" % (twist, self.domain()) else: diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 54b40ba0024..f549fb95e85 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -214,9 +214,11 @@ def _call_(self, x): v = x_twist * self._matrix if self._derivation is not None: v += D(list(map(self._derivation, x))) - if not C.is_ambient(): + if C.is_ambient(): + v = v.list() + else: v = C.linear_combination_of_basis(v) - return C._element_constructor_(v) + return C(v) def _repr_(self): r""" @@ -246,7 +248,7 @@ def _repr_(self): Domain: Vector space of dimension 3 over Finite Field in z of size 7^3 Codomain: Vector space of dimension 3 over Finite Field in z of size 7^3 """ - twist = self.parent()._repr_twist() + twist = self.parent()._ore._repr_twist() s = "Free module pseudomorphism (%s) defined " % twist if self._side == "right": s += "as left-multiplication " @@ -290,7 +292,7 @@ def matrix(self): """ if self._side == "left": - return self._matrix + return self._matrix.__copy__() else: return self._matrix.transpose() diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index f28b90ad198..1c36401db3c 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -317,7 +317,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False, Univariate Polynomial Ring in x over Finite Field in a of size 5^2 sage: S. = OrePolynomialRing(k, Frob, polcast=False) sage: S - Ore Polynomial Ring in x over Finite Field in a of size 5^2 twisted by Identity + Ore Polynomial Ring in x over Finite Field in a of size 5^2 untwisted """ if base_ring not in CommutativeRings(): raise TypeError('base_ring must be a commutative ring') @@ -325,7 +325,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False, if (twist.domain() is not base_ring or twist.codomain() is not base_ring): raise TypeError("the twisting morphism must be an endomorphism of base_ring (=%s)" % base_ring) - if twist.is_identity() and polcast: + if twist.is_identity(): morphism = None else: morphism = twist @@ -596,6 +596,38 @@ def _coerce_map_from_(self, P): if P.variable_name() == self.variable_name(): return base_ring.has_coerce_map_from(P.base_ring()) + def _repr_twist(self): + r""" + Return a string representation of the twisting morphisms. + + This is a helper method. + + TESTS:: + + sage: F. = GF(5^3) + sage: Frob = F.frobenius_endomorphism() + + sage: S. = OrePolynomialRing(F, Frob) + sage: S._repr_twist() + 'twisted by z |--> z^5' + + sage: T. = OrePolynomialRing(F, Frob^3, polcast=False) + sage: T._repr_twist() + 'untwisted' + + """ + s = "" + if self._morphism is not None: + s += self._morphism._repr_short() + if self._derivation is not None: + if s != "": + s += " and " + s += self._derivation._repr_() + if s == "": + return "untwisted" + else: + return "twisted by " + s + def _repr_(self) -> str: r""" Return a string representation of ``self``. @@ -613,13 +645,7 @@ def _repr_(self) -> str: sage: T Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt """ - s = "Ore Polynomial Ring in %s over %s twisted by " % (self.variable_name(), self.base_ring()) - if self._derivation is None: - s += self._morphism._repr_short() - else: - if self._morphism is not None: - s += "%s and " % self._morphism._repr_short() - s += self._derivation._repr_() + s = "Ore Polynomial Ring in %s over %s %s" % (self.variable_name(), self.base_ring(), self._repr_twist()) if self.is_sparse(): s = "Sparse " + s return s @@ -1201,6 +1227,9 @@ def fraction_field(self): self._fraction_field.register_coercion(self) return self._fraction_field + def quotient(self, P, names=None): + return self(P).quotient_module(names=names) + def _pushout_(self, other): r""" Return the pushout of this Ore polynomial ring and ``other``. From 0dd14fef3740954c8e82a3fe8e05040e5f87a9f1 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 23 Sep 2024 18:56:58 +0200 Subject: [PATCH 160/507] fix lint and doctest --- src/sage/modules/free_module_pseudohomspace.py | 2 +- src/sage/modules/free_module_pseudomorphism.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 14762e86078..b78f7fb3c18 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -85,7 +85,7 @@ def __classcall_private__(cls, domain, codomain, twist): if ore.base_ring() is not ring: raise ValueError("base rings do not match") else: - ore = OrePolynomialRing(ring, twist, names='x') + ore = OrePolynomialRing(ring, twist, names='x', polcast=False) if isinstance(ore, OrePolynomialRing) and ore._derivation is not None: if not codomain.has_coerce_map_from(domain): raise ValueError("the domain does not coerce into the codomain") diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index f549fb95e85..8e05955bd26 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -414,7 +414,6 @@ def side_switch(self): mat = self._matrix return self.parent()(mat, side) - def __eq__(self, other): r""" Compare this morphism with ``other``. @@ -452,4 +451,3 @@ def __eq__(self, other): return False if isinstance(other, FreeModulePseudoMorphism): return self.parent() is other.parent() and self._matrix == other._matrix - From fe44f43a35605fe2a4683225e95760178e8a2168 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 26 Sep 2024 07:06:56 +0200 Subject: [PATCH 161/507] apply derivation after coercion --- src/sage/modules/free_module_pseudomorphism.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 8e05955bd26..619c162f9a7 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -212,13 +212,13 @@ def _call_(self, x): else: x_twist = D(list(map(self._morphism, x))) v = x_twist * self._matrix - if self._derivation is not None: - v += D(list(map(self._derivation, x))) if C.is_ambient(): - v = v.list() + v = C(v.list()) else: v = C.linear_combination_of_basis(v) - return C(v) + if self._derivation is not None: + v += D(list(map(self._derivation, x))) + return v def _repr_(self): r""" From 438c8c4967b966c9c11f399fd54ac68964d8d94c Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 26 Sep 2024 07:09:13 +0200 Subject: [PATCH 162/507] fix lint --- src/sage/modules/free_module.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index bb8c8ca4484..70c6a25e05f 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3226,7 +3226,6 @@ def pseudohom(self, f, twist, codomain=None, side="left"): parent = self.PseudoHom(twist, codomain) return FreeModulePseudoMorphism(parent, f, side) - def inner_product_matrix(self): """ Return the default identity inner product matrix associated to this From abe978d96c3115f0d8a3d9e3d0af5677bdcfef70 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 26 Sep 2024 15:55:38 +0200 Subject: [PATCH 163/507] PseudoHom -> pseudoHom --- src/sage/modules/free_module.py | 12 ++++---- .../modules/free_module_pseudohomspace.py | 28 +++++++++---------- .../modules/free_module_pseudomorphism.py | 6 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 70c6a25e05f..b08b428b639 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3110,7 +3110,7 @@ def hom(self, im_gens, codomain=None, **kwds): codomain = R**n return super().hom(im_gens, codomain, **kwds) - def PseudoHom(self, twist, codomain=None): + def pseudoHom(self, twist, codomain=None): r""" Return the Pseudo Hom space corresponding to given data. @@ -3126,17 +3126,17 @@ def PseudoHom(self, twist, codomain=None): sage: F = GF(25) sage: Frob = F.frobenius_endomorphism() sage: M = F^2 - sage: M.PseudoHom(Frob) + sage: M.pseudoHom(Frob) Set of Pseudoendomorphisms (twisted by z2 |--> z2^5) of Vector space of dimension 2 over Finite Field in z2 of size 5^2 .. SEEALSO:: :meth:`pseudohom` """ - from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace + from sage.modules.free_module_pseudohomspace import FreeModulepseudoHomspace if codomain is None: codomain = self - return FreeModulePseudoHomspace(self, codomain, twist) + return FreeModulepseudoHomspace(self, codomain, twist) def pseudohom(self, f, twist, codomain=None, side="left"): r""" @@ -3220,10 +3220,10 @@ def pseudohom(self, f, twist, codomain=None, side="left"): .. SEEALSO:: - :meth:`PseudoHom` + :meth:`pseudoHom` """ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism - parent = self.PseudoHom(twist, codomain) + parent = self.pseudoHom(twist, codomain) return FreeModulePseudoMorphism(parent, f, side) def inner_product_matrix(self): diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index b78f7fb3c18..e4a30341c32 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -31,7 +31,7 @@ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism -class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): +class FreeModulepseudoHomspace(UniqueRepresentation, HomsetWithBase): Element = FreeModulePseudoMorphism r""" @@ -45,7 +45,7 @@ class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): sage: F = GF(125) sage: M = F^2 sage: Frob = F.frobenius_endomorphism() - sage: PHS = M.PseudoHom(Frob) + sage: PHS = M.pseudoHom(Frob) sage: h = PHS([[1, 2], [1, 1]]) sage: e = M((4*F.gen()^2 + F.gen() + 2, 4*F.gen()^2 + 4*F.gen() + 4)) sage: h(e) @@ -70,9 +70,9 @@ def __classcall_private__(cls, domain, codomain, twist): sage: F = GF(125) sage: Frob = F.frobenius_endomorphism() sage: M = F^2 - sage: H = M.PseudoHom(Frob) + sage: H = M.pseudoHom(Frob) sage: type(H) - + sage: # Testsuite(H).run() @@ -108,7 +108,7 @@ def __init__(self, domain, codomain, ore): sage: F = GF(125) sage: Frob = F.frobenius_endomorphism() sage: M = F^2 - sage: M.PseudoHom(Frob) + sage: M.pseudoHom(Frob) Set of Pseudoendomorphisms (twisted by z3 |--> z3^5) of Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ @@ -134,7 +134,7 @@ def _element_constructor_(self, f, side="left"): sage: F. = GF(5^3) sage: Frob = F.frobenius_endomorphism() sage: V = F^2 - sage: H = V.PseudoHom(Frob) + sage: H = V.pseudoHom(Frob) sage: H([[1, z], [z, z^2]]) Free module pseudomorphism (twisted by z |--> z^5) defined by the matrix @@ -153,7 +153,7 @@ def __reduce__(self): sage: F = GF(125) sage: Frob = F.frobenius_endomorphism() sage: M = F^2 - sage: H = M.PseudoHom(Frob) + sage: H = M.pseudoHom(Frob) sage: loads(dumps(M)) is M True """ @@ -161,7 +161,7 @@ def __reduce__(self): twist = self._morphism else: twist = self._derivation - return FreeModulePseudoHomspace, (self.domain(), self.codomain(), twist) + return FreeModulepseudoHomspace, (self.domain(), self.codomain(), twist) def _repr_(self): r""" @@ -172,12 +172,12 @@ def _repr_(self): sage: Fq = GF(7^3) sage: Frob = Fq.frobenius_endomorphism() sage: V = Fq^2 - sage: V.PseudoHom(Frob) # indirect doctest + sage: V.pseudoHom(Frob) # indirect doctest Set of Pseudoendomorphisms (twisted by z3 |--> z3^7) of Vector space of dimension 2 over Finite Field in z3 of size 7^3 :: - sage: V.PseudoHom(Frob, codomain=Fq^3) # indirect doctest + sage: V.pseudoHom(Frob, codomain=Fq^3) # indirect doctest Set of Pseudomorphism (twisted by z3 |--> z3^7) from Vector space of dimension 2 over Finite Field in z3 of size 7^3 to Vector space of dimension 3 over Finite Field in z3 of size 7^3 :: @@ -185,7 +185,7 @@ def _repr_(self): sage: A. = QQ[] sage: d = A.derivation() sage: M = A^3 - sage: M.PseudoHom(d) + sage: M.pseudoHom(d) Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in t over Rational Field """ @@ -209,7 +209,7 @@ def ore_ring(self, var='x'): sage: Fq. = GF(7^3) sage: Frob = Fq.frobenius_endomorphism() sage: V = Fq^2 - sage: H = V.PseudoHom(Frob) + sage: H = V.pseudoHom(Frob) sage: H.ore_ring() Ore Polynomial Ring in x over Finite Field in z of size 7^3 twisted by z |--> z^7 @@ -231,7 +231,7 @@ def matrix_space(self): sage: Frob = Fq.frobenius_endomorphism() sage: V = Fq^2 sage: W = Fq^3 - sage: H = V.PseudoHom(Frob, codomain=W) + sage: H = V.pseudoHom(Frob, codomain=W) sage: H.matrix_space() Full MatrixSpace of 2 by 3 dense matrices over Finite Field in z of size 7^3 @@ -247,7 +247,7 @@ def basis(self, side="left"): sage: Fq = GF(7^3) sage: Frob = Fq.frobenius_endomorphism() sage: V = Fq^2 - sage: PHS = V.PseudoHom(Frob) + sage: PHS = V.pseudoHom(Frob) sage: PHS.basis() [Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [1 0] diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 619c162f9a7..60165dc455a 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -100,7 +100,7 @@ def __init__(self, parent, f, side): sage: F. = GF(5^3) sage: Frob = F.frobenius_endomorphism() sage: M = F^2 - sage: H = M.PseudoHom(Frob) + sage: H = M.pseudoHom(Frob) sage: H Set of Pseudoendomorphisms (twisted by z |--> z^5) of Vector space of dimension 2 over Finite Field in z of size 5^3 @@ -116,7 +116,7 @@ def __init__(self, parent, f, side): Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 sage: type(f) - + or a pseudomorphism with the same parent:: @@ -133,7 +133,7 @@ def __init__(self, parent, f, side): sage: id = End(F).identity() sage: g = M.hom(mat) - sage: M.PseudoHom(id)(g) + sage: M.pseudoHom(id)(g) Free module pseudomorphism (untwisted) defined by the matrix [ 1 z] [ z^2 2*z + 2] From 27a7e4d98b9f9a6cb76a5891372732188cc45926 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 27 Sep 2024 09:02:29 +0200 Subject: [PATCH 164/507] minor fixes --- src/sage/modules/free_module.py | 4 ++-- src/sage/modules/free_module_pseudohomspace.py | 8 ++++---- src/sage/modules/free_module_pseudomorphism.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b08b428b639..a2c0132291c 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3133,10 +3133,10 @@ def pseudoHom(self, twist, codomain=None): :meth:`pseudohom` """ - from sage.modules.free_module_pseudohomspace import FreeModulepseudoHomspace + from sage.modules.free_module_pseudohomspace import FreeModulePseudoHomspace if codomain is None: codomain = self - return FreeModulepseudoHomspace(self, codomain, twist) + return FreeModulePseudoHomspace(self, codomain, twist) def pseudohom(self, f, twist, codomain=None, side="left"): r""" diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index e4a30341c32..11589d64f42 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -31,7 +31,7 @@ from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism -class FreeModulepseudoHomspace(UniqueRepresentation, HomsetWithBase): +class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): Element = FreeModulePseudoMorphism r""" @@ -72,7 +72,7 @@ def __classcall_private__(cls, domain, codomain, twist): sage: M = F^2 sage: H = M.pseudoHom(Frob) sage: type(H) - + sage: # Testsuite(H).run() @@ -86,7 +86,7 @@ def __classcall_private__(cls, domain, codomain, twist): raise ValueError("base rings do not match") else: ore = OrePolynomialRing(ring, twist, names='x', polcast=False) - if isinstance(ore, OrePolynomialRing) and ore._derivation is not None: + if ore._derivation is not None: if not codomain.has_coerce_map_from(domain): raise ValueError("the domain does not coerce into the codomain") return cls.__classcall__(cls, domain, codomain, ore) @@ -161,7 +161,7 @@ def __reduce__(self): twist = self._morphism else: twist = self._derivation - return FreeModulepseudoHomspace, (self.domain(), self.codomain(), twist) + return FreeModulePseudoHomspace, (self.domain(), self.codomain(), twist) def _repr_(self): r""" diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 60165dc455a..30ccb047151 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -116,7 +116,7 @@ def __init__(self, parent, f, side): Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 sage: type(f) - + or a pseudomorphism with the same parent:: From e671a993f5d80ed62d6660c1a915c5e04d2d165c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 28 Sep 2024 21:32:15 +0200 Subject: [PATCH 165/507] make iterating over monomial_coefficients more robust --- src/sage/data_structures/stream.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 73bf5a9c026..d9aa93d4b17 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -2018,7 +2018,8 @@ def _collect_equations(self, offset): # TODO: it is a coincidence that `coefficients` # currently exists in all examples; # the monomials are only needed for the error messages - elt_coeffs = list(zip(elt.monomials(), elt.coefficients())) + elt_coeffs = [(self._coefficient_ring.monomial(idx), coeff) + for idx, coeff in elt.monomial_coefficients().items()] all_coeffs.append(elt_coeffs) for idx, coeff in elt_coeffs: From c5828caff8e7218747cec9519322d59956be51f9 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 17 Oct 2024 23:59:42 +0200 Subject: [PATCH 166/507] Turn some doctests into long tests --- .../elliptic_curves/ell_rational_field.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 0bc71b09d42..8a935ad0c09 100755 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -441,7 +441,7 @@ def mwrank(self, options=''): EXAMPLES:: sage: E = EllipticCurve('37a1') - sage: E.mwrank() #random + sage: E.mwrank() # random ... sage: print(E.mwrank()) Curve [0,0,1,-1,0] : Basic pair: I=48, J=-432 @@ -2316,7 +2316,7 @@ def gens(self, proof=None, **kwds): over Rational Field sage: E1.gens() # random (if database not used) [(-400 : 8000 : 1), (0 : -8000 : 1)] - sage: E1.gens(algorithm='pari') #random + sage: E1.gens(algorithm='pari') # random [(-400 : 8000 : 1), (0 : -8000 : 1)] TESTS:: @@ -2334,6 +2334,13 @@ def gens(self, proof=None, **kwds): sage: P = E.lift_x(10/9) sage: set(E.gens()) <= set([P,-P]) True + + Check that :issue:`38813` has been fixed: + + sage: set_random_seed(91390048253425197917505296851335255685) + sage: E = EllipticCurve([-127^2,0]) + sage: E.gens(use_database=False, algorithm='pari', pari_effort=4) # long time + [(611429153205013185025/9492121848205441 : 15118836457596902442737698070880/924793900700594415341761 : 1)] """ if proof is None: from sage.structure.proof.proof import get_flag @@ -2386,15 +2393,16 @@ def _compute_gens(self, proof, True sage: E = EllipticCurve([-127^2,0]) - sage: E.gens(use_database=False, algorithm='pari',pari_effort=4) # random + sage: E.gens(use_database=False, algorithm='pari', pari_effort=4) # long time, random [(611429153205013185025/9492121848205441 : 15118836457596902442737698070880/924793900700594415341761 : 1)] TESTS:: + sage: E = EllipticCurve([-127^2,0]) sage: P = E.lift_x(611429153205013185025/9492121848205441) - sage: ge = set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) - sage: ge <= set([P+T for T in E.torsion_points()] - ....: + [-P+T for T in E.torsion_points()]) + sage: (set(E.gens(use_database=False, algorithm='pari', pari_effort=4)) # long time + ....: <= set([P+T for T in E.torsion_points()] + ....: + [-P+T for T in E.torsion_points()])) True """ # If the optional extended database is installed and an From 32103e45c6a354bc7f963e33363f843d7581d175 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 14 Nov 2024 10:39:30 +0100 Subject: [PATCH 167/507] format docstring --- src/sage/matrix/matrix_cdv.pyx | 16 +++++++++++----- src/sage/modules/free_module.py | 6 +++--- .../modules/free_module_pseudohomspace.py | 19 +++++-------------- .../modules/free_module_pseudomorphism.py | 12 +----------- .../rings/polynomial/ore_polynomial_ring.py | 1 - 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index 238d875844f..749180bd85b 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -1,3 +1,4 @@ +# sage_setup: distribution = sagemath-modules r""" Special methods for matrices over discrete valuation rings/fields. """ @@ -36,7 +37,7 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): TESTS:: sage: # needs sage.rings.padics - sage: K = Qp(5, print_mode='digits', prec=5) + sage: K = Qp(5, print_mode="digits", prec=5) sage: H = matrix(K, 3, 3, range(9)) sage: H [ 0 ...00001 ...00002] @@ -69,12 +70,14 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): n = H.nrows() for j in range(n-1): k = j + 1 - maxi = H.get_unsafe(k, j).precision_relative() + #maxi = H.get_unsafe(k, j).precision_relative() + maxi = - H.get_unsafe(k, j).valuation() i = j + 2 while maxi is not Infinity and i < n: entry = H.get_unsafe(i, j) if entry: - m = entry.precision_relative() + #m = entry.precision_relative() + m = -entry.valuation() if m > maxi: maxi = m k = i @@ -84,9 +87,12 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): H.swap_rows_c(j+1, k) H.swap_columns_c(j+1, k) pivot = H.get_unsafe(j+1, j) + # print(pivot) if pivot: - inv = ~pivot + inv = (~pivot).lift_to_precision() for i in range(j+2, n): scalar = inv * H.get_unsafe(i, j) - H.add_multiple_of_row_c(i, j+1, -scalar, j) + scalar = scalar.lift_to_precision() + # print(" ", scalar) + H.add_multiple_of_row_c(i, j+1, -scalar, 0) H.add_multiple_of_column_c(j+1, i, scalar, 0) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index a2c0132291c..c42649bb613 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3118,7 +3118,7 @@ def pseudoHom(self, twist, codomain=None): - ``twist`` -- the twisting morphism or the twisting derivation - - ``codomain`` (default: ``None``) -- the codomain of the pseudo + - ``codomain`` -- (default: ``None``) the codomain of the pseudo morphisms; if ``None``, the codomain is the same than the domain EXAMPLES:: @@ -3161,10 +3161,10 @@ def pseudohom(self, f, twist, codomain=None, side="left"): - ``twist`` -- the twisting morphism or the twisting derivation - - ``codomain`` (default: ``None``) -- the codomain of the pseudo + - ``codomain`` -- (default: ``None``) the codomain of the pseudo morphisms; if ``None``, the codomain is the same than the domain - - ``side`` (default: ``left``) -- side of the vectors acted on by + - ``side`` -- (default: ``left``) side of the vectors acted on by the matrix EXAMPLES:: diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 11589d64f42..fc6705f8f0b 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -1,10 +1,9 @@ """ -Space of Pseudomorphisms of free modules +Space of pseudomorphisms of free modules AUTHORS: - - Xavier Caruso, Yossef Musleh (2024-09): initial version - +- Xavier Caruso, Yossef Musleh (2024-09): initial version """ # **************************************************************************** # Copyright (C) 2024 Xavier Caruso @@ -32,8 +31,6 @@ class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): - Element = FreeModulePseudoMorphism - r""" This class implements the space of Pseudomorphisms with a fixed twist. @@ -50,8 +47,9 @@ class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): sage: e = M((4*F.gen()^2 + F.gen() + 2, 4*F.gen()^2 + 4*F.gen() + 4)) sage: h(e) (z3, 2*z3^2 + 3*z3 + 3) - """ + Element = FreeModulePseudoMorphism + @staticmethod def __classcall_private__(cls, domain, codomain, twist): r""" @@ -75,7 +73,6 @@ def __classcall_private__(cls, domain, codomain, twist): sage: # Testsuite(H).run() - """ ring = domain.base_ring() if codomain.base_ring() is not ring: @@ -110,7 +107,6 @@ def __init__(self, domain, codomain, ore): sage: M = F^2 sage: M.pseudoHom(Frob) Set of Pseudoendomorphisms (twisted by z3 |--> z3^5) of Vector space of dimension 2 over Finite Field in z3 of size 5^3 - """ self._domain = domain self._codomain = codomain @@ -142,7 +138,6 @@ def _element_constructor_(self, f, side="left"): [ z z^2] Domain: Vector space of dimension 2 over Finite Field in z of size 5^3 Codomain: Vector space of dimension 2 over Finite Field in z of size 5^3 - """ return self.element_class(self, f, side) @@ -187,7 +182,6 @@ def _repr_(self): sage: M = A^3 sage: M.pseudoHom(d) Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in t over Rational Field - """ twist = self._ore._repr_twist() if self.domain() is self.codomain(): @@ -201,7 +195,7 @@ def ore_ring(self, var='x'): INPUT: - - ``var`` (default: ``x``) -- a string, the name of + - ``var`` -- string (default: ``x``) the name of tha variable EXAMPLES:: @@ -216,7 +210,6 @@ def ore_ring(self, var='x'): sage: H.ore_ring('y') Ore Polynomial Ring in y over Finite Field in z of size 7^3 twisted by z |--> z^7 - """ return self._ore.change_var(var) @@ -234,7 +227,6 @@ def matrix_space(self): sage: H = V.pseudoHom(Frob, codomain=W) sage: H.matrix_space() Full MatrixSpace of 2 by 3 dense matrices over Finite Field in z of size 7^3 - """ return self._matrix_space @@ -266,6 +258,5 @@ def basis(self, side="left"): [0 1] Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3] - """ return Sequence(self(mat) for mat in self._matrix_space.basis()) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 30ccb047151..a830f422eb2 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -3,8 +3,7 @@ AUTHORS: - - Xavier Caruso, Yossef Musleh (2024-09): initial version - +- Xavier Caruso, Yossef Musleh (2024-09): initial version """ # **************************************************************************** # Copyright (C) 2024 Xavier Caruso @@ -80,7 +79,6 @@ class FreeModulePseudoMorphism(Morphism): sage: v = (4*z^2 + 4*z + 3, 2, z + 5) sage: phi(v) (2*z + 1, 6*z^2 + 4*z + 5) - """ def __init__(self, parent, f, side): """ @@ -155,7 +153,6 @@ def __init__(self, parent, f, side): Traceback (most recent call last): ... ValueError: the side must be either 'left' or 'right' - """ Morphism.__init__(self, parent) dom = parent.domain() @@ -199,7 +196,6 @@ def _call_(self, x): sage: g = M.pseudohom([[1, z, 3], [0, 1, 1], [2, 1, 1]], Frob, side="right") sage: g(e) (z^2 + 6*z + 2, z^2 + 2*z + 1, 2*z^2 + 4*z) - """ D = self.domain() C = self.codomain() @@ -289,7 +285,6 @@ def matrix(self): sage: v = M.random_element() sage: f(v) == vector([Frob(c) for c in v]) * f.matrix() True - """ if self._side == "left": return self._matrix.__copy__() @@ -317,7 +312,6 @@ def twisting_derivation(self): sage: V = Fq^2 sage: f = V.pseudohom([[1, z], [0, z^2]], Frob) sage: f.twisting_derivation() - """ return self._derivation @@ -342,7 +336,6 @@ def twisting_morphism(self): sage: M = P^2 sage: f = M.pseudohom([[1, 2*x], [x, 1]], d) sage: f.twisting_morphism() - """ return self._morphism @@ -369,7 +362,6 @@ def side(self): sage: h2([1, 0]) (1, z^2) """ - return self._side def side_switch(self): @@ -404,7 +396,6 @@ def side_switch(self): sage: v = V.random_element() sage: h1(v) == h2(v) True - """ if self._side == "left": side = "right" @@ -442,7 +433,6 @@ def __eq__(self, other): sage: h = V.hom(m) sage: g == h True - """ if isinstance(other, FreeModuleMorphism): try: diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 1c36401db3c..8e4a331b911 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -614,7 +614,6 @@ def _repr_twist(self): sage: T. = OrePolynomialRing(F, Frob^3, polcast=False) sage: T._repr_twist() 'untwisted' - """ s = "" if self._morphism is not None: From aa0b7135b5e25e1d547e31e274f424d755c0bf3a Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 14 Nov 2024 15:29:49 +0100 Subject: [PATCH 168/507] fix doctest --- src/sage/matrix/matrix_cdv.pyx | 16 +++++----------- src/sage/modules/free_module.py | 8 ++++---- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index 749180bd85b..238d875844f 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -1,4 +1,3 @@ -# sage_setup: distribution = sagemath-modules r""" Special methods for matrices over discrete valuation rings/fields. """ @@ -37,7 +36,7 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): TESTS:: sage: # needs sage.rings.padics - sage: K = Qp(5, print_mode="digits", prec=5) + sage: K = Qp(5, print_mode='digits', prec=5) sage: H = matrix(K, 3, 3, range(9)) sage: H [ 0 ...00001 ...00002] @@ -70,14 +69,12 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): n = H.nrows() for j in range(n-1): k = j + 1 - #maxi = H.get_unsafe(k, j).precision_relative() - maxi = - H.get_unsafe(k, j).valuation() + maxi = H.get_unsafe(k, j).precision_relative() i = j + 2 while maxi is not Infinity and i < n: entry = H.get_unsafe(i, j) if entry: - #m = entry.precision_relative() - m = -entry.valuation() + m = entry.precision_relative() if m > maxi: maxi = m k = i @@ -87,12 +84,9 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): H.swap_rows_c(j+1, k) H.swap_columns_c(j+1, k) pivot = H.get_unsafe(j+1, j) - # print(pivot) if pivot: - inv = (~pivot).lift_to_precision() + inv = ~pivot for i in range(j+2, n): scalar = inv * H.get_unsafe(i, j) - scalar = scalar.lift_to_precision() - # print(" ", scalar) - H.add_multiple_of_row_c(i, j+1, -scalar, 0) + H.add_multiple_of_row_c(i, j+1, -scalar, j) H.add_multiple_of_column_c(j+1, i, scalar, 0) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index c42649bb613..214a02eaa73 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3144,11 +3144,11 @@ def pseudohom(self, f, twist, codomain=None, side="left"): We recall that, given two `R`-modules `M` and `M'` together with a ring homomorphism `\theta: R \to R` and a `\theta`-derivation, - `\delta: R \to R` (that is, a map such that: + `\delta: R \to R` (that is, a map such that `\delta(xy) = \theta(x)\delta(y) + \delta(x)y`), a - pseudomorphism `f : M \to M'` is a additive map such that + pseudomorphism `f : M \to M'` is an additive map such that - .. MATH: + .. MATH:: f(\lambda x) = \theta(\lambda) f(x) + \delta(\lambda) x @@ -3210,7 +3210,7 @@ def pseudohom(self, f, twist, codomain=None, side="left"): True If the twisting derivation is not zero, the domain must - coerce into the codomain + coerce into the codomain:: sage: N = R^3 sage: M.pseudohom([[1, t, t^2], [1, t^2, t^4]], d, codomain=N) From 2305dd1953ccfc00a4b3a2976f651c7199656503 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 15 Nov 2024 08:00:43 +0100 Subject: [PATCH 169/507] formatting --- .../modules/free_module_pseudohomspace.py | 22 +++++++++++++------ .../modules/free_module_pseudomorphism.py | 10 +++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index fc6705f8f0b..2d1df9c99d8 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -106,7 +106,8 @@ def __init__(self, domain, codomain, ore): sage: Frob = F.frobenius_endomorphism() sage: M = F^2 sage: M.pseudoHom(Frob) - Set of Pseudoendomorphisms (twisted by z3 |--> z3^5) of Vector space of dimension 2 over Finite Field in z3 of size 5^3 + Set of Pseudoendomorphisms (twisted by z3 |--> z3^5) of + Vector space of dimension 2 over Finite Field in z3 of size 5^3 """ self._domain = domain self._codomain = codomain @@ -168,12 +169,15 @@ def _repr_(self): sage: Frob = Fq.frobenius_endomorphism() sage: V = Fq^2 sage: V.pseudoHom(Frob) # indirect doctest - Set of Pseudoendomorphisms (twisted by z3 |--> z3^7) of Vector space of dimension 2 over Finite Field in z3 of size 7^3 + Set of Pseudoendomorphisms (twisted by z3 |--> z3^7) of + Vector space of dimension 2 over Finite Field in z3 of size 7^3 :: sage: V.pseudoHom(Frob, codomain=Fq^3) # indirect doctest - Set of Pseudomorphism (twisted by z3 |--> z3^7) from Vector space of dimension 2 over Finite Field in z3 of size 7^3 to Vector space of dimension 3 over Finite Field in z3 of size 7^3 + Set of Pseudomorphism (twisted by z3 |--> z3^7) + from Vector space of dimension 2 over Finite Field in z3 of size 7^3 + to Vector space of dimension 3 over Finite Field in z3 of size 7^3 :: @@ -181,7 +185,8 @@ def _repr_(self): sage: d = A.derivation() sage: M = A^3 sage: M.pseudoHom(d) - Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in t over Rational Field + Set of Pseudoendomorphisms (twisted by d/dt) of Ambient free module of rank 3 over + the principal ideal domain Univariate Polynomial Ring in t over Rational Field """ twist = self._ore._repr_twist() if self.domain() is self.codomain(): @@ -245,15 +250,18 @@ def basis(self, side="left"): [1 0] [0 0] Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [0 1] [0 0] Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [0 0] [1 0] Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 - Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix + Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3, + Free module pseudomorphism (twisted by z3 |--> z3^7) defined by the matrix [0 0] [0 1] Domain: Vector space of dimension 2 over Finite Field in z3 of size 7^3 diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index a830f422eb2..72b9f6de3b3 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -19,7 +19,7 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#################################################################################### +# **************************************************************************** from sage.categories.morphism import Morphism from sage.structure.richcmp import rich_to_bool, richcmp @@ -32,13 +32,13 @@ class FreeModulePseudoMorphism(Morphism): ring homomorphism, and `\delta: R \to R` a `\theta`-derivation, which is a map such that: - .. MATH: + .. MATH:: \delta(xy) = \theta(x)\delta(y) + \delta(x)y. A pseudomorphism `f : M \to M` is an additive map such that - .. MATH: + .. MATH:: f(\lambda x) = \theta(\lambda)f(x) + \delta(\lambda) x @@ -258,7 +258,7 @@ def matrix(self): Return the underlying matrix of this pseudomorphism. It is defined as the matrix `M` whose lines (resp. columns if - ``side`` is ``right``) are the coordinates of the images of + ``side`` is ``"right"``) are the coordinates of the images of the distinguished basis of the domain. EXAMPLES:: @@ -272,6 +272,8 @@ def matrix(self): [ 0 1 z^2] [z + 1 1 1] + :: + sage: e1, e2, e3 = M.basis() sage: f(e1) (1, z, 3) From 4d3be5572023df5caef87e3f1ea529bf8b83072d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 17 Nov 2024 09:53:03 +0100 Subject: [PATCH 170/507] TestSuite --- src/sage/modules/free_module.py | 7 +- .../modules/free_module_pseudohomspace.py | 70 ++++++++++++++++++- .../modules/free_module_pseudomorphism.py | 18 ++++- .../rings/polynomial/ore_polynomial_ring.py | 4 +- 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 214a02eaa73..ba9e951854e 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3112,7 +3112,7 @@ def hom(self, im_gens, codomain=None, **kwds): def pseudoHom(self, twist, codomain=None): r""" - Return the Pseudo Hom space corresponding to given data. + Return the pseudo-Hom space corresponding to given data. INPUT: @@ -3222,9 +3222,8 @@ def pseudohom(self, f, twist, codomain=None, side="left"): :meth:`pseudoHom` """ - from sage.modules.free_module_pseudomorphism import FreeModulePseudoMorphism - parent = self.pseudoHom(twist, codomain) - return FreeModulePseudoMorphism(parent, f, side) + H = self.pseudoHom(twist, codomain) + return H(f, side) def inner_product_matrix(self): """ diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 2d1df9c99d8..2df8be4b3a2 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -72,7 +72,7 @@ def __classcall_private__(cls, domain, codomain, twist): sage: type(H) - sage: # Testsuite(H).run() + sage: TestSuite(H).run() """ ring = domain.base_ring() if codomain.base_ring() is not ring: @@ -268,3 +268,71 @@ def basis(self, side="left"): Codomain: Vector space of dimension 2 over Finite Field in z3 of size 7^3] """ return Sequence(self(mat) for mat in self._matrix_space.basis()) + + def _test_additive_associativity(self, tester): + r""" + Test associativity for (not necessarily all) elements in this parent. + + This test is not relevant for pseudo-morphisms because they are not + stable by addition. + + TESTS:: + + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: PHS = V.pseudoHom(Frob) + sage: TestSuite(PHS).run() # indirect doctest + """ + pass + + def _test_distributivity(self, tester): + r""" + Test distributivity for (not necessarily all) elements in this parent. + + This test is not relevant for pseudo-morphisms because they are not + stable by addition. + + TESTS:: + + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: PHS = V.pseudoHom(Frob) + sage: TestSuite(PHS).run() # indirect doctest + """ + pass + + def _test_one(self, tester): + r""" + Test properties the identity element. + + This test is not relevant for pseudo-morphisms because the identity + is not a pseudo-morphism in general. + + TESTS:: + + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: PHS = V.pseudoHom(Frob) + sage: TestSuite(PHS).run() # indirect doctest + """ + pass + + def _test_zero(self, tester): + r""" + Test properties of the zero element. + + This test is not relevant for pseudo-morphisms because the zero + map is not a pseudo-morphism in general. + + TESTS:: + + sage: Fq = GF(7^3) + sage: Frob = Fq.frobenius_endomorphism() + sage: V = Fq^2 + sage: PHS = V.pseudoHom(Frob) + sage: TestSuite(PHS).run() # indirect doctest + """ + pass diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 72b9f6de3b3..965152a893e 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -131,7 +131,8 @@ def __init__(self, parent, f, side): sage: id = End(F).identity() sage: g = M.hom(mat) - sage: M.pseudoHom(id)(g) + sage: g2 = M.pseudoHom(id)(g) + sage: g2 Free module pseudomorphism (untwisted) defined by the matrix [ 1 z] [ z^2 2*z + 2] @@ -140,7 +141,8 @@ def __init__(self, parent, f, side): An example with ``side=right``:: - sage: M.pseudohom(mat, Frob, side="right") + sage: h = M.pseudohom(mat, Frob, side="right") + sage: h Free module pseudomorphism (twisted by z |--> z^5) defined as left-multiplication by the matrix [ 1 z] [ z^2 2*z + 2] @@ -153,6 +155,12 @@ def __init__(self, parent, f, side): Traceback (most recent call last): ... ValueError: the side must be either 'left' or 'right' + + :: + + sage: TestSuite(f).run() + sage: TestSuite(g2).run() + sage: TestSuite(h).run() """ Morphism.__init__(self, parent) dom = parent.domain() @@ -407,6 +415,9 @@ def side_switch(self): mat = self._matrix return self.parent()(mat, side) + def __nonzero__(self): + return not (self._derivation is None and self._matrix) + def __eq__(self, other): r""" Compare this morphism with ``other``. @@ -443,3 +454,6 @@ def __eq__(self, other): return False if isinstance(other, FreeModulePseudoMorphism): return self.parent() is other.parent() and self._matrix == other._matrix + + def _test_nonzero_equal(self, tester): + pass diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 8e4a331b911..073a3fc9292 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -321,7 +321,9 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False, """ if base_ring not in CommutativeRings(): raise TypeError('base_ring must be a commutative ring') - if isinstance(twist, Morphism): + if twist is None: + morphism = derivation = None + elif isinstance(twist, Morphism): if (twist.domain() is not base_ring or twist.codomain() is not base_ring): raise TypeError("the twisting morphism must be an endomorphism of base_ring (=%s)" % base_ring) From 65ab9e49aafd357c494dc0e07996beccdc77bff3 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 17 Nov 2024 09:57:12 +0100 Subject: [PATCH 171/507] remove method `quotient` --- src/sage/rings/polynomial/ore_polynomial_ring.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 073a3fc9292..adf6edc13d4 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1228,9 +1228,6 @@ def fraction_field(self): self._fraction_field.register_coercion(self) return self._fraction_field - def quotient(self, P, names=None): - return self(P).quotient_module(names=names) - def _pushout_(self, other): r""" Return the pushout of this Ore polynomial ring and ``other``. From 7b561584639198874b8c48f663046a753ebf07c3 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:23:31 +0700 Subject: [PATCH 172/507] Allow CRT_list() to be called with one element --- src/sage/arith/misc.py | 45 ++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 745d5fcbbe7..05a6792f7de 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1841,7 +1841,6 @@ def gcd(a, b=None, **kwargs): except TypeError: return m(py_scalar_to_element(b), **kwargs) - from sage.structure.sequence import Sequence seq = Sequence(py_scalar_to_element(el) for el in a) if seq.universe() is ZZ: return GCD_list(seq) @@ -3498,11 +3497,15 @@ def crt(a, b, m=None, n=None): CRT = crt -def CRT_list(values, moduli): +def CRT_list(values, moduli=None): r""" Given a list ``values`` of elements and a list of corresponding ``moduli``, find a single element that reduces to each element of ``values`` modulo the corresponding moduli. + This function can also be called with one argument, each element + of the list is a :class:`IntegerMod_abstract` object. In this case, it returns + another :class:`IntegerMod_abstract` object. + .. SEEALSO:: - :func:`crt` @@ -3529,6 +3532,13 @@ def CRT_list(values, moduli): ... ValueError: no solution to crt problem since gcd(180,150) does not divide 92-1 + Call with one argument:: + + sage: x = CRT_list([mod(2,3),mod(3,5),mod(2,7)]); x + 23 + sage: x.parent() + Ring of integers modulo 105 + The arguments must be lists:: sage: CRT_list([1,2,3],"not a list") @@ -3575,14 +3585,26 @@ def CRT_list(values, moduli): sage: ms [5, 7, 9] """ - if not isinstance(values, list) or not isinstance(moduli, list): + if not isinstance(values, list) or not isinstance(moduli, (list, type(None))): raise ValueError("arguments to CRT_list should be lists") - if len(values) != len(moduli): - raise ValueError("arguments to CRT_list should be lists of the same length") - if not values: - return ZZ.zero() - if len(values) == 1: - return moduli[0].parent()(values[0]) + return_mod = moduli is None + if return_mod: + from sage.rings.finite_rings.integer_mod import IntegerMod_abstract, Mod + if not values: + return Mod(0, 1) + if not all(isinstance(v, IntegerMod_abstract) for v in values): + raise TypeError("arguments to CRT_list should be lists of IntegerMod") + if len(values) == 1: + return values[0] + moduli = [v.modulus() for v in values] + values = [v.lift() for v in values] + else: + if len(values) != len(moduli): + raise ValueError("arguments to CRT_list should be lists of the same length") + if not values: + return ZZ.zero() + if len(values) == 1: + return moduli[0].parent()(values[0]) # The result is computed using a binary tree. In typical cases, # this scales much better than folding the list from one side. @@ -3593,7 +3615,10 @@ def CRT_list(values, moduli): vs[i] = CRT(vs[i], v, ms[i], m) ms[i] = lcm(ms[i], m) values, moduli = vs, ms - return values[0] % moduli[0] + if return_mod: + return Mod(values[0], moduli[0]) + else: + return values[0] % moduli[0] def CRT_basis(moduli): From 1962918af8a45f63581f1876593f34fa58d319ac Mon Sep 17 00:00:00 2001 From: mklss <59539887+mklss@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:38:56 +0100 Subject: [PATCH 173/507] Add reseed_rng option to p_iter_fork --- src/sage/parallel/decorate.py | 11 +++++++++++ src/sage/parallel/use_fork.py | 29 ++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index 90ee3a5cc42..86e455fd955 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -308,6 +308,7 @@ def parallel(p_iter='fork', ncpus=None, **kwds): - ``ncpus`` -- integer; maximal number of subprocesses to use at the same time - ``timeout`` -- number of seconds until each subprocess is killed (only supported by ``'fork'``; zero means not at all) + - ``reseed_rng``: reseed the rng in each subprocess .. warning:: @@ -398,6 +399,16 @@ def parallel(p_iter='fork', ncpus=None, **kwds): sage: Foo.square_classmethod(3) 9 + + By default, all subprocesses use the same random seed and therefore the same deterministic randomness. + For functions that should be randomized, we can reseed the random seed in each subprocess:: + + sage: @parallel(reseed_rng = True) + ....: def unif(n): return ZZ.random_element(x = 0, y = n) + sage: set_random_seed(42) + sage: sorted(unif([1000]*3)) + [(((1000,), {}), 444), (((1000,), {}), 597), (((1000,), {}), 640)] + .. warning:: Currently, parallel methods do not work with the diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index 36269127b80..c7f71b49f58 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -19,6 +19,8 @@ from sage.interfaces.process import ContainChildren from sage.misc.timing import walltime +from sage.misc.randstate import set_random_seed +from sage.misc.prandom import getrandbits class WorkerData: """ @@ -68,6 +70,7 @@ class p_iter_fork: about what the iterator does (e.g., killing subprocesses) - ``reset_interfaces`` -- boolean (default: ``True``); whether to reset all pexpect interfaces + - ``reseed_rng`` -- booolean (default: ``False``); whether or not to reseed the rng in the subprocesses EXAMPLES:: @@ -80,7 +83,7 @@ class p_iter_fork: sage: X.verbose False """ - def __init__(self, ncpus, timeout=0, verbose=False, reset_interfaces=True): + def __init__(self, ncpus, timeout=0, verbose=False, reset_interfaces=True, reseed_rng=False): """ Create a ``fork()``-based parallel iterator. @@ -103,6 +106,8 @@ def __init__(self, ncpus, timeout=0, verbose=False, reset_interfaces=True): self.timeout = float(timeout) # require a float self.verbose = verbose self.reset_interfaces = reset_interfaces + self.reseed_rng = reseed_rng + self.worker_seed = None def __call__(self, f, inputs): """ @@ -148,8 +153,6 @@ def __call__(self, f, inputs): sage: list(Polygen([QQ,QQ])) [(((Rational Field,), {}), x), (((Rational Field,), {}), x)] """ - n = self.ncpus - v = list(inputs) import os import sys import signal @@ -158,12 +161,19 @@ def __call__(self, f, inputs): dir = tmp_dir() timeout = self.timeout + n = self.ncpus + inputs = list(inputs) + if self.reseed_rng: + seeds = [getrandbits(512) for _ in range(0, len(inputs))] + vs = list(zip(inputs, seeds)) + else: + vs = list(zip(inputs, [None]*len(inputs))) workers = {} try: - while v or workers: + while vs or workers: # Spawn up to n subprocesses - while v and len(workers) < n: - v0 = v.pop(0) # Input value for the next subprocess + while vs and len(workers) < n: + (v0, seed0) = vs.pop(0) # Input value and seed for the next subprocess with ContainChildren(): pid = os.fork() # The way fork works is that pid returns the @@ -171,8 +181,8 @@ def __call__(self, f, inputs): # process and returns 0 for the subprocess. if not pid: # This is the subprocess. + self.worker_seed = seed0 if self.reseed_rng else None self._subprocess(f, dir, *v0) - workers[pid] = WorkerData(v0) if len(workers) > 0: @@ -304,6 +314,11 @@ def _subprocess(self, f, dir, args, kwds={}): else: invalidate_all() + # Reseed rng, if requested. + if self.reseed_rng == True: + set_random_seed(self.worker_seed) + + # Now evaluate the function f. value = f(*args, **kwds) From 702cebf6e491c7e8bd6092329299623ab06b0c01 Mon Sep 17 00:00:00 2001 From: mklss <59539887+mklss@users.noreply.github.com> Date: Sun, 24 Nov 2024 17:20:42 +0100 Subject: [PATCH 174/507] Appease linter. --- src/sage/parallel/decorate.py | 1 - src/sage/parallel/use_fork.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index 86e455fd955..fc38410b7bb 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -399,7 +399,6 @@ def parallel(p_iter='fork', ncpus=None, **kwds): sage: Foo.square_classmethod(3) 9 - By default, all subprocesses use the same random seed and therefore the same deterministic randomness. For functions that should be randomized, we can reseed the random seed in each subprocess:: diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index c7f71b49f58..89aa1c42146 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -315,10 +315,9 @@ def _subprocess(self, f, dir, args, kwds={}): invalidate_all() # Reseed rng, if requested. - if self.reseed_rng == True: + if self.reseed_rng: set_random_seed(self.worker_seed) - # Now evaluate the function f. value = f(*args, **kwds) From beadc1699b2b363ddb78d1287b6d992b2d0a7c92 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:31:09 +0700 Subject: [PATCH 175/507] More coverage --- src/sage/arith/misc.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 408d5c4cfa8..21c30c926d0 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -3574,6 +3574,21 @@ def CRT_list(values, moduli=None): sage: CRT_list([mpz(2),mpz(3),mpz(2)], [mpz(3),mpz(5),mpz(7)]) 23 + Tests for call with one argument:: + + sage: x = CRT_list([mod(2,3)]); x + 2 + sage: x.parent() + Ring of integers modulo 3 + sage: x = CRT_list([]); x + 0 + sage: x.parent() + Ring of integers modulo 1 + sage: x = CRT_list([2]); x + Traceback (most recent call last): + ... + TypeError: if one argument is given, it should be a list of IntegerMod + Make sure we are not mutating the input lists:: sage: xs = [1,2,3] @@ -3593,7 +3608,7 @@ def CRT_list(values, moduli=None): if not values: return Mod(0, 1) if not all(isinstance(v, IntegerMod_abstract) for v in values): - raise TypeError("arguments to CRT_list should be lists of IntegerMod") + raise TypeError("if one argument is given, it should be a list of IntegerMod") if len(values) == 1: return values[0] moduli = [v.modulus() for v in values] From 3d79e3cc8ce6756ef3c6f55a873734a644e9d7c9 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 30 Nov 2024 13:53:46 +0700 Subject: [PATCH 176/507] Fix some errors in documentation of cachefunc --- src/sage/misc/cachefunc.pyx | 76 +++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 52c10174dca..5efa2f932e8 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -147,7 +147,7 @@ that has generally a slower attribute access, but fully supports cached methods. We remark, however, that cached methods are *much* faster if attribute access works. So, we expect that :class:`~sage.structure.element.ElementWithCachedMethod` will -hardly by used. +hardly be used. :: sage: # needs sage.misc.cython @@ -1882,7 +1882,7 @@ cdef class CachedMethodCaller(CachedFunction): sage: a.f(5) is a.f(y=1,x=5) True - The method can be called as a bound function using the same cache:: + The method can be called as a unbound function using the same cache:: sage: a.f(5) is Foo.f(a, 5) True @@ -1940,7 +1940,7 @@ cdef class CachedMethodCaller(CachedFunction): True """ if self._instance is None: - # cached method bound to a class + # cached method bound to a class i.e. unbound method, such as ``Foo.f`` instance = args[0] args = args[1:] return self._cachedmethod.__get__(instance)(*args, **kwds) @@ -1987,7 +1987,7 @@ cdef class CachedMethodCaller(CachedFunction): 5 """ if self._instance is None: - # cached method bound to a class + # cached method bound to a class i.e. unbound method, such as ``CachedMethodTest.f`` instance = args[0] args = args[1:] return self._cachedmethod.__get__(instance).cached(*args, **kwds) @@ -2548,16 +2548,15 @@ cdef class CachedMethod(): sage: a.f(3) is res True - Note, however, that the :class:`CachedMethod` is replaced by a - :class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs` - as soon as it is bound to an instance or class:: + Note, however, that accessing the attribute directly will call :meth:`__get__`, + and returns a :class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs`. sage: P. = QQ[] sage: I = P*[a,b] sage: type(I.__class__.gens) - - So, you would hardly ever see an instance of this class alive. + sage: type(I.__class__.__dict__["gens"]) + The parameter ``key`` can be used to pass a function which creates a custom cache key for inputs. In the following example, this parameter is @@ -2640,13 +2639,17 @@ cdef class CachedMethod(): sage: a.f0() 4 - The computations in method ``f`` are tried to store in a - dictionary assigned to the instance ``a``:: + For methods with parameters, computations in method ``f`` are + tried to store in a dictionary assigned to the instance ``a``:: sage: hasattr(a, '_cache__f') True sage: a._cache__f {((2,), ()): 4} + sage: a._cache_f0 + Traceback (most recent call last): + ... + AttributeError: 'Foo' object has no attribute '_cache_f0'... As a shortcut, useful to speed up internal computations, the same dictionary is also available as an attribute @@ -2694,6 +2697,8 @@ cdef class CachedMethod(): def __call__(self, inst, *args, **kwds): """ Call the cached method as a function on an instance. + This code path is not used directly except in a few rare cases, + see examples for details. INPUT: @@ -2745,20 +2750,48 @@ cdef class CachedMethod(): ....: def f(self, n=2): ....: return self._x^n sage: a = Foo(2) + + Initially ``_cache__f`` is not an attribute of ``a``:: + + sage: hasattr(a, "_cache__f") + False + + When the attribute is accessed (thus ``__get__`` is called), + the cache is created and assigned to the attribute:: + + sage: a.f + Cached version of + sage: a._cache__f + {} sage: a.f() 4 + sage: a.f.cache + {((2,), ()): 4} + sage: a._cache__f + {((2,), ()): 4} - Note that we cannot provide a direct test, since ``a.f`` is - an instance of :class:`CachedMethodCaller`. But during its - initialisation, this method was called in order to provide the - cached method caller with its cache, and, if possible, assign - it to an attribute of ``a``. So, the following is an indirect - doctest:: + Testing the method directly:: - sage: a.f.cache # indirect doctest - {((2,), ()): 4} + sage: a = Foo(2) + sage: hasattr(a, "_cache__f") + False + sage: Foo.__dict__["f"]._get_instance_cache(a) + {} sage: a._cache__f + {} + sage: a.f() + 4 + sage: Foo.__dict__["f"]._get_instance_cache(a) {((2,), ()): 4} + + Using ``__dict__`` is needed to access this function because + ``Foo.f`` would call ``__get__`` and thus create a + :class:`CachedMethodCaller`:: + + sage: type(Foo.f) + + sage: type(Foo.__dict__["f"]) + """ default = {} if self._cachedfunc.do_pickle else NonpicklingDict() try: @@ -2868,8 +2901,9 @@ cdef class CachedSpecialMethod(CachedMethod): For new style classes ``C``, it is not possible to override a special method, such as ``__hash__``, in the ``__dict__`` of an instance ``c`` of - ``C``, because Python will for efficiency reasons always use what is - provided by the class, not by the instance. + ``C``, because Python will always use what is provided by the class, not + by the instance to avoid metaclass confusion. See + ``_. By consequence, if ``__hash__`` would be wrapped by using :class:`CachedMethod`, then ``hash(c)`` will access ``C.__hash__`` and bind From 70f47a467889329d16fa4cda2878f6f4fedac29c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:45:33 +0700 Subject: [PATCH 177/507] Remove a duplicated paragraph --- src/sage/misc/cachefunc.pyx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 5efa2f932e8..7e65cef7341 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -97,15 +97,6 @@ approach is still needed for cpdef methods:: sage: O.direct_method(5) is O.direct_method(5) True -In some cases, one would only want to keep the result in cache as long -as there is any other reference to the result. By :issue:`12215`, this is -enabled for :class:`~sage.structure.unique_representation.UniqueRepresentation`, -which is used to create unique parents: If an algebraic structure, such -as a finite field, is only temporarily used, then it will not stay in -cache forever. That behaviour is implemented using ``weak_cached_function``, -that behaves the same as ``cached_function``, except that it uses a -:class:`~sage.misc.weak_dict.CachedWeakValueDictionary` for storing the results. - By :issue:`11115`, even if a parent does not allow attribute assignment, it can inherit a cached method from the parent class of a category (previously, the cache would have been broken):: From c16f7b0b69aa6713efdb0e84bbd1be6539cce434 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:51:45 +0700 Subject: [PATCH 178/507] Add warning on shallow copy of object with cached_method --- src/sage/misc/cachefunc.pyx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 7e65cef7341..67ce1ae15e9 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -406,6 +406,30 @@ the parent as its first argument:: sage: d = a.add_bigoh(1) # needs sage.rings.padics sage: b._cache_key() == d._cache_key() # this would be True if the parents were not included False + +Note that shallow copy of mutable objects may behave unexpectedly:: + + sage: class Foo: + ....: @cached_method + ....: def f(self): + ....: return self.x + sage: from copy import copy, deepcopy + sage: a = Foo() + sage: a.x = 1 + sage: a.f() + 1 + sage: b = copy(a) + sage: b.x = 2 + sage: b.f() # incorrect! + 1 + sage: b.f is a.f # this is the problem + True + sage: b = deepcopy(a) + sage: b.x = 2 + sage: b.f() # correct + 2 + sage: b.f is a.f + False """ # **************************************************************************** From 354bc31c848ef72f8af36a0511d784b19dcec217 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 1 Dec 2024 15:50:55 +0700 Subject: [PATCH 179/507] Apply suggestions --- src/sage/misc/cachefunc.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 67ce1ae15e9..6790b2ab4e8 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -1897,7 +1897,7 @@ cdef class CachedMethodCaller(CachedFunction): sage: a.f(5) is a.f(y=1,x=5) True - The method can be called as a unbound function using the same cache:: + The method can be called as an unbound function using the same cache:: sage: a.f(5) is Foo.f(a, 5) True @@ -1955,7 +1955,7 @@ cdef class CachedMethodCaller(CachedFunction): True """ if self._instance is None: - # cached method bound to a class i.e. unbound method, such as ``Foo.f`` + # unbound cached method such as ``Foo.f`` instance = args[0] args = args[1:] return self._cachedmethod.__get__(instance)(*args, **kwds) @@ -2002,7 +2002,7 @@ cdef class CachedMethodCaller(CachedFunction): 5 """ if self._instance is None: - # cached method bound to a class i.e. unbound method, such as ``CachedMethodTest.f`` + # unbound cached method such as ``CachedMethodTest.f`` instance = args[0] args = args[1:] return self._cachedmethod.__get__(instance).cached(*args, **kwds) @@ -2654,8 +2654,8 @@ cdef class CachedMethod(): sage: a.f0() 4 - For methods with parameters, computations in method ``f`` are - tried to store in a dictionary assigned to the instance ``a``:: + For methods with parameters, the results of method ``f`` is attempted + to be stored in a dictionary attribute of the instance ``a``:: sage: hasattr(a, '_cache__f') True From 2ca0716feca04a842a4e96e26855b5befb93c3d9 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 8 Dec 2024 10:49:13 +0700 Subject: [PATCH 180/507] Fix incorrect code block rendering --- src/sage/misc/cachefunc.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 6790b2ab4e8..6f1b2b1d430 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2566,6 +2566,8 @@ cdef class CachedMethod(): Note, however, that accessing the attribute directly will call :meth:`__get__`, and returns a :class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs`. + :: + sage: P. = QQ[] sage: I = P*[a,b] sage: type(I.__class__.gens) From 320daec3714e3ba4f853c2149d8e1e894c538204 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:00:21 +0700 Subject: [PATCH 181/507] Make hack used in debug_options slightly cleaner --- src/sage/structure/debug_options.pyx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/sage/structure/debug_options.pyx b/src/sage/structure/debug_options.pyx index 78e12faa0ce..a906278715a 100644 --- a/src/sage/structure/debug_options.pyx +++ b/src/sage/structure/debug_options.pyx @@ -41,12 +41,14 @@ cdef class DebugOptions_class: cdef DebugOptions_class debug = DebugOptions_class() -# Since "debug" is declared with a type, it can only be cimported at +# Since "debug" is declared with cdef, it can only be cimported at # the Cython level, not imported in plain Python. So we add it to the -# globals manually. We need to hack into Cython internals for this -# since Sage is compiled with the old_style_globals option. -from cpython.object cimport PyObject -cdef extern from *: - PyObject* __pyx_d - -(__pyx_d)["debug"] = debug +# globals manually. However this will make the variable out of sync +# if some user modifies the object, which is inevitable. +# See https://github.com/cython/cython/issues/3959#issuecomment-753455240 +# and https://github.com/cython/cython/issues/656 +# Note that ``_this_module`` could not be ``globals()`` +# because Sage is compiled with the old_style_globals option. +import sage.structure.debug_options as _this_module +_this_module.debug = debug +del _this_module From c0e9b90bf8bc7a99f9f7411df25e92021a337667 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 9 Dec 2024 06:02:06 +0700 Subject: [PATCH 182/507] Remove erroneous member declaration in farey_symbol --- src/sage/modular/arithgroup/farey_symbol.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index af81a0f61fc..499a685036a 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -47,7 +47,6 @@ from sage.structure.richcmp cimport richcmp_not_equal cdef extern from "sl2z.hpp": cppclass cpp_SL2Z "SL2Z": - mpz_class a, b, c, d cpp_SL2Z(int, int, int, int) cpp_SL2Z(mpz_class, mpz_class, mpz_class, mpz_class) mpz_class a() From 81cde250c638953d4f3a8b18af8a065e4435d0b7 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 12 Dec 2024 15:40:27 +0100 Subject: [PATCH 183/507] typos --- src/sage/modules/free_module_pseudohomspace.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 2df8be4b3a2..d6e94c7c295 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -201,7 +201,7 @@ def ore_ring(self, var='x'): INPUT: - ``var`` -- string (default: ``x``) the name of - tha variable + the variable EXAMPLES:: @@ -273,7 +273,7 @@ def _test_additive_associativity(self, tester): r""" Test associativity for (not necessarily all) elements in this parent. - This test is not relevant for pseudo-morphisms because they are not + This test is not relevant for pseudomorphisms because they are not stable by addition. TESTS:: @@ -290,7 +290,7 @@ def _test_distributivity(self, tester): r""" Test distributivity for (not necessarily all) elements in this parent. - This test is not relevant for pseudo-morphisms because they are not + This test is not relevant for pseudomorphisms because they are not stable by addition. TESTS:: @@ -307,8 +307,8 @@ def _test_one(self, tester): r""" Test properties the identity element. - This test is not relevant for pseudo-morphisms because the identity - is not a pseudo-morphism in general. + This test is not relevant for pseudomorphisms because the identity + is not a pseudomorphism in general. TESTS:: @@ -324,8 +324,8 @@ def _test_zero(self, tester): r""" Test properties of the zero element. - This test is not relevant for pseudo-morphisms because the zero - map is not a pseudo-morphism in general. + This test is not relevant for pseudomorphisms because the zero + map is not a pseudomorphism in general. TESTS:: From c06a1433e2c8b166e74c44a101581f9c249cabda Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 12 Dec 2024 15:41:21 +0100 Subject: [PATCH 184/507] uncapitalize pseudomorphisms --- src/sage/modules/free_module_pseudohomspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index d6e94c7c295..292d16f086d 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -32,7 +32,7 @@ class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): r""" - This class implements the space of Pseudomorphisms with a fixed twist. + This class implements the space of pseudomorphisms with a fixed twist. For free modules, the elements of a pseudomorphism correspond to matrices which define the mapping on elements of a basis. From d935b8d62ce58071786904c6d9e4ce477ab91e6f Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 14 Dec 2024 21:55:09 +0700 Subject: [PATCH 185/507] Add note about makeflags and ninja parallelism --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index af91374fe19..f7844bf6ac7 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,20 @@ in the Installation Guide. powerful machines, you might even consider `-j16`, as building with more jobs than CPU cores can speed things up further. + Alternatively, the `MAKEFLAGS` environment variable can be used. + In this case, only provide the flag itself, for example + `export MAKEFLAGS="-j4"`. + + Note that the compilation may nonetheless uses a different number of + threads, because sometimes `ninja` is used. + Unfortunately, [there is no way to control number of jobs `ninja` uses + from environment variables](https://github.com/ninja-build/ninja/issues/1482). + See also https://github.com/sagemath/sage/issues/38950. + + If the [Meson build system](https://doc-release--sagemath.netlify.app/html/en/installation/meson) + is used, the number of jobs running in parallel passed to `meson compile` will be respected, + because everything are managed by `ninja`. + To reduce the terminal output during the build, type `export V=0`. (`V` stands for "verbosity".) From c10623f0ee19d047b46d7f39d2b302f5662d4ae7 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:49:18 +0700 Subject: [PATCH 186/507] Apply suggestions from code review Co-authored-by: Travis Scrimshaw --- src/sage/arith/misc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index ca889d2bc74..9a09a3263a2 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -3498,7 +3498,8 @@ def crt(a, b, m=None, n=None): def CRT_list(values, moduli=None): - r""" Given a list ``values`` of elements and a list of corresponding + r""" + Given a list ``values`` of elements and a list of corresponding ``moduli``, find a single element that reduces to each element of ``values`` modulo the corresponding moduli. @@ -3600,7 +3601,7 @@ def CRT_list(values, moduli=None): sage: ms [5, 7, 9] """ - if not isinstance(values, list) or not isinstance(moduli, (list, type(None))): + if not isinstance(values, list) or (moduli is not None and not isinstance(moduli, list)): raise ValueError("arguments to CRT_list should be lists") return_mod = moduli is None if return_mod: From c0820e6373b703362a83feb8e6cb65a269844ac8 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:53:35 +0700 Subject: [PATCH 187/507] Apply more suggested changes --- src/sage/arith/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 9a09a3263a2..e7bb72fc8e3 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -3504,8 +3504,8 @@ def CRT_list(values, moduli=None): ``values`` modulo the corresponding moduli. This function can also be called with one argument, each element - of the list is a :class:`IntegerMod_abstract` object. In this case, it returns - another :class:`IntegerMod_abstract` object. + of the list is a :mod:`modular integer `. + In this case, it returns another modular integer. .. SEEALSO:: From 0344c371eaaa6114fe97c3dfd1859067eaa7cb13 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:31:19 +0700 Subject: [PATCH 188/507] Retrigger CI From c1814966ce9319cfeb37eb64e3fa310cb97a96d5 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 27 Dec 2024 20:56:48 +0700 Subject: [PATCH 189/507] Refactor is_exact for period lattice --- src/sage/schemes/elliptic_curves/ell_field.py | 4 ++ .../schemes/elliptic_curves/period_lattice.py | 71 ++++++++++++------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 3ff2826f3e7..1f5e72c0343 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -1548,10 +1548,14 @@ def period_lattice(self): Period lattice associated to Elliptic Curve defined by y^2 = x^3 + x + 6 over Rational Field sage: EllipticCurve(RR, [1, 6]).period_lattice() Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 6.00000000000000 over Real Field with 53 bits of precision + sage: EllipticCurve(RDF, [1, 6]).period_lattice() + Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.0*x + 6.0 over Real Double Field sage: EllipticCurve(RealField(100), [1, 6]).period_lattice() Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.0000000000000000000000000000*x + 6.0000000000000000000000000000 over Real Field with 100 bits of precision sage: EllipticCurve(CC, [1, 6]).period_lattice() Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.00000000000000*x + 6.00000000000000 over Complex Field with 53 bits of precision + sage: EllipticCurve(CDF, [1, 6]).period_lattice() + Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.0*x + 6.0 over Complex Double Field sage: EllipticCurve(ComplexField(100), [1, 6]).period_lattice() Period lattice associated to Elliptic Curve defined by y^2 = x^3 + 1.0000000000000000000000000000*x + 6.0000000000000000000000000000 over Complex Field with 100 bits of precision sage: EllipticCurve(AA, [1, 6]).period_lattice() diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 54cd135dea9..050243cb45f 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -108,6 +108,7 @@ import sage.rings.abc +from sage.categories.morphism import IdentityMorphism from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import from sage.modules.free_module import FreeModule_generic_pid @@ -116,7 +117,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA, QQbar from sage.rings.rational_field import QQ -from sage.rings.real_mpfr import RealField, RealField_class, RealNumber +from sage.rings.real_mpfr import RealField, RealNumber from sage.schemes.elliptic_curves.constructor import EllipticCurve from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal @@ -231,14 +232,14 @@ def __init__(self, E, embedding=None): # the given embedding: K = E.base_field() - self.is_approximate = isinstance(K, (RealField_class, ComplexField_class)) + self._is_exact = K.is_exact() if embedding is None: if K in (AA, QQbar): embedding = K.hom(QQbar) real = K == AA - elif self.is_approximate: - embedding = K.hom(K) - real = isinstance(K, RealField_class) + elif not self._is_exact: + embedding = IdentityMorphism(K) + real = isinstance(K, (sage.rings.abc.RealField, sage.rings.abc.RealDoubleField)) else: embs = K.embeddings(AA) real = len(embs) > 0 @@ -271,24 +272,24 @@ def __init__(self, E, embedding=None): # The ei are used both for period computation and elliptic # logarithms. - if self.is_approximate: - self.f2 = self.E.two_division_polynomial() - else: + if self._is_exact: self.Ebar = self.E.change_ring(self.embedding) self.f2 = self.Ebar.two_division_polynomial() + else: + self.f2 = self.E.two_division_polynomial() if self.real_flag == 1: # positive discriminant - self._ei = self.f2.roots(K if self.is_approximate else AA,multiplicities=False) + self._ei = self.f2.roots(AA if self._is_exact else K, multiplicities=False) self._ei.sort() # e1 < e2 < e3 e1, e2, e3 = self._ei elif self.real_flag == -1: # negative discriminant - self._ei = self.f2.roots(ComplexField(K.precision()) if self.is_approximate else QQbar, multiplicities=False) + self._ei = self.f2.roots(QQbar if self._is_exact else ComplexField(K.precision()), multiplicities=False) self._ei = sorted(self._ei, key=lambda z: z.imag()) e1, e3, e2 = self._ei # so e3 is real - if not self.is_approximate: + if self._is_exact: e3 = AA(e3) self._ei = [e1, e2, e3] else: - self._ei = self.f2.roots(ComplexField(K.precision()) if self.is_approximate else QQbar, multiplicities=False) + self._ei = self.f2.roots(QQbar if self._is_exact else ComplexField(K.precision()), multiplicities=False) e1, e2, e3 = self._ei # The quantities sqrt(e_i-e_j) are cached (as elements of @@ -350,7 +351,7 @@ def __repr__(self): Defn: a |--> 1.259921049894873? """ K = self.E.base_field() - if K in (QQ, AA, QQbar) or isinstance(K, (RealField_class, ComplexField_class)): + if K in (QQ, AA, QQbar) or isinstance(self.embedding, IdentityMorphism): return "Period lattice associated to %s" % (self.E) return "Period lattice associated to %s with respect to the embedding %s" % (self.E, self.embedding) @@ -656,7 +657,7 @@ def _compute_default_prec(self): r""" Internal function to compute the default precision to be used if nothing is passed in. """ - return self.E.base_field().precision() if self.is_approximate else RealField().precision() + return RealField().precision() if self._is_exact else self.E.base_field().precision() @cached_method def _compute_periods_real(self, prec=None, algorithm='sage'): @@ -704,7 +705,7 @@ def _compute_periods_real(self, prec=None, algorithm='sage'): if algorithm == 'pari': ainvs = self.E.a_invariants() - if self.E.base_field() is not QQ and not self.is_approximate: + if self.E.base_field() is not QQ and self._is_exact: ainvs = [C(self.embedding(ai)).real() for ai in ainvs] # The precision for omega() is determined by ellinit() @@ -716,7 +717,7 @@ def _compute_periods_real(self, prec=None, algorithm='sage'): raise ValueError("invalid value of 'algorithm' parameter") pi = R.pi() - # Up to now everything has been exact in AA or QQbar (unless self.is_approximate), + # Up to now everything has been exact in AA or QQbar (if self._is_exact), # but now we must go transcendental. Only now is the desired precision used! if self.real_flag == 1: # positive discriminant a, b, c = (R(x) for x in self._abc) @@ -788,7 +789,7 @@ def _compute_periods_complex(self, prec=None, normalise=True): prec = self._compute_default_prec() C = ComplexField(prec) - # Up to now everything has been exact in AA or QQbar (unless self.is_approximate), + # Up to now everything has been exact in AA or QQbar (if self._is_exact), # but now we must go transcendental. Only now is the desired precision used! pi = C.pi() a, b, c = (C(x) for x in self._abc) @@ -1757,10 +1758,19 @@ def elliptic_logarithm(self, P, prec=None, reduce=True): sage: L.real_flag -1 sage: P = E(3, 6) - sage: L.elliptic_logarithm(P) + sage: L.elliptic_logarithm(P) # abs tol 1e-26 2.4593388737550379526023682666 - sage: L.elliptic_exponential(_) + sage: L.elliptic_exponential(_) # abs tol 1e-26 (3.0000000000000000000000000000 : 5.9999999999999999999999999999 : 1.0000000000000000000000000000) + sage: E = EllipticCurve(RDF, [1, 6]) + sage: L = E.period_lattice() + sage: L.real_flag + -1 + sage: P = E(3, 6) + sage: L.elliptic_logarithm(P) # abs tol 1e-13 + 2.45933887375504 + sage: L.elliptic_exponential(_) # abs tol 1e-13 + (3.00000000000000 : 6.00000000000001 : 1.00000000000000) Real approximate field, positive discriminant:: @@ -1769,9 +1779,9 @@ def elliptic_logarithm(self, P, prec=None, reduce=True): sage: L.real_flag 1 sage: P = E.lift_x(4) - sage: L.elliptic_logarithm(P) + sage: L.elliptic_logarithm(P) # abs tol 1e-26 0.51188849089267627141925354967 - sage: L.elliptic_exponential(_) + sage: L.elliptic_exponential(_) # abs tol 1e-26 (4.0000000000000000000000000000 : -7.1414284285428499979993998114 : 1.0000000000000000000000000000) Complex approximate field:: @@ -1781,10 +1791,19 @@ def elliptic_logarithm(self, P, prec=None, reduce=True): sage: L.real_flag 0 sage: P = E.lift_x(4) - sage: L.elliptic_logarithm(P) + sage: L.elliptic_logarithm(P) # abs tol 1e-26 -1.1447032790074574712147458157 - 0.72429843602171875396186134806*I - sage: L.elliptic_exponential(_) + sage: L.elliptic_exponential(_) # abs tol 1e-26 (4.0000000000000000000000000000 + 1.2025589033682610849950210280e-30*I : -8.2570982991257407680322611854 - 0.42387771989714340809597881586*I : 1.0000000000000000000000000000) + sage: E = EllipticCurve(CDF, [I, 3*I+4]) + sage: L = E.period_lattice() + sage: L.real_flag + 0 + sage: P = E.lift_x(4) + sage: L.elliptic_logarithm(P) # abs tol 1e-13 + -1.14470327900746 - 0.724298436021719*I + sage: L.elliptic_exponential(_) # abs tol 1e-13 + (4.00000000000000 - 0*I : -8.25709829912574 - 0.423877719897148*I : 1.00000000000000) """ if P.curve() is not self.E: raise ValueError("Point is on the wrong curve") @@ -2002,10 +2021,10 @@ def elliptic_exponential(self, z, to_curve=True): if to_curve: K = x.parent() - if self.is_approximate: - v = self.embedding - else: + if self._is_exact: v = refine_embedding(self.embedding, Infinity) + else: + v = self.embedding a1, a2, a3, a4, a6 = (K(v(a)) for a in self.E.ainvs()) b2 = K(v(self.E.b2())) x = x - b2 / 12 From 174d4a44589b154e69911e27993d819a85f2710b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 28 Dec 2024 12:13:20 +0100 Subject: [PATCH 190/507] add keyword prec to log and exp --- .../charzero_drinfeld_module.py | 66 ++++++++++++++----- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 6142ec2fa2d..c69cb6c4dbb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -25,11 +25,13 @@ from .drinfeld_module import DrinfeldModule from sage.rings.integer_ring import ZZ +from sage.rings.infinity import Infinity from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import lazy_import('sage.rings.lazy_series_ring', 'LazyPowerSeriesRing') +lazy_import('sage.rings.power_series_ring', 'PowerSeriesRing') class DrinfeldModule_charzero(DrinfeldModule): @@ -149,7 +151,7 @@ def _compute_coefficient_exp(self, k): c += self._compute_coefficient_exp(i)*self._compute_coefficient_log(j)**(q**i) return -c - def exponential(self, name='z'): + def exponential(self, prec=Infinity, name='z'): r""" Return the exponential of this Drinfeld module. @@ -158,28 +160,37 @@ def exponential(self, name='z'): INPUT: + - ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``); + the precision at which the series is returned; if ``Infinity``, + a lazy power series in returned + - ``name`` -- string (default: ``'z'``); the name of the generator of the lazy power series ring - OUTPUT: a lazy power series over the base field - EXAMPLES:: sage: A = GF(2)['T'] sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1]) sage: q = A.base_ring().cardinality() - sage: exp = phi.exponential(); exp - z + ((1/(T^2+T))*z^2) + ((1/(T^8+T^6+T^5+T^3))*z^4) + O(z^8) - The exponential is returned as a lazy power series, meaning that - any of its coefficients can be computed on demands:: + When ``prec`` is ``Infinity`` (which is the default), + the exponential is returned as a lazy power series, meaning + that any of its coefficients can be computed on demands:: + sage: exp = phi.exponential(); exp + z + ((1/(T^2+T))*z^2) + ((1/(T^8+T^6+T^5+T^3))*z^4) + O(z^8) sage: exp[2^4] 1/(T^64 + T^56 + T^52 + ... + T^27 + T^23 + T^15) sage: exp[2^5] 1/(T^160 + T^144 + T^136 + ... + T^55 + T^47 + T^31) + On the contrary, when ``prec`` is a finite number, all the + required coefficients are computed at once:: + + sage: phi.exponential(prec=10) + z + (1/(T^2 + T))*z^2 + (1/(T^8 + T^6 + T^5 + T^3))*z^4 + (1/(T^24 + T^20 + T^18 + T^17 + T^14 + T^13 + T^11 + T^7))*z^8 + O(z^10) + Example in higher rank:: sage: A = GF(5)['T'] @@ -216,7 +227,6 @@ def exponential(self, name='z'): See section 4.6 of [Gos1998]_ for the definition of the exponential. """ - L = LazyPowerSeriesRing(self._base, name) zero = self._base.zero() q = self._Fq.cardinality() @@ -228,7 +238,13 @@ def coeff_exp(k): return self._compute_coefficient_exp(v) else: return zero - return L(coeff_exp, valuation=1) + + if prec is Infinity: + L = LazyPowerSeriesRing(self._base, name) + return L(coeff_exp, valuation=1) + else: + L = PowerSeriesRing(self._base, name, default_prec=prec) + return L([0] + [coeff_exp(i) for i in range(1,prec)], prec=prec) @cached_method def _compute_coefficient_log(self, k): @@ -264,7 +280,7 @@ def _compute_coefficient_log(self, k): c += self._compute_coefficient_log(i)*self._gen[j]**(q**i) return c/(T - T**(q**k)) - def logarithm(self, name='z'): + def logarithm(self, prec=Infinity, name='z'): r""" Return the logarithm of the given Drinfeld module. @@ -275,27 +291,36 @@ def logarithm(self, name='z'): INPUT: + - ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``); + the precision at which the series is returned; if ``Infinity``, + a lazy power series in returned + - ``name`` -- string (default: ``'z'``); the name of the generator of the lazy power series ring - OUTPUT: a lazy power series over the base field - EXAMPLES:: sage: A = GF(2)['T'] sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1]) - sage: log = phi.logarithm(); log - z + ((1/(T^2+T))*z^2) + ((1/(T^6+T^5+T^3+T^2))*z^4) + O(z^8) - The logarithm is returned as a lazy power series, meaning that - any of its coefficients can be computed on demands:: + When ``prec`` is ``Infinity`` (which is the default), + the logarithm is returned as a lazy power series, meaning + that any of its coefficients can be computed on demands:: + sage: log = phi.logarithm(); log + z + ((1/(T^2+T))*z^2) + ((1/(T^6+T^5+T^3+T^2))*z^4) + O(z^8) sage: log[2^4] 1/(T^30 + T^29 + T^27 + ... + T^7 + T^5 + T^4) sage: log[2^5] 1/(T^62 + T^61 + T^59 + ... + T^8 + T^6 + T^5) + On the contrary, when ``prec`` is a finite number, all the + required coefficients are computed at once:: + + sage: phi.logarithm(prec=10) + z + (1/(T^2 + T))*z^2 + (1/(T^6 + T^5 + T^3 + T^2))*z^4 + (1/(T^14 + T^13 + T^11 + T^10 + T^7 + T^6 + T^4 + T^3))*z^8 + O(z^10) + Example in higher rank:: sage: A = GF(5)['T'] @@ -317,7 +342,6 @@ def logarithm(self, name='z'): sage: log[2**3] == -1/((T**q - T)*(T**(q**2) - T)*(T**(q**3) - T)) # expected value True """ - L = LazyPowerSeriesRing(self._base, name) q = self._Fq.cardinality() def coeff_log(k): @@ -328,7 +352,13 @@ def coeff_log(k): return self._compute_coefficient_log(v) else: return self._base.zero() - return L(coeff_log, valuation=1) + + if prec is Infinity: + L = LazyPowerSeriesRing(self._base, name) + return L(coeff_log, valuation=1) + else: + L = PowerSeriesRing(self._base, name, default_prec=prec) + return L([0] + [coeff_log(i) for i in range(1, prec)], prec=prec) @cached_method def _compute_goss_polynomial(self, n, q, poly_ring, X): From 4e37acb959bb310ebfedb100da52a30a3a7326db Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 12:23:56 +0100 Subject: [PATCH 191/507] avoid conversion in asteroidal_triples --- src/sage/graphs/asteroidal_triples.pyx | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 7c904f777f3..d2adc64ad4b 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -65,6 +65,8 @@ from cysignals.signals cimport sig_on, sig_off from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph @@ -124,6 +126,17 @@ def is_asteroidal_triple_free(G, certificate=False): Traceback (most recent call last): ... ValueError: The first parameter must be a Graph. + + The method is valid for immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.is_asteroidal_triple_free() == H.is_asteroidal_triple_free() + True """ from sage.graphs.graph import Graph if not isinstance(G, Graph): @@ -145,9 +158,16 @@ def is_asteroidal_triple_free(G, certificate=False): # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef bitset_t seen bitset_init(seen, n) @@ -168,7 +188,8 @@ def is_asteroidal_triple_free(G, certificate=False): finally: # Release memory bitset_free(seen) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) # ==> We return the result From 218d4327df62ad3905e43b08a89f543f9c62da7c Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 13:04:11 +0100 Subject: [PATCH 192/507] avoid conversion in centrality.pyx --- src/sage/graphs/centrality.pyx | 89 ++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index 6a68eab6684..522e1b3607b 100644 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -23,6 +23,8 @@ from cysignals.signals cimport sig_check from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport * from sage.libs.gmp.mpq cimport * from sage.rings.rational cimport Rational @@ -115,6 +117,19 @@ def centrality_betweenness(G, bint exact=False, bint normalize=True): {0: 0.0, 1: 0.0} sage: centrality_betweenness(Graph(2), exact=1) {0: 0, 1: 0} + + The method is valid for immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.centrality_betweenness() == H.centrality_betweenness() + True + sage: G.centrality_betweenness(exact=True) == H.centrality_betweenness(exact=True) + True """ if exact: return centrality_betweenness_C(G, 0, normalize=normalize) @@ -146,12 +161,13 @@ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True): return {v: zero for v in G} # A copy of G, for faster neighbor enumeration + cdef StaticSparseCGraph cg cdef short_digraph g # A second copy, to remember the edges used during the BFS (see doc) cdef short_digraph bfs_dag - cdef list int_to_vertex = list(G) + cdef list int_to_vertex cdef int n = G.order() @@ -178,7 +194,14 @@ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True): mpq_init(mpq_tmp) try: - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + g = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + init_reverse(bfs_dag, g) queue = check_allocarray(n, sizeof(uint32_t)) @@ -304,7 +327,8 @@ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True): mpq_clear(mpq_tmp) finally: - free_short_digraph(g) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(g) free_short_digraph(bfs_dag) bitset_free(seen) bitset_free(next_layer) @@ -667,6 +691,19 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): True sage: all(abs(topk[i][0] - sorted_centr[i]) < 1e-12 for i in range(len(topk))) True + + Immutable graphs:: + + sage: from sage.graphs.centrality import centrality_closeness_top_k + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: k = randint(1, 10) + sage: centrality_closeness_top_k(G, k) == centrality_closeness_top_k(H, k) + True """ cdef list res if k >= G.order(): @@ -684,12 +721,19 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): return [] cdef MemoryAllocator mem = MemoryAllocator() + cdef StaticSparseCGraph cg cdef short_digraph sd # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph - cdef list V = list(G) - init_short_digraph(sd, G, edge_labelled=False, vertex_list=V) + cdef list V + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + V = cg._vertex_to_labels + else: + V = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=V) cdef int n = sd.n cdef int* reachL = mem.malloc(n * sizeof(int)) cdef int* reachU @@ -824,6 +868,9 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): if verbose > 0: print("Final performance ratio: {}".format(visited / (n * (sd.neighbors[sd.n] - sd.edges)))) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) + res = [(1.0 / farness[v], V[v]) for v in topk[:k] if v != -1] try: res = sorted(res, reverse=True) @@ -884,6 +931,18 @@ def centrality_closeness_random_k(G, int k=1): Traceback (most recent call last): ... ValueError: G must be an undirected Graph + + The method is valid for immutable graphs:: + + sage: from sage.graphs.centrality import centrality_closeness_random_k + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: centrality_closeness_random_k(G, 10) == centrality_closeness_random_k(H, 10) + True """ G._scream_if_not_simple() if G.is_directed(): @@ -905,13 +964,21 @@ def centrality_closeness_random_k(G, int k=1): cdef double* partial_farness = mem.malloc(n * sizeof(double)) cdef uint32_t* distance cdef uint32_t* waiting_list + cdef StaticSparseCGraph cg cdef short_digraph sd cdef bitset_t seen cdef double farness cdef int i, j cdef dict closeness_centrality_array = {} - cdef list int_to_vertex = list(G) - cdef dict vertex_to_int = {v: i for i, v in enumerate(int_to_vertex)} + cdef list int_to_vertex + cdef dict vertex_to_int + if isinstance(G, StaticSparseBackend): + cg = G._cg + int_to_vertex = cg._vertex_to_labels + vertex_to_int = cg._vertex_to_int + else: + int_to_vertex = list(G) + vertex_to_int = {v: i for i, v in enumerate(int_to_vertex)} # Initialize for i in range(n): @@ -939,7 +1006,10 @@ def centrality_closeness_random_k(G, int k=1): # Copying the whole graph as a static_sparse_graph for fast shortest # paths computation in unweighted graph. This data structure is well # documented in module sage.graphs.base.static_sparse_graph - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + sd = cg.g + else: + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) distance = mem.malloc(n * sizeof(uint32_t)) waiting_list = mem.malloc(n * sizeof(uint32_t)) bitset_init(seen, n) @@ -955,7 +1025,8 @@ def centrality_closeness_random_k(G, int k=1): closeness_centrality_array[int_to_vertex[V[i]]] = (n - 1) / farness bitset_free(seen) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) # Estimate the closeness centrality for remaining n-k vertices. for i in range(k, n): From 8749c5ca1a7d7d7f343cf04e8496ec64f95b42a2 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 13:16:59 +0100 Subject: [PATCH 193/507] avoid conversion in convexity_properties.pyx --- src/sage/graphs/convexity_properties.pyx | 61 ++++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/convexity_properties.pyx b/src/sage/graphs/convexity_properties.pyx index f292cae3eed..3d56e47ce6e 100644 --- a/src/sage/graphs/convexity_properties.pyx +++ b/src/sage/graphs/convexity_properties.pyx @@ -40,6 +40,8 @@ from sage.graphs.distances_all_pairs cimport c_distances_all_pairs from cysignals.memory cimport sig_free from memory_allocator cimport MemoryAllocator from libc.stdint cimport uint32_t +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport (short_digraph, init_short_digraph, free_short_digraph, @@ -572,6 +574,17 @@ def geodetic_closure(G, S): Traceback (most recent call last): ... NotImplementedError: the geodetic closure of digraphs has not been implemented yet + + The method is valid for immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: geodetic_closure(G, [0, 3]) == geodetic_closure(H, [0, 3]) + True """ if G.is_directed(): raise NotImplementedError("the geodetic closure of digraphs has not been implemented yet") @@ -591,13 +604,23 @@ def geodetic_closure(G, S): cdef int n = G.order() cdef int nS = len(S) - cdef list int_to_vertex = list(G) - cdef dict vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)} - cdef list S_int = [vertex_to_int[u] for u in S] + cdef list int_to_vertex + cdef dict vertex_to_int # Copy the graph as a short digraph + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + vertex_to_int = cg._vertex_to_int + else: + int_to_vertex = list(G) + vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)} + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + + cdef list S_int = [vertex_to_int[u] for u in S] # Allocate some data structures cdef MemoryAllocator mem = MemoryAllocator() @@ -668,7 +691,8 @@ def geodetic_closure(G, S): bitset_free(seen) bitset_free(visited) bitset_free(closure) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) return ret @@ -745,6 +769,17 @@ def is_geodetic(G): sage: G.add_edge(G.random_edge()) sage: G.is_geodetic() False + + The method is valid for immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.is_geodetic() == H.is_geodetic() + True """ if G.has_multiple_edges(): return False @@ -754,15 +789,21 @@ def is_geodetic(G): # Copy the graph as a short digraph cdef int n = G.order() + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + else: + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # Allocate some data structures cdef MemoryAllocator mem = MemoryAllocator() cdef uint32_t * distances = mem.malloc(n * sizeof(uint32_t)) cdef uint32_t * waiting_list = mem.malloc(n * sizeof(uint32_t)) if not distances or not waiting_list: - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) raise MemoryError() cdef bitset_t seen bitset_init(seen, n) @@ -811,7 +852,8 @@ def is_geodetic(G): elif distances[u] == distances[v] + 1: # G is not geodetic bitset_free(seen) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) return False p_tmp += 1 @@ -820,7 +862,8 @@ def is_geodetic(G): waiting_beginning += 1 bitset_free(seen) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) # The graph is geodetic return True From a08e0c8a823c216fa849a2b3299134df5cd2b4a1 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 13:55:51 +0100 Subject: [PATCH 194/507] avoid some conversions in distances_all_pairs.pyx --- src/sage/graphs/distances_all_pairs.pyx | 62 +++++++++++++++++++------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 23d2e1c79d7..84595fe67dd 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -132,7 +132,8 @@ from memory_allocator cimport MemoryAllocator from sage.graphs.base.c_graph cimport CGraphBackend from sage.graphs.base.c_graph cimport CGraph - +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport (short_digraph, init_short_digraph, init_reverse, @@ -1934,9 +1935,16 @@ def radius_DHV(G): if n <= 1: return 0 - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint32_t source, ecc_source cdef uint32_t antipode, ecc_antipode @@ -1987,7 +1995,8 @@ def radius_DHV(G): LB = ecc_lower_bound[v] source = v # vertex with minimum eccentricity lower bound - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) bitset_free(seen) if UB == UINT32_MAX: from sage.rings.infinity import Infinity @@ -2052,8 +2061,13 @@ def wiener_index(G): # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + else: + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) # allocated some data structures cdef bitset_t seen @@ -2074,7 +2088,8 @@ def wiener_index(G): for v in range(0 if G.is_directed() else (u + 1), n): s += distances[v] - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) bitset_free(seen) if s == UINT64_MAX: @@ -2364,8 +2379,14 @@ def szeged_index(G, algorithm=None): if G.order() <= 1: return 0 + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + else: + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) + cdef uint64_t s if algorithm == "low": @@ -2373,7 +2394,8 @@ def szeged_index(G, algorithm=None): else: s = c_szeged_index_high_memory(sd) - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) return s @@ -2446,8 +2468,13 @@ def distances_distribution(G): if n <= 1: return {} + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + else: + init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G)) cdef MemoryAllocator mem = MemoryAllocator() cdef uint32_t * distances = mem.allocarray(2 * n, sizeof(uint32_t)) @@ -2470,7 +2497,8 @@ def distances_distribution(G): for v in range(n): count[distances[v]] += 1 - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) bitset_free(seen) from sage.rings.infinity import Infinity @@ -2577,9 +2605,16 @@ def antipodal_graph(G): A.add_edges(itertools.product(c1, c2)) return A - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef MemoryAllocator mem = MemoryAllocator() cdef uint32_t * distances = mem.allocarray(2 * n, sizeof(uint32_t)) @@ -2607,7 +2642,8 @@ def antipodal_graph(G): A.add_edge(u, int_to_vertex[vj]) j -= 1 - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) bitset_free(seen) A.add_vertices(G) From 8389d723f8cf7468cf535b403d73e3c566f2b06d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 28 Dec 2024 14:34:16 +0100 Subject: [PATCH 195/507] class polynomial and Taelman exponential units --- .../charzero_drinfeld_module.py | 183 ++++++++++++++++++ .../drinfeld_modules/drinfeld_module.py | 11 +- src/sage/rings/ring_extension_element.pyx | 40 ++++ 3 files changed, 233 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 6142ec2fa2d..a8cf1e9a2c5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -24,8 +24,12 @@ from .drinfeld_module import DrinfeldModule +from sage.functions.other import ceil from sage.rings.integer_ring import ZZ +from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector + from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import @@ -414,3 +418,182 @@ def goss_polynomial(self, n, var='X'): X = poly_ring.gen() q = self._Fq.cardinality() return self._compute_goss_polynomial(n, q, poly_ring, X) + + +class DrinfeldModule_rational(DrinfeldModule_charzero): + """ + A class for Drinfeld modules defined over the fraction + field of the underlying function field + """ + def _phiT_matrix(self, polynomial_part): + r""" + Return the matrix of `\phi_T` modulo `\pi^s` where `s` is + chosen such that `\pi^s` is in the domain of convergence + of the logarithm. + + It is an helper function; do not call it directly. + """ + A = self.function_ring() + Fq = A.base_ring() + q = Fq.cardinality() + r = self.rank() + + gs = [] + for g in self.coefficients(sparse=False): + g = g.backend(force=True) + if g.denominator().is_one(): + gs.append(A(g.numerator().list())) + else: + raise ValueError("the Drinfeld module must have polynomial coefficients") + s = max(ceil(gs[i].degree() / (q**i - 1)) for i in range(1, r+1)) - 1 + if s < 0: + s = 0 + + M = matrix(Fq, s) + if polynomial_part: + P = vector(A, s) + qk = 1 + for k in range(r+1): + for i in range(s): + e = (i+1)*qk + if polynomial_part: + P[i] += gs[k] >> e + for j in range(s): + e -= 1 + if e < 0: + break + M[i, j] += gs[k][e] + qk *= q + + if polynomial_part: + return M, P + else: + return M + + def class_polynomial(self): + r""" + Return the class polynomial, that is the Fitting ideal + of the class module, of this Drinfeld module. + + EXAMPLES: + + We check that the class module of the Carlitz module + is trivial:: + + sage: q = 5 + sage: Fq = GF(q) + sage: A = Fq['T'] + sage: K. = Frac(A) + sage: C = DrinfeldModule(A, [T, 1]); C + Drinfeld module defined by T |--> t + T + sage: C.class_polynomial() + 1 + + When the coefficients of the Drinfeld module have small + enough degrees, the class module is always trivial:: + + sage: r = 4 + sage: phi = DrinfeldModule(A, [T] + [A.random_element(degree=q**i) for i in range(1, r+1)]) + sage: phi.class_polynomial() + 1 + + Here is an example with a nontrivial class module:: + + sage: phi = DrinfeldModule(A, [T, -T^(2*q-1) + 2*T^(q-1)]) + sage: phi.class_polynomial() + T + 3 + """ + A = self.function_ring() + Fq = A.base_ring() + M = self._phiT_matrix(False) + s = M.nrows() + if s == 0: + # self is small + return A.one() + + v = vector(Fq, s) + v[s-1] = 1 + vs = [v] + for i in range(s-1): + v = v*M + vs.append(v) + V = matrix(vs) + V.echelonize() + + dim = V.rank() + pivots = V.pivots() + j = ip = 0 + for i in range(dim, s): + while ip < dim and j == pivots[ip]: + j += 1 + ip += 1 + V[i,j] = 1 + + N = (V * M * ~V).submatrix(dim, dim) + return A(N.charpoly()) + + def taelman_exponential_unit(self): + r""" + Return the exponential of the fundamental Taelman unit. + + EXAMPLES: + + The Taelman exponential unit of The Carlitz module is `1`:: + + sage: q = 7 + sage: Fq = GF(q) + sage: A = Fq['T'] + sage: K. = Frac(A) + sage: C = DrinfeldModule(A, [T, 1]); C + Drinfeld module defined by T |--> t + T + sage: C.taelman_exponential_unit() + 1 + + The same occurs more generally when the coefficients of the + Drinfeld module have small enough degrees:: + + sage: r = 4 + sage: phi = DrinfeldModule(A, [T] + [A.random_element(degree=q**i) for i in range(1, r+1)]) + sage: phi.taelman_exponential_unit() + 1 + + Usually, as soon as we leave the world of small Drinfeld modules, + Taelman's exponential units are highly non trivial:: + + sage: phi = DrinfeldModule(A, [T, T^(2*q+1), T^3]) + sage: phi.taelman_exponential_unit() + T^52 + T^22 + T^8 + T^2 + 1 + """ + A = self.function_ring() + Fq = A.base_ring() + q = Fq.cardinality() + M, P = self._phiT_matrix(True) + s = M.nrows() + if s == 0: + # self is small + return A(1) + + gs = self.coefficients(sparse=False) + v = vector(Fq, s) + v[s-1] = 1 + p = A.zero() + vs = [v] + ps = [p] + for i in range(s): + pq = p + p = v * P + for j in range(len(gs) - 1): + p += gs[j] * pq + pq = pq ** q + p += gs[-1] * pq + v = v * M + vs.append(v) + ps.append(p) + vs.reverse() + ps.reverse() + V = matrix(vs) + + unit = V.left_kernel().basis()[0] + expunit = sum(unit[i]*ps[i] for i in range(s+1)) + expunit /= expunit.numerator().leading_coefficient() + return expunit diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 0b8d4cd32ff..f65b6753831 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -37,6 +37,7 @@ from sage.misc.misc_c import prod from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.rings.fraction_field import FractionField_generic from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic from sage.structure.parent import Parent @@ -621,9 +622,17 @@ def __classcall_private__(cls, function_ring, gen, name='t'): raise ValueError('generator must have positive degree') # Instantiate the appropriate class: - if base_field.is_finite(): + backend = base_field.backend(force=True) + if backend.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import DrinfeldModule_finite return DrinfeldModule_finite(gen, category) + if isinstance(backend, FractionField_generic): + ring = backend.ring() + if (isinstance(ring, PolynomialRing_generic) + and ring.base_ring() is function_ring_base + and base_morphism(T) == ring.gen()): + from .charzero_drinfeld_module import DrinfeldModule_rational + return DrinfeldModule_rational(gen, category) if not category._characteristic: from .charzero_drinfeld_module import DrinfeldModule_charzero return DrinfeldModule_charzero(gen, category) diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index 600b1d1a62e..d74ed04ac26 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -129,6 +129,46 @@ cdef class RingExtensionElement(CommutativeAlgebraElement): wrapper.__doc__ = method.__doc__ return wrapper + def __getitem__(self, i): + r""" + Return the `i`-th item of this element. + + This methods calls the appropriate method of the backend if + ``import_methods`` is set to ``True`` + + EXAMPLES:: + + sage: R. = QQ[] + sage: E = R.over() + sage: P = E(x^2 + 2*x + 3) + sage: P[0] + 3 + """ + if (self._parent)._import_methods: + output = self._backend[to_backend(i)] + return from_backend(output, self._parent) + return TypeError("this element is not subscriptable") + + def __call__(self, *args, **kwargs): + r""" + Call this element. + + This methods calls the appropriate method of the backend if + ``import_methods`` is set to ``True`` + + EXAMPLES:: + + sage: R. = QQ[] + sage: E = R.over() + sage: P = E(x^2 + 2*x + 3) + sage: P(1) + 6 + """ + if (self._parent)._import_methods: + output = self._backend(*to_backend(args), **to_backend(kwargs)) + return from_backend(output, self._parent) + return TypeError("this element is not callable") + def __dir__(self): """ Return the list of all the attributes of this element; From 7a808ca45d851a223a0d3f77e9d93a2937e40bad Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 14:35:53 +0100 Subject: [PATCH 196/507] avoid some conversions in generic_graph_pyx.pyx --- .../graphs/base/static_sparse_backend.pyx | 2 + src/sage/graphs/generic_graph_pyx.pyx | 41 +++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index e51fc238ac5..f81713a8618 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -124,6 +124,8 @@ cdef class StaticSparseCGraph(CGraph): self.number_of_loops = check_calloc(self.g.n, sizeof(int)) except MemoryError: free_short_digraph(self.g) + if self._directed: + free_short_digraph(self.g_rev) raise for i in range(self.g.n): for tmp in range(out_degree(self.g, i)): diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 9f8b90d79ab..c8ecfa18469 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -32,6 +32,8 @@ from memory_allocator cimport MemoryAllocator from sage.cpython.string cimport char_to_str from sage.libs.gmp.mpz cimport * from sage.misc.prandom import random +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport init_reverse @@ -1327,6 +1329,18 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, sage: b, C = fh(G, find_path=False) sage: b, len(C) (True, 4) + + Immutable graphs:: + + sage: G = graphs.PetersenGraph() + sage: H = Graph(G, immutable=True) + sage: fh(H) + (False, [7, 5, 0, 1, 2, 3, 8, 6, 9, 4]) + sage: fh(H, find_path=True) + (True, [5, 0, 1, 6, 8, 3, 2, 7, 9, 4]) + sage: G = DiGraph([(0, 1), (1, 2), (2, 3)], immutable=True) + sage: fh(G) + (False, [0, 1, 2, 3]) """ G._scream_if_not_simple() @@ -1372,13 +1386,23 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, memset(member, 0, n * sizeof(int)) # static copy of the graph for more efficient operations - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) cdef short_digraph rev_sd cdef bint reverse = False if directed: - init_reverse(rev_sd, sd) + if isinstance(G, StaticSparseBackend) and cg._directed: + rev_sd = cg.g_rev + else: + init_reverse(rev_sd, sd) # A list to store the available vertices at each step cdef list available_vertices = [] @@ -1518,9 +1542,11 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, if bigcount * reset_bound > max_iter: output = [int_to_vertex[longest_path[i]] for i in range(longest)] - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) if directed: - free_short_digraph(rev_sd) + if not (isinstance(G, StaticSparseBackend) and cg._directed): + free_short_digraph(rev_sd) if longest_reversed: return (False, output[::-1]) return (False, output) @@ -1551,8 +1577,9 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, f"{int_to_vertex[path[0]]} are not adjacent") output = [int_to_vertex[path[i]] for i in range(length)] - free_short_digraph(sd) - if directed: + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) + if directed and not (isinstance(G, StaticSparseBackend) and cg._directed): free_short_digraph(rev_sd) return (True, output) From 50e32ae236c295b31f23332da05a875f625a8529 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 14:49:13 +0100 Subject: [PATCH 197/507] avoid some conversions in clique_separators.pyx --- .../clique_separators.pyx | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 3cb10805b8a..b58ed2104aa 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -25,6 +25,8 @@ from libc.stdint cimport uint32_t from cysignals.signals cimport sig_on, sig_off, sig_check from memory_allocator cimport MemoryAllocator +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph @@ -413,6 +415,17 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal {1} _{}_ / / {3} {2} + + Immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.atoms_and_clique_separators() == H.atoms_and_clique_separators() + True """ cdef list A = [] # atoms cdef list Sh = [] # separators @@ -452,13 +465,20 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal return A, Sc cdef int N = G.order() - cdef list int_to_vertex = list(G) # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + sd = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) # variables for the manipulation of the short digraph cdef uint32_t** p_vertices = sd.neighbors @@ -574,7 +594,8 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal for u in Cint: active[u] = False - free_short_digraph(sd) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(sd) H.clear() # We add the last atom From 39ae7511930376feb2b77f462af3aaa537e6f1ce Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 15:06:30 +0100 Subject: [PATCH 198/507] avoid some conversions in isoperimetric_inequalities.pyx --- .../graphs/isoperimetric_inequalities.pyx | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/isoperimetric_inequalities.pyx b/src/sage/graphs/isoperimetric_inequalities.pyx index 731cfc00040..2fa715a102a 100644 --- a/src/sage/graphs/isoperimetric_inequalities.pyx +++ b/src/sage/graphs/isoperimetric_inequalities.pyx @@ -23,9 +23,11 @@ Authors: from cysignals.signals cimport sig_on, sig_off from cysignals.memory cimport check_malloc, sig_free -from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph from sage.data_structures.binary_matrix cimport * from sage.graphs.base.static_dense_graph cimport dense_graph_init +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend +from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph from sage.rings.infinity import Infinity from sage.rings.rational_field import QQ @@ -88,6 +90,17 @@ def cheeger_constant(g): Traceback (most recent call last): ... ValueError: Cheeger constant is not defined for the empty graph + + Immutable graph:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.cheeger_constant() == H.cheeger_constant() + True """ if g.is_directed(): raise ValueError("Cheeger constant is only defined on non-oriented graph") @@ -99,6 +112,7 @@ def cheeger_constant(g): elif not g.is_connected(): return QQ((0, 1)) + cdef StaticSparseCGraph cg cdef short_digraph sd # a copy of the graph g cdef int * subgraph # vertices of the subgraph (stack) cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in)) @@ -110,7 +124,11 @@ def cheeger_constant(g): cdef unsigned long vmin = 1 # value of the volume for the min cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) + if isinstance(g, StaticSparseBackend): + cg = g._cg + sd = cg.g + else: + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) subgraph = check_malloc(sd.n * sizeof(int)) bitsubgraph = check_malloc(sd.n * sizeof(int)) @@ -170,7 +188,8 @@ def cheeger_constant(g): return QQ((bmin, vmin)) finally: - free_short_digraph(sd) + if not isinstance(g, StaticSparseBackend): + free_short_digraph(sd) sig_free(subgraph) sig_free(bitsubgraph) sig_off() @@ -224,6 +243,17 @@ def edge_isoperimetric_number(g): Traceback (most recent call last): ... ValueError: edge-isoperimetric number not defined for the empty graph + + Immutable graph:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.edge_isoperimetric_number() == H.edge_isoperimetric_number() + True """ if g.is_directed(): raise ValueError("edge isoperimetric number is only defined on non-oriented graph") @@ -235,6 +265,7 @@ def edge_isoperimetric_number(g): elif not g.is_connected(): return QQ((0, 1)) + cdef StaticSparseCGraph cg cdef short_digraph sd # a copy of the graph g cdef int * subgraph # vertices of the subgraph (stack) cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in)) @@ -244,7 +275,11 @@ def edge_isoperimetric_number(g): cdef int u = 0 # current vertex cdef int i - init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) + if isinstance(g, StaticSparseBackend): + cg = g._cg + sd = cg.g + else: + init_short_digraph(sd, g, edge_labelled=False, vertex_list=list(g)) cdef unsigned long bmin = sd.neighbors[1] - sd.neighbors[0] # value of boundary for the min cdef unsigned long vmin = 1 # value of the volume for the min @@ -310,7 +345,8 @@ def edge_isoperimetric_number(g): finally: sig_off() - free_short_digraph(sd) + if not isinstance(g, StaticSparseBackend): + free_short_digraph(sd) sig_free(subgraph) sig_free(bitsubgraph) From c11caf815170a954aea74a6b4027bdc261b1015e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 15:15:55 +0100 Subject: [PATCH 199/507] avoid some conversions in weakly_chordal.pyx --- src/sage/graphs/weakly_chordal.pyx | 58 +++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index 37961e273e5..e16a1445f3d 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -37,6 +37,8 @@ Methods from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * +from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph +from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph @@ -199,6 +201,17 @@ def is_long_hole_free(g, certificate=False): sage: graphs.EmptyGraph().is_long_hole_free() True + + Immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.is_long_hole_free() == H.is_long_hole_free() + True """ g._scream_if_not_simple() @@ -211,9 +224,16 @@ def is_long_hole_free(g, certificate=False): # documented in the module sage.graphs.base.static_sparse_graph. # Vertices are relabeled in 0..n-1 cdef int n = g.order() - cdef list id_label = list(g) + cdef list id_label + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) + if isinstance(g, StaticSparseBackend): + cg = g._cg + sd = cg.g + id_label = cg._vertex_to_labels + else: + id_label = list(g) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) # Make a dense copy of the graph for quick adjacency tests cdef bitset_t dense_graph @@ -257,7 +277,8 @@ def is_long_hole_free(g, certificate=False): if not res: # We release memory before returning the result - free_short_digraph(sd) + if isinstance(g, StaticSparseBackend): + free_short_digraph(sd) bitset_free(dense_graph) if certificate: @@ -268,7 +289,8 @@ def is_long_hole_free(g, certificate=False): InPath[u] = -1 # Release memory - free_short_digraph(sd) + if not isinstance(g, StaticSparseBackend): + free_short_digraph(sd) bitset_free(dense_graph) if certificate: @@ -427,6 +449,17 @@ def is_long_antihole_free(g, certificate=False): sage: graphs.EmptyGraph().is_long_hole_free() True + + Immutable graphs:: + + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: G.is_long_antihole_free() == H.is_long_antihole_free() + True """ g._scream_if_not_simple() @@ -439,9 +472,16 @@ def is_long_antihole_free(g, certificate=False): # documented in the module sage.graphs.base.static_sparse_graph. # Vertices are relabeled in 0..n-1 cdef int n = g.order() - cdef list id_label = list(g) + cdef list id_label + cdef StaticSparseCGraph cg cdef short_digraph sd - init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) + if isinstance(g, StaticSparseBackend): + cg = g._cg + sd = cg.g + id_label = cg._vertex_to_labels + else: + id_label = list(g) + init_short_digraph(sd, g, edge_labelled=False, vertex_list=id_label) # Make a dense copy of the graph for quick adjacency tests cdef bitset_t dense_graph @@ -486,7 +526,8 @@ def is_long_antihole_free(g, certificate=False): if not res: # We release memory before returning the result - free_short_digraph(sd) + if isinstance(g, StaticSparseBackend): + free_short_digraph(sd) bitset_free(dense_graph) if certificate: @@ -497,7 +538,8 @@ def is_long_antihole_free(g, certificate=False): InPath[u] = -1 # Release memory - free_short_digraph(sd) + if not isinstance(g, StaticSparseBackend): + free_short_digraph(sd) bitset_free(dense_graph) if certificate: From d89a6bafde190bcb346b98f5fd804e35899a8a69 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 28 Dec 2024 16:20:29 +0100 Subject: [PATCH 200/507] avoid some conversions in static_sparse_graph.pyx --- src/sage/graphs/base/static_sparse_graph.pyx | 98 +++++++++++++++++--- src/sage/graphs/centrality.pyx | 2 + 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index c27f0d9a8cc..71299081802 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -802,21 +802,43 @@ def tarjan_strongly_connected_components(G): ....: s2 = Set(map(Set,scc2)) ....: if s1 != s2: ....: print("Ooch !") + + Immutable digraphs:: + + sage: from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components + sage: G = digraphs.RandomDirectedGNP(10, .4) + sage: G._backend + + sage: H = DiGraph(G, immutable=True) + sage: H._backend + + sage: tarjan_strongly_connected_components(G) == tarjan_strongly_connected_components(H) + True """ from sage.graphs.digraph import DiGraph if not isinstance(G, DiGraph): raise ValueError("G must be a DiGraph.") - cdef MemoryAllocator mem = MemoryAllocator() - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + g = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + + cdef MemoryAllocator mem = MemoryAllocator() cdef int * scc = mem.malloc(g.n * sizeof(int)) sig_on() cdef int nscc = tarjan_strongly_connected_components_C(g, scc) sig_off() - free_short_digraph(g) + + if not isinstance(G, StaticSparseBackend): + free_short_digraph(g) cdef int i cdef list output = [[] for i in range(nscc)] @@ -872,6 +894,7 @@ cdef void strongly_connected_components_digraph_C(short_digraph g, int nscc, int output.n = nscc output.m = m + output.edge_labels = NULL output.neighbors = check_allocarray((1+output.n), sizeof(uint32_t *)) @@ -920,15 +943,37 @@ def strongly_connected_components_digraph(G): ....: for e in g.edges(sort=False): ....: assert(sccs[e[0]]==sccs[e[1]] or scc_digraph.has_edge(sccs[e[0]],sccs[e[1]])) ....: assert(sccs[e[0]] >= sccs[e[1]]) + + Immutable digraphs:: + + sage: from sage.graphs.base.static_sparse_graph import strongly_connected_components_digraph + sage: G = digraphs.RandomDirectedGNP(10, .4) + sage: G._backend + + sage: H = DiGraph(G, immutable=True) + sage: H._backend + + sage: A = strongly_connected_components_digraph(G)[0] + sage: B = strongly_connected_components_digraph(H)[0] + sage: A.is_isomorphic(B) + True """ from sage.graphs.digraph import DiGraph if not isinstance(G, DiGraph): raise ValueError("G must be a DiGraph.") - cdef MemoryAllocator mem = MemoryAllocator() - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph g, scc_g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + g = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + + cdef MemoryAllocator mem = MemoryAllocator() cdef int * scc = mem.malloc(g.n * sizeof(int)) cdef int i, j, nscc cdef list edges = [] @@ -944,7 +989,11 @@ def strongly_connected_components_digraph(G): edges.append((i, scc_g.neighbors[i][j])) output.add_edges(edges) sig_off() - free_short_digraph(g) + + if not isinstance(G, StaticSparseBackend): + free_short_digraph(g) + free_short_digraph(scc_g) + return output, {v: scc[i] for i, v in enumerate(int_to_vertex)} @@ -968,7 +1017,8 @@ cdef void free_short_digraph(short_digraph g) noexcept: """ sig_free(g.edges) sig_free(g.neighbors) - cpython.Py_XDECREF(g.edge_labels) + if g.edge_labels != NULL: + cpython.Py_XDECREF(g.edge_labels) def triangles_count(G): @@ -986,14 +1036,35 @@ def triangles_count(G): {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0} sage: sum(triangles_count(graphs.CompleteGraph(15)).values()) == 3*binomial(15,3) # needs sage.symbolic True + + TESTS: + + Immutable graphs:: + + sage: from sage.graphs.base.static_sparse_graph import triangles_count + sage: G = graphs.RandomGNP(10, .7) + sage: G._backend + + sage: H = Graph(G, immutable=True) + sage: H._backend + + sage: triangles_count(G) == triangles_count(H) + True """ from sage.rings.integer import Integer G._scream_if_not_simple() # g is a copy of G. If G is internally a static sparse graph, we use it. - cdef list int_to_vertex = list(G) + cdef list int_to_vertex + cdef StaticSparseCGraph cg cdef short_digraph g - init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) + if isinstance(G, StaticSparseBackend): + cg = G._cg + g = cg.g + int_to_vertex = cg._vertex_to_labels + else: + int_to_vertex = list(G) + init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex) cdef uint64_t * count = check_calloc(G.order(), sizeof(uint64_t)) @@ -1027,7 +1098,8 @@ def triangles_count(G): ans = {w: Integer(count[i] // 2) for i, w in enumerate(int_to_vertex)} - free_short_digraph(g) + if not isinstance(G, StaticSparseBackend): + free_short_digraph(g) sig_free(count) return ans @@ -1111,7 +1183,7 @@ def spectral_radius(G, prec=1e-10): sage: while not G.is_strongly_connected(): ....: shuffle(r) ....: G.add_edges(enumerate(r), loops=False) - sage: spectral_radius(G, 1e-10) # random + sage: spectral_radius(G, 1e-10) # random # long time (1.9997956006500042, 1.9998043797692782) The algorithm takes care of multiple edges:: diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index 522e1b3607b..5ab37041823 100644 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -482,6 +482,8 @@ cdef void _estimate_reachable_vertices_dir(short_digraph g, int* reachL, int* re reachL[i] = reachL_scc[scc[i]] reachU[i] = min(reachU_scc[scc[i]], g.n) + free_short_digraph(sccgraph) + cdef void _compute_reachable_vertices_undir(short_digraph g, int* reachable) noexcept: r""" From f0aed91e051899c6b0da734734c6ed339284dc3b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 07:47:13 +0100 Subject: [PATCH 201/507] more doctests --- src/doc/en/reference/references/index.rst | 3 + .../charzero_drinfeld_module.py | 71 ++++++++++++++++--- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ca81cda3f75..42468429656 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -6473,6 +6473,9 @@ REFERENCES: **T** +.. [Tae2012] Lenny Taelman, *Special L-values of Drinfeld modules*, + Ann. Math. 175 (1), 2012, 369–391 + .. [Tak1999] Kisao Takeuchi, Totally real algebraic number fields of degree 9 with small discriminant, Saitama Math. J. 17 (1999), 63--85 (2000). diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index a8cf1e9a2c5..b9c79246a12 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -2,14 +2,16 @@ r""" Drinfeld modules over rings of characteristic zero -This module provides the class +This module provides the classes :class:`sage.rings.function_fields.drinfeld_module.charzero_drinfeld_module.DrinfeldModule_charzero`, +:class:`sage.rings.function_fields.drinfeld_module.charzero_drinfeld_module.DrinfeldModule_rational`, which inherits :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. AUTHORS: - David Ayotte (2023-09) +- Xavier Caruso (2024-12) - computation of class polynomials and Taelman's units """ # ***************************************************************************** @@ -427,11 +429,45 @@ class DrinfeldModule_rational(DrinfeldModule_charzero): """ def _phiT_matrix(self, polynomial_part): r""" - Return the matrix of `\phi_T` modulo `\pi^s` where `s` is - chosen such that `\pi^s` is in the domain of convergence - of the logarithm. + Return the matrix giving the action of `\phi_T` modulo `u^s` + where `u = 1/T` is the uniformizer at infinity `s` is chosen + such that `u^s` is in the domain of convergence of the logarithm. It is an helper function; do not call it directly. + + INPUT: + + - ``polynomial_part`` -- boolean; if ``False``, omit the + part with negative powers of `u`; if ``True``, return this + part as a polynomial vector in `T` + + TESTS:: + + sage: q = 5 + sage: Fq = GF(q) + sage: A = Fq['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^20]) + sage: phi._phiT_matrix(False) + [0 0 0 0] + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + sage: phi._phiT_matrix(True) + ( + [0 0 0 0] + [1 0 0 0] + [0 1 0 0] + [0 0 1 0], (T^15 + 1, T^10, T^5, 1) + ) + + :: + + sage: psi = DrinfeldModule(A, [T, 1/T]) + sage: psi._phiT_matrix(False) + Traceback (most recent call last): + ... + ValueError: the Drinfeld module must have polynomial coefficients """ A = self.function_ring() Fq = A.base_ring() @@ -502,13 +538,23 @@ def class_polynomial(self): sage: phi = DrinfeldModule(A, [T, -T^(2*q-1) + 2*T^(q-1)]) sage: phi.class_polynomial() T + 3 + + TESTS: + + The Drinfeld module must have polynomial coefficients:: + + sage: phi = DrinfeldModule(A, [T, 1/T]) + sage: phi.class_polynomial() + Traceback (most recent call last): + ... + ValueError: the Drinfeld module must have polynomial coefficients """ A = self.function_ring() Fq = A.base_ring() M = self._phiT_matrix(False) s = M.nrows() if s == 0: - # self is small + # small case return A.one() v = vector(Fq, s) @@ -534,7 +580,16 @@ def class_polynomial(self): def taelman_exponential_unit(self): r""" - Return the exponential of the fundamental Taelman unit. + Return the exponential of a fundamental Taelman's unit + of this Drinfeld module. + + A Taelman's unit is by definition an element `x \in + \FF_q((1/T))` whose exponential falls in `\FF_q[T]`. + + Taelman's units form a `\FF_q[T]`-line in `\FF_q((1/T))`; + a fundamental unit is by definition a generator of this line. + + We refer to [Tae2012]_ for more details about this construction. EXAMPLES: @@ -570,8 +625,8 @@ def taelman_exponential_unit(self): M, P = self._phiT_matrix(True) s = M.nrows() if s == 0: - # self is small - return A(1) + # small case + return self.base().one() gs = self.coefficients(sparse=False) v = vector(Fq, s) From f2f9c100036b309eeab5283f3686c0e020e2eb18 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 07:51:21 +0100 Subject: [PATCH 202/507] small fix --- .../charzero_drinfeld_module.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index b9c79246a12..b7dc1c6d910 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -26,7 +26,6 @@ from .drinfeld_module import DrinfeldModule -from sage.functions.other import ceil from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix @@ -449,18 +448,19 @@ def _phiT_matrix(self, polynomial_part): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T^20]) sage: phi._phiT_matrix(False) - [0 0 0 0] - [1 0 0 0] - [0 1 0 0] - [0 0 1 0] + [0 0 0 0 0] + [1 0 0 0 0] + [0 1 0 0 0] + [0 0 1 0 0] + [0 0 0 1 1] sage: phi._phiT_matrix(True) ( - [0 0 0 0] - [1 0 0 0] - [0 1 0 0] - [0 0 1 0], (T^15 + 1, T^10, T^5, 1) + [0 0 0 0 0] + [1 0 0 0 0] + [0 1 0 0 0] + [0 0 1 0 0] + [0 0 0 1 1], (T^15 + 1, T^10, T^5, 1, 0) ) - :: sage: psi = DrinfeldModule(A, [T, 1/T]) @@ -481,9 +481,7 @@ def _phiT_matrix(self, polynomial_part): gs.append(A(g.numerator().list())) else: raise ValueError("the Drinfeld module must have polynomial coefficients") - s = max(ceil(gs[i].degree() / (q**i - 1)) for i in range(1, r+1)) - 1 - if s < 0: - s = 0 + s = max(gs[i].degree() // (q**i - 1) for i in range(1, r+1)) M = matrix(Fq, s) if polynomial_part: From 98f852aeb938d9cc8d9619aeb2c93750f0baec5a Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 07:56:40 +0100 Subject: [PATCH 203/507] fix lint --- .../drinfeld_modules/charzero_drinfeld_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index b7dc1c6d910..aba96365c02 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -440,7 +440,7 @@ def _phiT_matrix(self, polynomial_part): part with negative powers of `u`; if ``True``, return this part as a polynomial vector in `T` - TESTS:: + EXAMPLES:: sage: q = 5 sage: Fq = GF(q) @@ -461,6 +461,7 @@ def _phiT_matrix(self, polynomial_part): [0 0 1 0 0] [0 0 0 1 1], (T^15 + 1, T^10, T^5, 1, 0) ) + :: sage: psi = DrinfeldModule(A, [T, 1/T]) From 988ae0a6556a41db1a03021396cb39d06ee54444 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 08:12:00 +0100 Subject: [PATCH 204/507] avoid division by zero --- .../charzero_drinfeld_module.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index aba96365c02..2349d8c8de8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -3,9 +3,9 @@ Drinfeld modules over rings of characteristic zero This module provides the classes -:class:`sage.rings.function_fields.drinfeld_module.charzero_drinfeld_module.DrinfeldModule_charzero`, +:class:`sage.rings.function_fields.drinfeld_module.charzero_drinfeld_module.DrinfeldModule_charzero` and :class:`sage.rings.function_fields.drinfeld_module.charzero_drinfeld_module.DrinfeldModule_rational`, -which inherits +which both inherit :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. AUTHORS: @@ -527,8 +527,9 @@ def class_polynomial(self): When the coefficients of the Drinfeld module have small enough degrees, the class module is always trivial:: - sage: r = 4 - sage: phi = DrinfeldModule(A, [T] + [A.random_element(degree=q**i) for i in range(1, r+1)]) + sage: gs = [T] + [A.random_element(degree = q^i) + ....: for i in range(1, 5)] + sage: phi = DrinfeldModule(A, gs) sage: phi.class_polynomial() 1 @@ -606,8 +607,9 @@ def taelman_exponential_unit(self): The same occurs more generally when the coefficients of the Drinfeld module have small enough degrees:: - sage: r = 4 - sage: phi = DrinfeldModule(A, [T] + [A.random_element(degree=q**i) for i in range(1, r+1)]) + sage: gs = [T] + [A.random_element(degree = q^i - 1) + ....: for i in range(1, 5)] + sage: phi = DrinfeldModule(A, gs) sage: phi.taelman_exponential_unit() 1 @@ -649,5 +651,6 @@ def taelman_exponential_unit(self): unit = V.left_kernel().basis()[0] expunit = sum(unit[i]*ps[i] for i in range(s+1)) - expunit /= expunit.numerator().leading_coefficient() + if expunit: + expunit /= expunit.numerator().leading_coefficient() return expunit From eb19696b269e21bacd7ba3dc00660330dd8b98e1 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 12:03:00 +0100 Subject: [PATCH 205/507] fix lint --- src/sage/rings/ring_extension_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index d74ed04ac26..39916ab26f8 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -167,7 +167,7 @@ cdef class RingExtensionElement(CommutativeAlgebraElement): if (self._parent)._import_methods: output = self._backend(*to_backend(args), **to_backend(kwargs)) return from_backend(output, self._parent) - return TypeError("this element is not callable") + return TypeError("this element is not callable") def __dir__(self): """ From 421bbe5ee14d1dcb64e4eba74d8b2e88fc0a19df Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 29 Dec 2024 12:11:01 +0100 Subject: [PATCH 206/507] fix documentation (?) --- .../drinfeld_modules/charzero_drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 2349d8c8de8..ba71f2b5086 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -584,9 +584,9 @@ def taelman_exponential_unit(self): of this Drinfeld module. A Taelman's unit is by definition an element `x \in - \FF_q((1/T))` whose exponential falls in `\FF_q[T]`. + \mathbb F_q((1/T))` whose exponential falls in `\mathbb F_q[T]`. - Taelman's units form a `\FF_q[T]`-line in `\FF_q((1/T))`; + Taelman's units form a `\mathbb F_q[T]`-line in `\mathbb F_q((1/T))`; a fundamental unit is by definition a generator of this line. We refer to [Tae2012]_ for more details about this construction. From 21f46a64e06927b49a9ac3ea6e951384b362b8e5 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jan 2025 08:52:04 +0100 Subject: [PATCH 207/507] remove computation of Taelman's unit (will be for another PR) --- .../charzero_drinfeld_module.py | 240 ++++++++---------- 1 file changed, 101 insertions(+), 139 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index ba71f2b5086..f34b2ebbe8d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -424,86 +424,109 @@ def goss_polynomial(self, n, var='X'): class DrinfeldModule_rational(DrinfeldModule_charzero): """ A class for Drinfeld modules defined over the fraction - field of the underlying function field + field of the underlying function field. + + TESTS:: + + sage: q = 9 + sage: Fq = GF(q) + sage: A = Fq['T'] + sage: K. = Frac(A) + sage: C = DrinfeldModule(A, [T, 1]); C + Drinfeld module defined by T |--> t + T + sage: type(C) + """ - def _phiT_matrix(self, polynomial_part): + def coefficient_in_function_ring(self, n): r""" - Return the matrix giving the action of `\phi_T` modulo `u^s` - where `u = 1/T` is the uniformizer at infinity `s` is chosen - such that `u^s` is in the domain of convergence of the logarithm. - - It is an helper function; do not call it directly. + Return the `n`-th coefficient of this Drinfeld module as + an element of the underlying function ring. INPUT: - - ``polynomial_part`` -- boolean; if ``False``, omit the - part with negative powers of `u`; if ``True``, return this - part as a polynomial vector in `T` + - ``n`` -- an integer EXAMPLES:: sage: q = 5 sage: Fq = GF(q) sage: A = Fq['T'] - sage: K. = Frac(A) - sage: phi = DrinfeldModule(A, [T, T^20]) - sage: phi._phiT_matrix(False) - [0 0 0 0 0] - [1 0 0 0 0] - [0 1 0 0 0] - [0 0 1 0 0] - [0 0 0 1 1] - sage: phi._phiT_matrix(True) - ( - [0 0 0 0 0] - [1 0 0 0 0] - [0 1 0 0 0] - [0 0 1 0 0] - [0 0 0 1 1], (T^15 + 1, T^10, T^5, 1, 0) - ) - - :: - - sage: psi = DrinfeldModule(A, [T, 1/T]) - sage: psi._phiT_matrix(False) + sage: R = Fq['U'] + sage: K. = Frac(R) + sage: phi = DrinfeldModule(A, [U, 0, U^2, U^3]) + sage: phi.coefficient_in_function_ring(2) + T^2 + + Compare with the method meth:`coefficient`:: + + sage: phi.coefficient(2) + U^2 + + If the required coefficient is not a polynomials, + an error is raised:: + + sage: psi = DrinfeldModule(A, [U, 1/U]) + sage: psi.coefficient_in_function_ring(0) + T + sage: psi.coefficient_in_function_ring(1) Traceback (most recent call last): ... - ValueError: the Drinfeld module must have polynomial coefficients + ValueError: coefficient is not polynomial """ A = self.function_ring() - Fq = A.base_ring() - q = Fq.cardinality() - r = self.rank() + g = self.coefficient(n) + g = g.backend(force=True) + if g.denominator().is_one(): + return A(g.numerator().list()) + else: + raise ValueError("coefficient is not polynomial") + + def coefficients_in_function_ring(self, sparse=True): + r""" + Return the coefficients of this Drinfeld module as elements + of the underlying function ring. + + INPUT: + + - ``sparse`` -- a boolean (default: ``True``); if ``True``, + only return the nonzero coefficients; otherwise, return + all of them. + + EXAMPLES:: + + sage: q = 5 + sage: Fq = GF(q) + sage: A = Fq['T'] + sage: R = Fq['U'] + sage: K. = Frac(R) + sage: phi = DrinfeldModule(A, [U, 0, U^2, U^3]) + sage: phi.coefficients_in_function_ring() + [T, T^2, T^3] + sage: phi.coefficients_in_function_ring(sparse=False) + [T, 0, T^2, T^3] + Compare with the method meth:`coefficients`:: + + sage: phi.coefficients() + [U, U^2, U^3] + + If the coefficients are not polynomials, an error is raised:: + + sage: psi = DrinfeldModule(A, [U, 1/U]) + sage: psi.coefficients_in_function_ring() + Traceback (most recent call last): + ... + ValueError: coefficients are not polynomials + """ + A = self.function_ring() gs = [] - for g in self.coefficients(sparse=False): + for g in self.coefficients(sparse): g = g.backend(force=True) if g.denominator().is_one(): gs.append(A(g.numerator().list())) else: - raise ValueError("the Drinfeld module must have polynomial coefficients") - s = max(gs[i].degree() // (q**i - 1) for i in range(1, r+1)) - - M = matrix(Fq, s) - if polynomial_part: - P = vector(A, s) - qk = 1 - for k in range(r+1): - for i in range(s): - e = (i+1)*qk - if polynomial_part: - P[i] += gs[k] >> e - for j in range(s): - e -= 1 - if e < 0: - break - M[i, j] += gs[k][e] - qk *= q - - if polynomial_part: - return M, P - else: - return M + raise ValueError("coefficients are not polynomials") + return gs def class_polynomial(self): r""" @@ -547,16 +570,32 @@ def class_polynomial(self): sage: phi.class_polynomial() Traceback (most recent call last): ... - ValueError: the Drinfeld module must have polynomial coefficients + ValueError: coefficients are not polynomials """ A = self.function_ring() Fq = A.base_ring() - M = self._phiT_matrix(False) - s = M.nrows() + q = Fq.cardinality() + r = self.rank() + + gs = self.coefficients_in_function_ring(sparse=False) + + s = max(gs[i].degree() // (q**i - 1) for i in range(1, r+1)) if s == 0: # small case return A.one() + M = matrix(Fq, s) + qk = 1 + for k in range(r+1): + for i in range(s): + e = (i+1)*qk + for j in range(s): + e -= 1 + if e < 0: + break + M[i, j] += gs[k][e] + qk *= q + v = vector(Fq, s) v[s-1] = 1 vs = [v] @@ -577,80 +616,3 @@ def class_polynomial(self): N = (V * M * ~V).submatrix(dim, dim) return A(N.charpoly()) - - def taelman_exponential_unit(self): - r""" - Return the exponential of a fundamental Taelman's unit - of this Drinfeld module. - - A Taelman's unit is by definition an element `x \in - \mathbb F_q((1/T))` whose exponential falls in `\mathbb F_q[T]`. - - Taelman's units form a `\mathbb F_q[T]`-line in `\mathbb F_q((1/T))`; - a fundamental unit is by definition a generator of this line. - - We refer to [Tae2012]_ for more details about this construction. - - EXAMPLES: - - The Taelman exponential unit of The Carlitz module is `1`:: - - sage: q = 7 - sage: Fq = GF(q) - sage: A = Fq['T'] - sage: K. = Frac(A) - sage: C = DrinfeldModule(A, [T, 1]); C - Drinfeld module defined by T |--> t + T - sage: C.taelman_exponential_unit() - 1 - - The same occurs more generally when the coefficients of the - Drinfeld module have small enough degrees:: - - sage: gs = [T] + [A.random_element(degree = q^i - 1) - ....: for i in range(1, 5)] - sage: phi = DrinfeldModule(A, gs) - sage: phi.taelman_exponential_unit() - 1 - - Usually, as soon as we leave the world of small Drinfeld modules, - Taelman's exponential units are highly non trivial:: - - sage: phi = DrinfeldModule(A, [T, T^(2*q+1), T^3]) - sage: phi.taelman_exponential_unit() - T^52 + T^22 + T^8 + T^2 + 1 - """ - A = self.function_ring() - Fq = A.base_ring() - q = Fq.cardinality() - M, P = self._phiT_matrix(True) - s = M.nrows() - if s == 0: - # small case - return self.base().one() - - gs = self.coefficients(sparse=False) - v = vector(Fq, s) - v[s-1] = 1 - p = A.zero() - vs = [v] - ps = [p] - for i in range(s): - pq = p - p = v * P - for j in range(len(gs) - 1): - p += gs[j] * pq - pq = pq ** q - p += gs[-1] * pq - v = v * M - vs.append(v) - ps.append(p) - vs.reverse() - ps.reverse() - V = matrix(vs) - - unit = V.left_kernel().basis()[0] - expunit = sum(unit[i]*ps[i] for i in range(s+1)) - if expunit: - expunit /= expunit.numerator().leading_coefficient() - return expunit From f182fdab05901ed7ba7c83249c79ba9821f8025a Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jan 2025 09:47:05 +0100 Subject: [PATCH 208/507] simplify doctest Co-authored-by: Martin Rubey --- .../function_field/drinfeld_modules/charzero_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index c69cb6c4dbb..77ec8905aa4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -315,7 +315,7 @@ def logarithm(self, prec=Infinity, name='z'): sage: log[2^5] 1/(T^62 + T^61 + T^59 + ... + T^8 + T^6 + T^5) - On the contrary, when ``prec`` is a finite number, all the + If ``prec`` is a finite number, all the required coefficients are computed at once:: sage: phi.logarithm(prec=10) From cb99e9e26d93722f743a4cf1bcd264a628009719 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jan 2025 09:47:32 +0100 Subject: [PATCH 209/507] remove useless else in coeff_exp Co-authored-by: Martin Rubey --- .../drinfeld_modules/charzero_drinfeld_module.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 77ec8905aa4..d51dae1ba57 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -356,9 +356,8 @@ def coeff_log(k): if prec is Infinity: L = LazyPowerSeriesRing(self._base, name) return L(coeff_log, valuation=1) - else: - L = PowerSeriesRing(self._base, name, default_prec=prec) - return L([0] + [coeff_log(i) for i in range(1, prec)], prec=prec) + L = PowerSeriesRing(self._base, name, default_prec=prec) + return L([0] + [coeff_log(i) for i in range(1, prec)], prec=prec) @cached_method def _compute_goss_polynomial(self, n, q, poly_ring, X): From 492fcd4a4b8c3d082175dfe24df84158c7eb7aec Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jan 2025 09:47:46 +0100 Subject: [PATCH 210/507] remove useless else in coeff_log Co-authored-by: Martin Rubey --- .../drinfeld_modules/charzero_drinfeld_module.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index d51dae1ba57..27a4373bb9a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -242,9 +242,8 @@ def coeff_exp(k): if prec is Infinity: L = LazyPowerSeriesRing(self._base, name) return L(coeff_exp, valuation=1) - else: - L = PowerSeriesRing(self._base, name, default_prec=prec) - return L([0] + [coeff_exp(i) for i in range(1,prec)], prec=prec) + L = PowerSeriesRing(self._base, name, default_prec=prec) + return L([0] + [coeff_exp(i) for i in range(1,prec)], prec=prec) @cached_method def _compute_coefficient_log(self, k): From 64a087cb6261f70387b4bfc85cb6d27e973bc541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 3 Jan 2025 14:21:25 +0100 Subject: [PATCH 211/507] introduce new apozeta polynomial for posets --- src/sage/combinat/posets/posets.py | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 2a4b2d24f48..a56ffa5c728 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -184,6 +184,7 @@ :meth:`~FinitePoset.flag_h_polynomial` | Return the flag h-polynomial of the poset. :meth:`~FinitePoset.order_polynomial` | Return the order polynomial of the poset. :meth:`~FinitePoset.zeta_polynomial` | Return the zeta polynomial of the poset. + :meth:`~FinitePoset.apozeta_polynomial` | Return the apozeta polynomial of the poset. :meth:`~FinitePoset.M_triangle` | Return the M-triangle of the poset. :meth:`~FinitePoset.kazhdan_lusztig_polynomial` | Return the Kazhdan-Lusztig polynomial of the poset. :meth:`~FinitePoset.coxeter_polynomial` | Return the characteristic polynomial of the Coxeter transformation. @@ -7240,6 +7241,8 @@ def zeta_polynomial(self): In particular, `Z(2)` is the number of vertices and `Z(3)` is the number of intervals. + .. SEEALSO:: :meth:`apozeta_polynomial` + EXAMPLES:: sage: posets.ChainPoset(2).zeta_polynomial() @@ -7280,6 +7283,53 @@ def zeta_polynomial(self): f = g[n] + f / n return f + def apozeta_polynomial(self): + r""" + Return the apozeta polynomial of the poset ``self``. + + The poset is assumed to be graded. + + The apozeta polynomial of a poset is the unique polynomial + `Z^{a}(q)` such that for every integer `m > 1`, `Z^{a}(m)` is + the number of weakly increasing sequences `x_1 \leq x_2 \leq + \dots \leq x_{m-1}` of elements of the poset whose largest + element belongs to the top level of the poset. + + When the poset `P` has a unique maximal element, this is + equal to `Z(q-1)` where `Z` is the zeta polynomial of `P`. + + The name comes from the greek radical ``apo``. + + .. SEEALSO:: :meth:`zeta_polynomial` + + EXAMPLES:: + + sage: P = posets.NoncrossingPartitions(SymmetricGroup(4)) + sage: P.apozeta_polynomial() + 8/3*q^3 - 10*q^2 + 37/3*q - 5 + + sage: P = Poset({"a": "bc", "b": "d", "c": "de"}) + sage: P.apozeta_polynomial() + 3/2*q^2 - 5/2*q + 1 + sage: P.zeta_polynomial() + 3/2*q^2 - 1/2*q + + TESTS: + + Checking the simplest case:: + + sage: Poset({1: []}).apozeta_polynomial() + 1 + sage: parent(_) + Univariate Polynomial Ring in q over Rational Field + """ + R = PolynomialRing(QQ, 'q') + q = R.gen() + + top_level = self.level_sets()[-1] + return sum(binomial(q - 2, len(c) - 1) + for c in self.chains() if c and c[-1] in top_level) + def M_triangle(self): r""" Return the M-triangle of the poset. From 9a3f8186a9aaf0dc8e531211ecf25ea5a2a7e498 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 4 Jan 2025 00:21:59 +0800 Subject: [PATCH 212/507] Require Python 3.11 or newer; remove outdated workarounds --- .github/workflows/dist.yml | 2 +- .gitignore | 1 - README.md | 6 +- pyproject.toml | 4 +- pyrightconfig.json | 2 +- ruff.toml | 4 +- src/MANIFEST.in | 1 - src/doc/en/developer/coding_in_python.rst | 37 +-- src/doc/en/installation/conda.rst | 18 +- src/doc/en/installation/launching.rst | 5 +- src/sage/all__sagemath_repl.py | 6 - src/sage/cpython/atexit.pyx | 25 -- src/sage/cpython/cython_metaclass.h | 9 - src/sage/cpython/debug.pyx | 5 - src/sage/cpython/debugimpl.c | 348 -------------------- src/sage/cpython/pycore_long.h | 7 +- src/sage/doctest/forker.py | 2 +- src/sage/libs/gmp/pylong.pyx | 8 - src/sage/misc/fpickle.pyx | 11 +- src/sage/misc/inherit_comparison_impl.c | 6 - src/sage_docbuild/ext/sage_autodoc.py | 22 -- src/sage_setup/command/sage_build_cython.py | 3 +- src/setup.cfg.m4 | 2 +- 23 files changed, 38 insertions(+), 496 deletions(-) delete mode 100644 src/sage/cpython/debugimpl.c diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 538b44d1431..4f84a999f44 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -188,7 +188,7 @@ jobs: # CIBW_ARCHS: ${{ matrix.arch }} # https://cibuildwheel.readthedocs.io/en/stable/options/#requires-python - CIBW_PROJECT_REQUIRES_PYTHON: ">=3.9, <3.13" + CIBW_PROJECT_REQUIRES_PYTHON: ">=3.11, <3.13" # Environment during wheel build CIBW_ENVIRONMENT: "PATH=$(pwd)/prefix/bin:$PATH CPATH=$(pwd)/prefix/include:$CPATH LIBRARY_PATH=$(pwd)/prefix/lib:$LIBRARY_PATH LD_LIBRARY_PATH=$(pwd)/prefix/lib:$LD_LIBRARY_PATH PKG_CONFIG_PATH=$(pwd)/prefix/share/pkgconfig:$PKG_CONFIG_PATH ACLOCAL_PATH=/usr/share/aclocal PIP_CONSTRAINT=$(pwd)/constraints.txt PIP_FIND_LINKS=file://$(pwd)/wheelhouse SAGE_NUM_THREADS=6" # Use 'build', not 'pip wheel' diff --git a/.gitignore b/.gitignore index 323d81b557b..8111db2b0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -161,7 +161,6 @@ __pycache__/ /src/sage/modular/arithgroup/farey_symbol.h # List of C and C++ files that are actual source files, # NOT generated by Cython. The same list appears in src/MANIFEST.in -!/src/sage/cpython/debugimpl.c !/src/sage/graphs/base/boost_interface.cpp !/src/sage/graphs/cliquer/cl.c !/src/sage/graphs/graph_decompositions/sage_tdlib.cpp diff --git a/README.md b/README.md index af91374fe19..236709f7412 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,7 @@ in the Installation Guide. more details. - Python 3.4 or later, or Python 2.7, a full installation including - `urllib`; but ideally version 3.9.x, 3.10.x, 3.11.x, 3.12.x, which + `urllib`; but ideally version 3.11.x or later, which will avoid having to build Sage's own copy of Python 3. See [build/pkgs/python3/SPKG.rst](build/pkgs/python3/SPKG.rst) for more details. @@ -551,11 +551,11 @@ SAGE_ROOT Root directory (create by git clone) │ │ ├── installed/ │ │ │ Records of installed non-Python packages │ │ ├── scripts/ Scripts for uninstalling installed packages -│ │ └── venv-python3.9 (SAGE_VENV) +│ │ └── venv-python (SAGE_VENV) │ │ │ Installation hierarchy (virtual environment) │ │ │ for Python packages │ │ ├── bin/ Executables and installed scripts -│ │ ├── lib/python3.9/site-packages/ +│ │ ├── lib/python/site-packages/ │ │ │ Python modules/packages are installed here │ │ └── var/lib/sage/ │ │ └── wheels/ diff --git a/pyproject.toml b/pyproject.toml index da06db03649..5b4a4be8cee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,15 +70,13 @@ classifiers = [ "Operating System :: POSIX", "Operating System :: MacOS :: MacOS X", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering :: Mathematics", ] urls = {Homepage = "https://www.sagemath.org"} -requires-python = ">=3.9, <3.13" +requires-python = ">=3.11, <3.13" [project.optional-dependencies] R = [ diff --git a/pyrightconfig.json b/pyrightconfig.json index 643c56360c9..826c3aabe86 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -7,7 +7,7 @@ "root": "src" } ], - "pythonVersion": "3.9", + "pythonVersion": "3.11", "exclude": ["venv"], "venvPath": "./venv/", "venv": "./", diff --git a/ruff.toml b/ruff.toml index b3070914153..bb2e932e430 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,7 +1,7 @@ # https://docs.astral.sh/ruff/configuration/#config-file-discovery -# Assume Python 3.9 -target-version = "py39" +# Python 3.11 is the minimum supported version +target-version = "py311" lint.select = [ "E", # pycodestyle errors - https://docs.astral.sh/ruff/rules/#error-e diff --git a/src/MANIFEST.in b/src/MANIFEST.in index f6231401294..8428dfff314 100644 --- a/src/MANIFEST.in +++ b/src/MANIFEST.in @@ -23,7 +23,6 @@ global-exclude *.cpp # List of C and C++ files that are actual source files, # NOT generated by Cython. The same list appears in SAGE_ROOT/.gitignore # -include sage/cpython/debugimpl.c include sage/graphs/base/boost_interface.cpp include sage/graphs/cliquer/cl.c include sage/libs/eclib/wrap.cpp diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index 1ea6eebb317..f86ab9a09c3 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -12,17 +12,14 @@ Sage. Python language standard ======================== -Sage library code needs to be compatible with all versions of Python -that Sage supports. The information regarding the supported versions -can be found in the files ``build/pkgs/python3/spkg-configure.m4`` and -``src/setup.cfg.m4``. - -Python 3.9 is the oldest supported version. Hence, -all language and library features that are available in Python 3.9 can -be used; but features introduced in Python 3.10 cannot be used. If a -feature is deprecated in a newer supported version, it must be ensured -that deprecation warnings issued by Python do not lead to failures in -doctests. +Sage follows the time window-based support policy +`SPEC 0 — Minimum Supported Dependencies `_ +for Python versions. +The current minimum supported Python version can be found in the +``pyproject.toml`` file. Accordingly, only language and library features +available in this version can be used. If a feature is deprecated in a newer +supported version, it must be ensured that deprecation warnings issued by +Python do not lead to failures in doctests. Some key language and library features have been backported to older Python versions using one of two mechanisms: @@ -34,21 +31,9 @@ using one of two mechanisms: of annotations). All Sage library code that uses type annotations should include this ``__future__`` import and follow PEP 563. -- Backport packages - - - `importlib_metadata <../reference/spkg/importlib_metadata>`_ - (to be used in place of ``importlib.metadata``), - - `importlib_resources <../reference/spkg/importlib_resources>`_ - (to be used in place of ``importlib.resources``), - - `typing_extensions <../reference/spkg/typing_extensions>`_ - (to be used in place of ``typing``). - - The Sage library declares these packages as dependencies and ensures that - versions that provide features of Python 3.11 are available. - -Meta :issue:`29756` keeps track of newer Python features and serves -as a starting point for discussions on how to make use of them in the -Sage library. +- The `typing_extensions <../reference/spkg/typing_extensions>`_ package + is used to backport features from newer versions of the ``typing`` module. + The Sage library declares this package as a dependency. Design diff --git a/src/doc/en/installation/conda.rst b/src/doc/en/installation/conda.rst index ae560bb5a38..65f03583af6 100644 --- a/src/doc/en/installation/conda.rst +++ b/src/doc/en/installation/conda.rst @@ -50,7 +50,7 @@ Create a new conda environment containing SageMath, either with ``mamba`` or ``c $ conda create -n sage sage python=X -where ``X`` is version of Python, e.g. ``3.9``. +where ``X`` is version of Python, e.g. ``3.12``. To use Sage from there, @@ -86,22 +86,22 @@ Here we assume that you are using a git checkout. .. code-block:: shell - $ mamba env create --file environment-3.11-linux.yml --name sage-dev + $ mamba env create --file environment-3.12-linux.yml --name sage-dev $ conda activate sage-dev .. tab:: conda .. code-block:: shell - $ conda env create --file environment-3.11-linux.yml --name sage-dev + $ conda env create --file environment-3.12-linux.yml --name sage-dev $ conda activate sage-dev - Alternatively, you can use ``environment-3.11-linux.yml`` or - ``environment-optional-3.11-linux.yml``, which will only install standard + Alternatively, you can use ``environment-3.12-linux.yml`` or + ``environment-optional-3.12-linux.yml``, which will only install standard (and optional) packages without any additional developer tools. - A different Python version can be selected by replacing ``3.11`` by ``3.9`` - or ``3.10`` in these commands. + A different Python version can be selected by replacing ``3.12`` with the + desired version. - Bootstrap the source tree and install the build prerequisites and the Sage library:: @@ -137,7 +137,7 @@ After editing any Cython files, rebuild the Sage library using:: In order to update the conda environment later, you can run:: - $ mamba env update --file environment-3.11-linux.yml --name sage-dev + $ mamba env update --file environment-3.12-linux.yml --name sage-dev To build the documentation, use:: @@ -156,5 +156,5 @@ To build the documentation, use:: You can update the conda lock files by running ``.github/workflows/conda-lock-update.py`` or by running - ``conda-lock --platform linux-64 --filename environment-3.11-linux.yml --lockfile environment-3.11-linux.lock`` + ``conda-lock --platform linux-64 --filename environment-3.12-linux.yml --lockfile environment-3.12-linux.lock`` manually. diff --git a/src/doc/en/installation/launching.rst b/src/doc/en/installation/launching.rst index b612a89a9c1..bb99fbcfb00 100644 --- a/src/doc/en/installation/launching.rst +++ b/src/doc/en/installation/launching.rst @@ -67,9 +67,8 @@ Sage uses the following environment variables when it runs: - See https://docs.python.org/3/using/cmdline.html#environment-variables - for more variables used by Python (not an exhaustive list). With - Python 3.11 or later, a brief summary can also be obtained by - running `python3 --help-env`. + for more variables used by Python (not an exhaustive list). + A brief summary can also be obtained by running `python3 --help-env`. Using a Jupyter Notebook remotely --------------------------------- diff --git a/src/sage/all__sagemath_repl.py b/src/sage/all__sagemath_repl.py index c830950c26b..13efd115e1c 100644 --- a/src/sage/all__sagemath_repl.py +++ b/src/sage/all__sagemath_repl.py @@ -100,12 +100,6 @@ message=r"Pickle, copy, and deepcopy support will be " r"removed from itertools in Python 3.14.") -# triggered in Python 3.9 on Redhat-based distributions -# https://github.com/sagemath/sage/issues/37863 -# https://github.com/networkx/networkx/issues/7101 -warnings.filterwarnings('ignore', category=RuntimeWarning, - message="networkx backend defined more than once: nx-loopback") - from sage.all__sagemath_objects import * from sage.all__sagemath_environment import * diff --git a/src/sage/cpython/atexit.pyx b/src/sage/cpython/atexit.pyx index 8f833ab1437..c74c1d0308a 100644 --- a/src/sage/cpython/atexit.pyx +++ b/src/sage/cpython/atexit.pyx @@ -148,8 +148,6 @@ from cpython.ref cimport PyObject # Implement "_atexit_callbacks()" for each supported python version cdef extern from *: """ - #if PY_VERSION_HEX >= 0x030a0000 - /********** Python 3.10 **********/ #define Py_BUILD_CORE #undef _PyGC_FINALIZED #include "internal/pycore_interp.h" @@ -163,29 +161,6 @@ cdef extern from *: struct atexit_state state = interp->atexit; return state.callbacks; } - #else - /********** Python < 3.10 **********/ - /* Internal structures defined in the CPython source in - * Modules/atexitmodule.c and subject to (but unlikely to) change. Watch - * https://bugs.python.org/issue32082 for a request to (eventually) - * re-expose more of the atexit module's internals to Python - * typedef struct - */ - typedef struct { - PyObject *func; - PyObject *args; - PyObject *kwargs; - } atexit_callback; - typedef struct { - atexit_callback **atexit_callbacks; - int ncallbacks; - int callback_len; - } atexitmodule_state; - static atexit_callback ** _atexit_callbacks(PyObject *self) { - atexitmodule_state *state = PyModule_GetState(self); - return state->atexit_callbacks; - } - #endif """ ctypedef struct atexit_callback: PyObject* func diff --git a/src/sage/cpython/cython_metaclass.h b/src/sage/cpython/cython_metaclass.h index ecf7f973c3e..f7b6f345fa3 100644 --- a/src/sage/cpython/cython_metaclass.h +++ b/src/sage/cpython/cython_metaclass.h @@ -8,13 +8,6 @@ * http://www.gnu.org/licenses/ *****************************************************************************/ -/* Compatibility for python 3.8, can be removed later */ -#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) -static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) -{ ob->ob_type = type; } -#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type) -#endif - /* Tuple (None, None, None), initialized as needed */ static PyObject* NoneNoneNone; @@ -52,7 +45,6 @@ static CYTHON_INLINE int Sage_PyType_Ready(PyTypeObject* t) if (r < 0) return r; -#if PY_VERSION_HEX >= 0x03050000 // Cython 3 sets Py_TPFLAGS_HEAPTYPE before calling PyType_Ready, // and resets just after the call. We need to reset it earlier, // since otherwise the call to metaclass.__init__ below may have @@ -60,7 +52,6 @@ static CYTHON_INLINE int Sage_PyType_Ready(PyTypeObject* t) // See also: // https://github.com/cython/cython/issues/3603 t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE; -#endif /* Set or get metaclass (the type of t) */ PyTypeObject* metaclass; diff --git a/src/sage/cpython/debug.pyx b/src/sage/cpython/debug.pyx index f4e0a44046f..a11dfc085f3 100644 --- a/src/sage/cpython/debug.pyx +++ b/src/sage/cpython/debug.pyx @@ -19,9 +19,6 @@ cdef extern from "Python.h": # Helper to get a pointer to an object's __dict__ slot, if any PyObject** _PyObject_GetDictPtr(obj) -cdef extern from "debugimpl.c": - void _type_debug(PyTypeObject*) - from sage.cpython.getattr cimport AttributeErrorMessage @@ -303,5 +300,3 @@ def type_debug(cls): """ if not isinstance(cls, type): raise TypeError(f"{cls!r} is not a type") - - _type_debug(cls) diff --git a/src/sage/cpython/debugimpl.c b/src/sage/cpython/debugimpl.c deleted file mode 100644 index 176e93900c2..00000000000 --- a/src/sage/cpython/debugimpl.c +++ /dev/null @@ -1,348 +0,0 @@ -#include - -#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 9 - -static void _type_debug(PyTypeObject* tp) -{ - printf("Not implemented for CPython >= 3.9\n"); -} - -#else - -#define HAVE_WEAKREFS(tp) (1) -#define HAVE_CLASS(tp) (1) -#define HAVE_ITER(tp) (1) -#define HAVE_RICHCOMPARE(tp) (1) -#define HAVE_INPLACEOPS(tp) (1) -#define HAVE_SEQUENCE_IN(tp) (1) -#define HAVE_GETCHARBUFFER(tp) (1) -#define HAVE_NEW_DIVISION(tp) (1) -#define HAVE_INDEX(tp) (1) -#define HAVE_NEWBUFFER(tp) (1) -#define HAVE_FINALIZE(tp) (tp->tp_flags & Py_TPFLAGS_HAVE_FINALIZE) - -static void print_object(void* pyobj) -{ - if (pyobj == NULL) - { - printf("NULL\n"); - return; - } - - PyObject* obj = (PyObject*)pyobj; - - if (PyTuple_Check(obj)) - { - printf("%s:\n", Py_TYPE(obj)->tp_name); - - Py_ssize_t i, n = PyTuple_GET_SIZE(obj); - for (i = 0; i < n; i++) - { - printf(" "); - PyObject_Print(PyTuple_GET_ITEM(obj, i), stdout, 0); - printf("\n"); - } - } - else if (PyDict_Check(obj)) - { - printf("%s:\n", Py_TYPE(obj)->tp_name); - - Py_ssize_t pos = 0; - PyObject* key; - PyObject* value; - while (PyDict_Next(obj, &pos, &key, &value)) - { - printf(" "); - PyObject_Print(key, stdout, 0); - printf(": "); - PyObject_Print(value, stdout, 0); - printf("\n"); - } - } - else - { - PyObject_Print(obj, stdout, 0); - printf("\n"); - } -} - - -#define pointer_check_constant(ptr, val) \ - if (ptr == (void*)(val)) \ - { \ - special = 0; \ - printf(#val "\n"); \ - } - -#define subattr_pointer_value(sub, attr) \ - special = 1; \ - pointer_check_constant(tp->attr, NULL) \ - else pointer_check_constant(tp->attr, PyType_GenericAlloc) \ - else pointer_check_constant(tp->attr, PyType_GenericNew) \ - else pointer_check_constant(tp->attr, PyObject_Del) \ - else pointer_check_constant(tp->attr, PyObject_GC_Del) \ - else pointer_check_constant(tp->attr, PyObject_GenericGetAttr) \ - else pointer_check_constant(tp->attr, PyObject_GenericSetAttr) \ - else pointer_check_constant(tp->attr, _Py_HashPointer) \ - else pointer_check_constant(tp->attr, PyObject_HashNotImplemented) \ - else pointer_check_constant(tp->attr, subtype_traverse) \ - else pointer_check_constant(tp->attr, subtype_clear) \ - else pointer_check_constant(tp->attr, subtype_dealloc) \ - else \ - { \ - PyObject* mro = tp->tp_mro; \ - PyTypeObject* subtp; \ - Py_ssize_t i, n = PyTuple_GET_SIZE(mro); \ - for (i = n-1; i >= 0; i--) \ - { \ - subtp = (PyTypeObject*)PyTuple_GET_ITEM(mro, i); \ - if (subtp != tp && PyType_Check(subtp)) \ - { \ - if (subtp->sub && tp->attr == subtp->attr) \ - { \ - special = 0; \ - printf("== %s\n", subtp->tp_name); \ - break; \ - } \ - } \ - } \ - } \ - if (special) printf("%p\n", tp->attr); - -#define attr_pointer_value(attr) subattr_pointer_value(tp_name, attr); - -#define attr_pointer(attr) \ - printf(" " #attr ": "); \ - attr_pointer_value(attr); -#define attr_pointer_meth(attr, method) \ - printf(" " #attr " (" method "): "); \ - attr_pointer_value(attr); -#define subattr_pointer(sub, attr) \ - printf(" " #attr ": "); \ - subattr_pointer_value(sub, sub->attr); -#define subattr_pointer_meth(sub, attr, method) \ - printf(" " #attr " (" method "): "); \ - subattr_pointer_value(sub, sub->attr); - -#define attr_object(attr) \ - printf(" " #attr ": "); \ - print_object(tp->attr); -#define attr_object_meth(attr, method) \ - printf(" " #attr " (" method "): "); \ - print_object(tp->attr); - -#define attr_flag(flag) \ - if (tp->tp_flags & Py_TPFLAGS_ ## flag) \ - printf(" " #flag "\n"); - - -static void _type_debug(PyTypeObject* tp) -{ - int special; - - PyObject_Print((PyObject*)tp, stdout, 0); - printf(" (%p)\n", tp); - printf(" ob_refcnt: %ld\n", (long)Py_REFCNT(tp)); - printf(" ob_type: "); print_object(Py_TYPE(tp)); - printf(" tp_name: %s\n", tp->tp_name); - printf(" tp_basicsize: %ld\n", (long)tp->tp_basicsize); - printf(" tp_itemsize: %ld\n", (long)tp->tp_itemsize); - printf(" tp_dictoffset: %ld\n", (long)tp->tp_dictoffset); - if HAVE_WEAKREFS(tp) - { - printf(" tp_weaklistoffset: %ld\n", (long)tp->tp_weaklistoffset); - } - - if HAVE_CLASS(tp) - { - attr_object_meth(tp_base, "__base__"); - attr_object_meth(tp_bases, "__bases__"); - attr_object_meth(tp_mro, "__mro__"); - attr_object_meth(tp_dict, "__dict__"); - } - - attr_pointer(tp_alloc); - attr_pointer_meth(tp_new, "__new__"); - attr_pointer_meth(tp_init, "__init__"); - attr_pointer_meth(tp_dealloc, "__dealloc__"); - if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) - { - attr_pointer_meth(tp_del, "__del__"); - } - #if PY_MAJOR_VERSION >= 3 - if HAVE_FINALIZE(tp) - { - attr_pointer_meth(tp_finalize, "__del__"); - } - #endif - attr_pointer(tp_free); - - attr_pointer_meth(tp_repr, "__repr__"); - attr_pointer(tp_print); - attr_pointer_meth(tp_hash, "__hash__"); - attr_pointer_meth(tp_call, "__call__"); - attr_pointer_meth(tp_str, "__str__"); - #if PY_MAJOR_VERSION <= 2 - attr_pointer_meth(tp_compare, "cmp"); - #endif - attr_pointer_meth(tp_richcompare, "__richcmp__"); - attr_pointer_meth(tp_getattr, "__getattribute__"); - attr_pointer_meth(tp_setattr, "__setattribute__"); - attr_pointer_meth(tp_getattro, "__getattribute__"); - attr_pointer_meth(tp_setattro, "__setattribute__"); - if HAVE_ITER(tp) - { - attr_pointer_meth(tp_iter, "__iter__"); - attr_pointer_meth(tp_iternext, "__next__"); - } - if HAVE_CLASS(tp) - { - attr_pointer_meth(tp_descr_get, "__get__"); - attr_pointer_meth(tp_descr_set, "__set__"); - - attr_object(tp_cache); - attr_object(tp_weaklist); - } - if HAVE_RICHCOMPARE(tp) - { - attr_pointer(tp_traverse); - attr_pointer(tp_clear); - } - if HAVE_CLASS(tp) - { - attr_pointer(tp_is_gc); - } - - attr_pointer(tp_as_number); - if (special) - { - subattr_pointer_meth(tp_as_number, nb_add, "__add__"); - subattr_pointer_meth(tp_as_number, nb_subtract, "__sub__"); - subattr_pointer_meth(tp_as_number, nb_multiply, "__mul__"); - #if PY_MAJOR_VERSION <= 2 - subattr_pointer_meth(tp_as_number, nb_divide, "__div__"); - #endif - if HAVE_NEW_DIVISION(tp) - { - subattr_pointer_meth(tp_as_number, nb_floor_divide, "__floordiv__"); - subattr_pointer_meth(tp_as_number, nb_true_divide, "__truediv__"); - } - subattr_pointer_meth(tp_as_number, nb_remainder, "__mod__"); - subattr_pointer_meth(tp_as_number, nb_divmod, "__divmod__"); - subattr_pointer_meth(tp_as_number, nb_power, "__pow__"); - subattr_pointer_meth(tp_as_number, nb_negative, "__neg__"); - subattr_pointer_meth(tp_as_number, nb_positive, "__pos__"); - subattr_pointer_meth(tp_as_number, nb_absolute, "__abs__"); - subattr_pointer_meth(tp_as_number, nb_bool, "__bool__"); - subattr_pointer_meth(tp_as_number, nb_invert, "__invert__"); - subattr_pointer_meth(tp_as_number, nb_lshift, "__lshift__"); - subattr_pointer_meth(tp_as_number, nb_rshift, "__rshift__"); - subattr_pointer_meth(tp_as_number, nb_and, "__and__"); - subattr_pointer_meth(tp_as_number, nb_or, "__or__"); - subattr_pointer_meth(tp_as_number, nb_xor, "__xor__"); - subattr_pointer_meth(tp_as_number, nb_int, "__int__"); - #if PY_MAJOR_VERSION <= 2 - subattr_pointer_meth(tp_as_number, nb_long, "__long__"); - #endif - if HAVE_INDEX(tp) - { - subattr_pointer_meth(tp_as_number, nb_index, "__index__"); - } - subattr_pointer_meth(tp_as_number, nb_float, "__float__"); - #if PY_MAJOR_VERSION <= 2 - subattr_pointer_meth(tp_as_number, nb_oct, "__oct__"); - subattr_pointer_meth(tp_as_number, nb_hex, "__hex__"); - subattr_pointer(tp_as_number, nb_coerce); - #endif - - if HAVE_INPLACEOPS(tp) - { - subattr_pointer_meth(tp_as_number, nb_inplace_add, "__iadd__"); - subattr_pointer_meth(tp_as_number, nb_inplace_subtract, "__isub__"); - subattr_pointer_meth(tp_as_number, nb_inplace_multiply, "__imul__"); - #if PY_MAJOR_VERSION <= 2 - subattr_pointer_meth(tp_as_number, nb_inplace_divide, "__idiv__"); - #endif - if HAVE_NEW_DIVISION(tp) - { - subattr_pointer_meth(tp_as_number, nb_inplace_floor_divide, "__ifloordiv__"); - subattr_pointer_meth(tp_as_number, nb_inplace_true_divide, "__itruediv__"); - } - subattr_pointer_meth(tp_as_number, nb_inplace_remainder, "__imod__"); - subattr_pointer_meth(tp_as_number, nb_inplace_power, "__ipow__"); - subattr_pointer_meth(tp_as_number, nb_inplace_lshift, "__ilshift__"); - subattr_pointer_meth(tp_as_number, nb_inplace_rshift, "__irshift__"); - subattr_pointer_meth(tp_as_number, nb_inplace_and, "__iand__"); - subattr_pointer_meth(tp_as_number, nb_inplace_or, "__ior__"); - subattr_pointer_meth(tp_as_number, nb_inplace_xor, "__ixor__"); - } - } - - attr_pointer(tp_as_sequence); - if (special) - { - subattr_pointer_meth(tp_as_sequence, sq_length, "__len__"); - subattr_pointer_meth(tp_as_sequence, sq_concat, "__add__"); - if HAVE_INPLACEOPS(tp) - { - subattr_pointer_meth(tp_as_sequence, sq_inplace_concat, "__iadd__"); - } - subattr_pointer_meth(tp_as_sequence, sq_repeat, "__mul__"); - if HAVE_INPLACEOPS(tp) - { - subattr_pointer_meth(tp_as_sequence, sq_inplace_repeat, "__imul__"); - } - subattr_pointer_meth(tp_as_sequence, sq_item, "__getitem__"); - subattr_pointer_meth(tp_as_sequence, sq_ass_item, "__setitem__"); - if HAVE_SEQUENCE_IN(tp) - { - subattr_pointer_meth(tp_as_sequence, sq_contains, "__contains__"); - } - } - - attr_pointer(tp_as_mapping); - if (special) - { - subattr_pointer_meth(tp_as_mapping, mp_length, "__len__"); - subattr_pointer_meth(tp_as_mapping, mp_subscript, "__getitem__"); - subattr_pointer_meth(tp_as_mapping, mp_ass_subscript, "__setitem__"); - } - - attr_pointer(tp_as_buffer); - if (special) - { - #if PY_MAJOR_VERSION <= 2 - subattr_pointer(tp_as_buffer, bf_getreadbuffer); - subattr_pointer(tp_as_buffer, bf_getwritebuffer); - subattr_pointer(tp_as_buffer, bf_getsegcount); - if HAVE_GETCHARBUFFER(tp) - { - subattr_pointer(tp_as_buffer, bf_getcharbuffer); - } - #endif - if HAVE_NEWBUFFER(tp) - { - subattr_pointer_meth(tp_as_buffer, bf_getbuffer, "__getbuffer__"); - subattr_pointer_meth(tp_as_buffer, bf_releasebuffer, "__releasebuffer__"); - } - } - - printf(" tp_flags:\n"); - attr_flag(HEAPTYPE); - attr_flag(BASETYPE); - attr_flag(READY); - attr_flag(READYING); - attr_flag(HAVE_GC); - #if PY_MAJOR_VERSION <= 2 - attr_flag(CHECKTYPES); - #endif - attr_flag(HAVE_VERSION_TAG); - attr_flag(VALID_VERSION_TAG); - attr_flag(IS_ABSTRACT); - if (tp->tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG) - { - printf(" tp_version_tag: %lu\n", (unsigned long)tp->tp_version_tag); - } -} - -#endif diff --git a/src/sage/cpython/pycore_long.h b/src/sage/cpython/pycore_long.h index 99561f1ba96..bbe9d3964bd 100644 --- a/src/sage/cpython/pycore_long.h +++ b/src/sage/cpython/pycore_long.h @@ -2,12 +2,14 @@ #include #if PY_VERSION_HEX >= 0x030C00A5 +// For Python 3.12 compatibility #define ob_digit(o) (((PyLongObject*)o)->long_value.ob_digit) #else #define ob_digit(o) (((PyLongObject*)o)->ob_digit) #endif #if PY_VERSION_HEX >= 0x030C00A7 +// For Python 3.12 compatibility // taken from cpython:Include/internal/pycore_long.h @ 3.12 /* Long value tag bits: @@ -87,12 +89,7 @@ _PyLong_DigitCount(const PyLongObject *op) static inline void _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) { -#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION < 9) -// The function Py_SET_SIZE is defined starting with python 3.9. - Py_SIZE(op) = size; -#else Py_SET_SIZE(op, sign < 0 ? -size : size); -#endif } #endif diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index bf6d49906de..f3d1369be5a 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1082,7 +1082,7 @@ def compile_and_execute(self, example, compiler, globs): False sage: doctests, extras = FDS.create_doctests(globs) sage: ex0 = doctests[0].examples[0] - sage: flags = 32768 if sys.version_info.minor < 8 else 524288 + sage: flags = 524288 sage: def compiler(ex): ....: return compile(ex.source, '', ....: 'single', flags, 1) diff --git a/src/sage/libs/gmp/pylong.pyx b/src/sage/libs/gmp/pylong.pyx index 41587396120..a6049232443 100644 --- a/src/sage/libs/gmp/pylong.pyx +++ b/src/sage/libs/gmp/pylong.pyx @@ -32,14 +32,6 @@ from sage.cpython.pycore_long cimport (ob_digit, _PyLong_IsNegative, from sage.libs.gmp.mpz cimport * cdef extern from *: - """ - /* Compatibility for python 3.8, can be removed later */ - #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) - static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) - { ob->ob_size = size; } - #define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) - #endif - """ void Py_SET_SIZE(object, Py_ssize_t) int hash_bits """ #ifdef _PyHASH_BITS diff --git a/src/sage/misc/fpickle.pyx b/src/sage/misc/fpickle.pyx index 7a45ecc4f75..2c6a336c328 100644 --- a/src/sage/misc/fpickle.pyx +++ b/src/sage/misc/fpickle.pyx @@ -44,18 +44,13 @@ def reduce_code(co): raise ValueError("Cannot pickle code objects from closures") co_args = (co.co_argcount,) - if sys.version_info.minor >= 8: - co_args += (co.co_posonlyargcount,) + co_args += (co.co_posonlyargcount,) co_args += (co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name) - if sys.version_info.minor >= 11: - co_args += (co.co_qualname, co.co_firstlineno, - co.co_linetable, co.co_exceptiontable) - else: - co_args += (co.co_firstlineno, co.co_lnotab) - + co_args += (co.co_qualname, co.co_firstlineno, + co.co_linetable, co.co_exceptiontable) return (code_ctor, co_args) diff --git a/src/sage/misc/inherit_comparison_impl.c b/src/sage/misc/inherit_comparison_impl.c index f12dc2a6976..2854beab87c 100644 --- a/src/sage/misc/inherit_comparison_impl.c +++ b/src/sage/misc/inherit_comparison_impl.c @@ -8,14 +8,8 @@ static CYTHON_INLINE void inherit_comparison(PyTypeObject* dst, const PyTypeObject* src) { /* Do nothing if "dst" already has comparison defined */ -#if (PY_MAJOR_VERSION < 3) - if (dst->tp_compare) return; -#endif if (dst->tp_richcompare) return; /* Copy comparison method(s) */ -#if (PY_MAJOR_VERSION < 3) - dst->tp_compare = src->tp_compare; -#endif dst->tp_richcompare = src->tp_richcompare; } diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 94e7d076fa9..8b80350585c 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1969,28 +1969,6 @@ def get_doc(self) -> list[list[str]] | None: if isinstance(self.object, TypeVar): if self.object.__doc__ == TypeVar.__doc__: return [] - # ------------------------------------------------------------------ - # This section is kept for compatibility with python 3.9 - # see https://github.com/sagemath/sage/pull/38549#issuecomment-2327790930 - if sys.version_info[:2] < (3, 10): - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): - parts = self.modname.strip('.').split('.') - orig_objpath = self.objpath - for i in range(len(parts)): - new_modname = '.'.join(parts[:len(parts) - i]) - new_objpath = parts[len(parts) - i:] + orig_objpath - try: - analyzer = ModuleAnalyzer.for_module(new_modname) - analyzer.analyze() - key = ('', new_objpath[-1]) - comment = list(analyzer.attr_docs.get(key, [])) - if comment: - self.objpath = new_objpath - self.modname = new_modname - return [comment] - except PycodeError: - pass - # ------------------------------------------------------------------ if self.doc_as_attr: # Don't show the docstring of the class when it is an alias. if self.get_variable_comment(): diff --git a/src/sage_setup/command/sage_build_cython.py b/src/sage_setup/command/sage_build_cython.py index 880ce7383e2..2e04bd80f87 100644 --- a/src/sage_setup/command/sage_build_cython.py +++ b/src/sage_setup/command/sage_build_cython.py @@ -104,8 +104,7 @@ def finalize_options(self): ('force', 'force')] # Python 3.5 now has a parallel option as well - if sys.version_info[:2] >= (3, 5): - inherit_opts.append(('parallel', 'parallel')) + inherit_opts.append(('parallel', 'parallel')) self.set_undefined_options('build_ext', *inherit_opts) diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 969793209c8..5fd27e416f8 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -9,7 +9,7 @@ license_files = LICENSE.txt include(`setup_cfg_metadata.m4')dnl' [options] -python_requires = >=3.9, <3.13 +python_requires = >=3.11, <3.13 install_requires = SPKG_INSTALL_REQUIRES_six dnl From build/pkgs/sagelib/dependencies From 41b05093ba7800335131c21f7bd5c95978674380 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 4 Jan 2025 00:46:03 +0800 Subject: [PATCH 213/507] Use Python 3.12 in CI --- .github/workflows/build.yml | 6 +++--- .github/workflows/doc-build-pdf.yml | 6 +++--- .github/workflows/doc-build.yml | 6 +++--- .github/workflows/pyright.yml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 535c2948c39..45b4bf83339 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ on: platform: description: 'Platform' required: true - default: 'ubuntu-jammy-standard' + default: 'ubuntu-noble-standard' docker_tag: description: 'Docker tag' required: true @@ -68,8 +68,8 @@ concurrency: env: # Adapted from docker.yml - TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" - BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-with-targets:ci" FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" FROM_DOCKER_TARGET: "with-targets" FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml index dce25a132c9..f8d30cd32b6 100644 --- a/.github/workflows/doc-build-pdf.yml +++ b/.github/workflows/doc-build-pdf.yml @@ -9,7 +9,7 @@ on: platform: description: 'Platform' required: true - default: 'ubuntu-jammy-standard' + default: 'ubuntu-noble-standard' docker_tag: description: 'Docker tag' required: true @@ -22,8 +22,8 @@ concurrency: env: # Same as in build.yml - TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" - BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-with-targets:ci" FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" FROM_DOCKER_TARGET: "with-targets" FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index f46317ecda7..3d80d4c30a8 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -20,7 +20,7 @@ on: platform: description: 'Platform' required: true - default: 'ubuntu-jammy-standard' + default: 'ubuntu-noble-standard' docker_tag: description: 'Docker tag' required: true @@ -33,8 +33,8 @@ concurrency: env: # Same as in build.yml - TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" - BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-noble-standard' }}-with-targets:ci" FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" FROM_DOCKER_TARGET: "with-targets" FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml index eb84117cb3a..21ffe785d23 100644 --- a/.github/workflows/pyright.yml +++ b/.github/workflows/pyright.yml @@ -18,7 +18,7 @@ concurrency: jobs: pyright: runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-ubuntu-jammy-standard-with-targets:dev + container: ghcr.io/sagemath/sage/sage-ubuntu-noble-standard-with-targets:dev steps: - name: Checkout id: checkout From 28e546beaffdb1a697a26797bed95f237b80239c Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 12:39:21 +0100 Subject: [PATCH 214/507] add parameter immutable to DeBruijn, Kautz and ImaseItoh --- src/sage/graphs/digraph_generators.py | 113 ++++++++++++++++---------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index b374392163e..8d57586f65c 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -950,7 +950,7 @@ def Circulant(self, n, integers): return G - def DeBruijn(self, k, n, vertices='strings'): + def DeBruijn(self, k, n, vertices='strings', immutable=True): r""" Return the De Bruijn digraph with parameters `k,n`. @@ -977,6 +977,9 @@ def DeBruijn(self, k, n, vertices='strings'): are words over an alphabet (default) or integers (``vertices='string'``) + - ``immutable`` -- boolean (default: ``False``); whether to return + an immutable or mutable digraph. + EXAMPLES: de Bruijn digraph of degree 2 and diameter 2:: @@ -1030,40 +1033,46 @@ def DeBruijn(self, k, n, vertices='strings'): """ from sage.rings.integer import Integer + name = f"De Bruijn digraph (k={k}, n={n})" if vertices == 'strings': from sage.combinat.words.words import Words W = Words(list(range(k)) if isinstance(k, Integer) else k, n) A = Words(list(range(k)) if isinstance(k, Integer) else k, 1) - g = DiGraph(loops=True) if not n: - g.allow_multiple_edges(True) - v = W[0] - vs = v.string_rep() - for a in A: - g.add_edge(vs, vs, a.string_rep()) + multiedges = True + def edges(): + v = W[0] + vs = v.string_rep() + return ((vs, vs, a.string_rep()) for a in A) + else: - for w in W: - ww = w[1:] - ws = w.string_rep() - for a in A: - g.add_edge(ws, (ww * a).string_rep(), a.string_rep()) + multiedges = False + def edges(): + for w in W: + ww = w[1:] + ws = w.string_rep() + yield from ((ws, (ww * a).string_rep(), a.string_rep()) + for a in A) + + return DiGraph(edges(), format='list_of_edges', name=name, + loops=True, multiedges=multiedges, + immutable=immutable) elif vertices == 'integers': d = k if isinstance(k, Integer) else len(list(k)) if not d: - g = DiGraph(loops=True, multiedges=True) - else: - g = digraphs.GeneralizedDeBruijn(d ** n, d) + return DiGraph(loops=True, multiedges=True, name=name, + immutable=immutable) + + return digraphs.GeneralizedDeBruijn(d ** n, d, immutable=immutable, + name=name) else: raise ValueError('unknown type for vertices') - g.name("De Bruijn digraph (k={}, n={})".format(k, n)) - return g - - def GeneralizedDeBruijn(self, n, d): + def GeneralizedDeBruijn(self, n, d, immutable=False, name=None): r""" Return the generalized de Bruijn digraph of order `n` and degree `d`. @@ -1082,6 +1091,12 @@ def GeneralizedDeBruijn(self, n, d): - ``d`` -- integer; degree of the digraph (must be at least one) + - ``immutable`` -- boolean (default: ``False``); whether to return + an immutable or mutable digraph. + + - ``name`` -- string (default: ``None``); when set, the specified name + is used instead of the default one. + .. SEEALSO:: * :meth:`sage.graphs.generic_graph.GenericGraph.is_circulant` -- @@ -1115,15 +1130,15 @@ def GeneralizedDeBruijn(self, n, d): raise ValueError("order must be greater than or equal to one") if d < 1: raise ValueError("degree must be greater than or equal to one") + if name is None: + name = f"Generalized de Bruijn digraph (n={n}, d={d})" - GB = DiGraph(n, loops=True, multiedges=True, - name="Generalized de Bruijn digraph (n={}, d={})".format(n, d)) - for u in range(n): - for a in range(u * d, u * d + d): - GB.add_edge(u, a % n) - return GB + edges = ((u, a % n) for u in range(n) for a in range(u * d, u * d + d)) + return DiGraph([range(n), edges], format='vertices_and_edges', + loops=True, multiedges=True, immutable=immutable, + name=name) - def ImaseItoh(self, n, d): + def ImaseItoh(self, n, d, immutable=False, name=None): r""" Return the Imase-Itoh digraph of order `n` and degree `d`. @@ -1145,6 +1160,12 @@ def ImaseItoh(self, n, d): - ``d`` -- integer; degree of the digraph (must be greater than or equal to one) + - ``immutable`` -- boolean (default: ``False``); whether to return + an immutable or mutable digraph. + + - ``name`` -- string (default: ``None``); when set, the specified name + is used instead of the default one. + EXAMPLES:: sage: II = digraphs.ImaseItoh(8, 2) @@ -1181,15 +1202,15 @@ def ImaseItoh(self, n, d): raise ValueError("order must be greater than or equal to two") if d < 1: raise ValueError("degree must be greater than or equal to one") + if name is None: + name = f"Imase and Itoh digraph (n={n}, d={d})" - II = DiGraph(n, loops=True, multiedges=True, - name="Imase and Itoh digraph (n={}, d={})".format(n, d)) - for u in range(n): - for a in range(-u * d - d, -u * d): - II.add_edge(u, a % n) - return II + edges = ((u, a % n) for u in range(n) for a in range(-u * d - d, -u * d)) + return DiGraph([range(n), edges], format='vertices_and_edges', + loops=True, multiedges=True, immutable=immutable, + name=name) - def Kautz(self, k, D, vertices='strings'): + def Kautz(self, k, D, vertices='strings', immutable=False): r""" Return the Kautz digraph of degree `d` and diameter `D`. @@ -1224,6 +1245,9 @@ def Kautz(self, k, D, vertices='strings'): are words over an alphabet (default) or integers (``vertices='strings'``) + - ``immutable`` -- boolean (default: ``False``); whether to return + an immutable or mutable digraph. + EXAMPLES:: sage: # needs sage.combinat @@ -1298,6 +1322,8 @@ def Kautz(self, k, D, vertices='strings'): from sage.rings.integer import Integer + name = f"Kautz digraph (k={k}, D={D})" + if vertices == 'strings': from sage.combinat.words.words import Words @@ -1315,26 +1341,25 @@ def Kautz(self, k, D, vertices='strings'): V = VV # We now build the set of arcs - G = DiGraph() - for u in V: - us = u.string_rep() - for a in my_alphabet: - if not u.has_suffix(a): - G.add_edge(us, (u[1:] * a).string_rep(), - a.string_rep()) + def edges(): + for u in V: + us = u.string_rep() + yield from ((us, (u[1:] * a).string_rep(), a.string_rep()) + for a in my_alphabet if not u.has_suffix(a)) + + return DiGraph(edges(), format='list_of_edges', + name=name, immutable=immutable) elif vertices == 'integers': d = k if isinstance(k, Integer) else (len(list(k)) - 1) if d < 1: raise ValueError("degree must be greater than or equal to one") - G = digraphs.ImaseItoh((d + 1) * (d ** (D - 1)), d) + return digraphs.ImaseItoh((d + 1) * (d ** (D - 1)), d, + name=name, immutable=immutable) else: raise ValueError('unknown type for vertices') - G.name("Kautz digraph (k={}, D={})".format(k, D)) - return G - def RandomDirectedAcyclicGraph(self, n, p, weight_max=None): r""" Return a random (weighted) directed acyclic graph of order `n`. From 7b296b74973d26c4c35a8826871b6594f5ed68a2 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 12:57:34 +0100 Subject: [PATCH 215/507] #39266: fix lint issue --- src/sage/graphs/digraph_generators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 8d57586f65c..fde5107bcb4 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -1042,6 +1042,7 @@ def DeBruijn(self, k, n, vertices='strings', immutable=True): if not n: multiedges = True + def edges(): v = W[0] vs = v.string_rep() @@ -1049,6 +1050,7 @@ def edges(): else: multiedges = False + def edges(): for w in W: ww = w[1:] From 1c63178e1c012acff8006ec281e2b2e4b5da076d Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 14:44:13 +0100 Subject: [PATCH 216/507] #39266: fix default parameter in DeBruijn --- src/sage/graphs/digraph_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index fde5107bcb4..b7200af58f8 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -950,7 +950,7 @@ def Circulant(self, n, integers): return G - def DeBruijn(self, k, n, vertices='strings', immutable=True): + def DeBruijn(self, k, n, vertices='strings', immutable=False): r""" Return the De Bruijn digraph with parameters `k,n`. From 4cce1f5430b8798abb705b692724d20c02e96ff2 Mon Sep 17 00:00:00 2001 From: "Rusydi H. Makarim" Date: Fri, 10 Jan 2025 12:05:54 +0700 Subject: [PATCH 217/507] Add inversion function for sage.crypto.sboxes Add inversion mapping in GF(2^n) extending 0 -> 0. Such mapping is used for instance in the AES S-Box. --- src/sage/crypto/sboxes.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/crypto/sboxes.py b/src/sage/crypto/sboxes.py index ab0f2759573..cd628632122 100644 --- a/src/sage/crypto/sboxes.py +++ b/src/sage/crypto/sboxes.py @@ -399,6 +399,27 @@ def monomial_function(n, e): return SBox(X**e) +def inversion(n): + r""" + Return the S-Box constructed from the inversion mapping over `\GF{2^n}` extending `0 \mapsto 0` + + INPUT: + + - ``n`` -- size of the S-Box (i.e. the degree of the finite field extension) + + EXAMPLES:: + + sage: from sage.crypto.sboxes import inversion + sage: S4 = inversion(4) + sage: S4.differential_uniformity() + 4 + sage: S5 = inversion(5) + sage: S5.differential_uniformity() + 2 + """ + return monomial_function(n, 2**n - 2) + + # Bijective S-Boxes mapping 9 bits to 9 # ===================================== From 3589aa62521e66c5f4f9cb98e90c7be23840212c Mon Sep 17 00:00:00 2001 From: "Rusydi H. Makarim" Date: Fri, 17 Jan 2025 14:18:33 +0700 Subject: [PATCH 218/507] adjustment to comply with PEP8 --- src/sage/crypto/sboxes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/crypto/sboxes.py b/src/sage/crypto/sboxes.py index cd628632122..e1a1172baad 100644 --- a/src/sage/crypto/sboxes.py +++ b/src/sage/crypto/sboxes.py @@ -401,11 +401,12 @@ def monomial_function(n, e): def inversion(n): r""" - Return the S-Box constructed from the inversion mapping over `\GF{2^n}` extending `0 \mapsto 0` + Return the S-Box constructed from the inversion mapping over `\GF{2^n}` + extending `0 \mapsto 0`. INPUT: - - ``n`` -- size of the S-Box (i.e. the degree of the finite field extension) + - ``n`` -- size of the S-Box EXAMPLES:: From 886bad49b3bfa707011e67ec3787ad52a4b43d65 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 17 Jan 2025 18:30:40 +0100 Subject: [PATCH 219/507] rename also set-like species --- src/sage/rings/species.py | 116 ++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index 494e7020331..14d2facb8f3 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -33,6 +33,7 @@ from itertools import accumulate, chain, product +from sage.arith.misc import divisors from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis from sage.categories.modules import Modules from sage.categories.monoids import Monoids @@ -48,9 +49,10 @@ from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.libs.gap.libgap import libgap -from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_method, cached_function from sage.misc.fast_methods import WithEqualityById from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.misc_c import prod from sage.modules.free_module_element import vector from sage.monoids.indexed_free_monoid import (IndexedFreeAbelianMonoid, IndexedFreeAbelianMonoidElement) @@ -297,13 +299,13 @@ def __lt__(self, other): sage: len(P.cover_relations()) 7 sage: sorted(P.cover_relations(), key=str) - [[C_4, {((1,2)(3,4),)}], + [[C_4, E_2(X^2)], + [E_2(E_2), C_4], + [E_2(E_2), Pb_4], + [E_4, E_2(E_2)], [E_4, Eo_4], - [E_4, P_4], [Eo_4, Pb_4], - [P_4, C_4], - [P_4, Pb_4], - [Pb_4, {((1,2)(3,4),)}]] + [Pb_4, E_2(X^2)]] TESTS:: @@ -531,7 +533,7 @@ def _element_constructor_(self, G, pi=None, check=True): def _rename(self, n): r""" - Names for common species. + Give common species of degree `n` their traditional names. EXAMPLES:: @@ -589,6 +591,9 @@ def _rename(self, n): [(i, i+1) for i in range(1, n, 2)]] self(PermutationGroup(gens), pi, check=False).rename(f"Pb_{n}" + sort) + if self._arity == 1: + _ = _atomic_set_like_species(n) + def __contains__(self, x): r""" Return whether ``x`` is in ``self``. @@ -940,11 +945,11 @@ def _element_constructor_(self, G, pi=None, check=True): sage: a = lambda g, x: SetPartition([[g(e) for e in b] for b in x]) sage: X = SetPartitions(4, [2, 2]) sage: M((X, a, 'left'), {0: X.base_set()}) - P_4 + E_2(E_2) sage: X = SetPartitions(8, [4, 2, 2]) sage: M((X, a, 'left'), {0: X.base_set()}, check=False) - E_4*P_4 + E_4*E_2(E_2) TESTS:: @@ -1176,23 +1181,23 @@ def _richcmp_(self, other, op): sage: len(P.cover_relations()) 17 sage: sorted(P.cover_relations(), key=str) - [[C_4, {((1,2)(3,4),)}], + [[C_4, E_2(X^2)], + [E_2(E_2), C_4], + [E_2(E_2), E_2^2], + [E_2(E_2), Pb_4], + [E_2(X^2), X^4], + [E_2^2, E_2(X^2)], [E_2^2, X^2*E_2], - [E_2^2, {((1,2)(3,4),)}], + [E_4, E_2(E_2)], [E_4, Eo_4], - [E_4, P_4], [E_4, X*E_3], [Eo_4, Pb_4], [Eo_4, X*C_3], - [P_4, C_4], - [P_4, E_2^2], - [P_4, Pb_4], - [Pb_4, {((1,2)(3,4),)}], + [Pb_4, E_2(X^2)], [X*C_3, X^4], [X*E_3, X*C_3], [X*E_3, X^2*E_2], - [X^2*E_2, X^4], - [{((1,2)(3,4),)}, X^4]] + [X^2*E_2, X^4]] TESTS:: @@ -1439,7 +1444,7 @@ def __call__(self, *args): sage: X(E2) E_2 sage: E2(E2) - P_4 + E_2(E_2) sage: M = MolecularSpecies(["X","Y"]) sage: X = M(SymmetricGroup(1), {0: [1]}) @@ -1637,17 +1642,17 @@ def tilde(self): sage: P = PolynomialSpecies(QQ, "X") sage: sortkey = lambda x: (len(x[1]), sum(x[1].coefficients()), str(x[0])) sage: n=4; table(sorted([(m, P.monomial(m).tilde()) for m in M.subset(n)], key=sortkey)) - X^4 X^4 - X^2*E_2 2*X^2*E_2 - {((1,2)(3,4),)} 2*{((1,2)(3,4),)} - X*C_3 3*X*C_3 - C_4 4*C_4 - E_2^2 4*E_2^2 - Pb_4 4*Pb_4 - X*E_3 X*E_3 + X^2*E_2 + X*C_3 - Eo_4 Eo_4 + 2*X*C_3 + Pb_4 - P_4 2*P_4 + E_2^2 + Pb_4 + C_4 - E_4 E_4 + E_2^2 + X*C_3 + P_4 + C_4 + X^4 X^4 + E_2(X^2) 2*E_2(X^2) + X^2*E_2 2*X^2*E_2 + X*C_3 3*X*C_3 + C_4 4*C_4 + E_2^2 4*E_2^2 + Pb_4 4*Pb_4 + X*E_3 X*E_3 + X^2*E_2 + X*C_3 + Eo_4 Eo_4 + 2*X*C_3 + Pb_4 + E_2(E_2) 2*E_2(E_2) + E_2^2 + Pb_4 + C_4 + E_4 E_4 + E_2^2 + X*C_3 + E_2(E_2) + C_4 sage: P. = PolynomialSpecies(QQ) sage: E2 = PolynomialSpecies(QQ, "X")(SymmetricGroup(2)) @@ -1864,7 +1869,7 @@ def _compose_with_weighted_singletons(self, names, multiplicities, degrees): sage: C4 = P(CyclicPermutationGroup(4)) sage: C4._compose_with_weighted_singletons(["X"], [-1], [[4]]) - -C_4 + {((1,2)(3,4),)} + -C_4 + E_2(X^2) Exercise (2.5.17) in [BLL1998]_:: @@ -1894,7 +1899,7 @@ def _compose_with_weighted_singletons(self, names, multiplicities, degrees): TESTS:: sage: (C4+E2^2)._compose_with_weighted_singletons(["X"], [-1], [[4]]) - -C_4 + E_2^2 + {((1,2)(3,4),)} - 2*X^2*E_2 + X^4 + -C_4 + E_2^2 + E_2(X^2) - 2*X^2*E_2 + X^4 sage: C4._compose_with_weighted_singletons(["X"], [-1, 0], [[4]]) Traceback (most recent call last): @@ -1967,10 +1972,10 @@ def __call__(self, *args): sage: E2(-X) -E_2 + X^2 sage: E2(X^2) - {((1,2)(3,4),)} + E_2(X^2) sage: E2(X + X^2) - E_2 + X^3 + {((1,2)(3,4),)} + E_2 + X^3 + E_2(X^2) sage: P2 = PolynomialSpecies(QQ, ["X", "Y"]) sage: X = P2(SymmetricGroup(1), {0: [1]}) @@ -2194,7 +2199,7 @@ def _element_constructor_(self, G, pi=None, check=True): sage: X = SetPartitions(4, 2) sage: a = lambda g, x: SetPartition([[g(e) for e in b] for b in x]) sage: P((X, a, 'left'), {0: [1,2,3,4]}) - X*E_3 + P_4 + X*E_3 + E_2(E_2) The species of permutation groups:: @@ -2206,7 +2211,7 @@ def _element_constructor_(self, G, pi=None, check=True): ....: H = S.subgroup(G.conjugate(pi).gens()) ....: return next(K for K in X if K == H) sage: P((X, act), {0: range(1, n+1)}, check=False) - 4*E_4 + 4*P_4 + E_2^2 + 2*X*E_3 + 4*E_4 + 4*E_2(E_2) + E_2^2 + 2*X*E_3 Create a multisort species given an action:: @@ -2505,3 +2510,42 @@ def factor(s, c, d): for s in range(self._arity)) Element = PolynomialSpeciesElement + + +@cached_function +def _atomic_set_like_species(n): + r""" + Return a list of the atomic set like species of degree `n`, + and provide their traditional names. + + INPUT: + + - ``n`` -- positive integer, the degree + + EXAMPLES:: + + sage: from sage.rings.species import _atomic_set_like_species + sage: _atomic_set_like_species(6) + [E_2(E_3), E_2(X*E_2), E_2(X^3), E_3(E_2), E_3(X^2), E_6] + + sage: oeis([len(_atomic_set_like_species(n)) for n in range(1,10)]) # optional - internet + 0: A007650: Number of set-like atomic species of degree n. + """ + M = MolecularSpecies("X") + if n == 1: + return [M(SymmetricGroup(1))] + result = [] + for d in divisors(n): + if d == 1: + continue + E_d = M(SymmetricGroup(d)) + if d == n: + result.append(E_d) + continue + for pi in Partitions(n // d): + for l_F in cartesian_product([_atomic_set_like_species(p) for p in pi]): + G = prod(l_F) + F = E_d(G) + F.support()[0].rename(f"E_{d}({G})") + result.append(F) + return result From 81ded324ba86b26829c7a8bf2c31e71be63f70b5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 18 Jan 2025 18:03:18 +0100 Subject: [PATCH 220/507] multisort set-like species --- src/sage/rings/species.py | 46 +++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index 14d2facb8f3..e747ec1eee3 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -43,6 +43,7 @@ from sage.combinat.cyclic_sieving_phenomenon import orbit_decomposition from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.integer_vector import IntegerVectors +from sage.combinat.integer_vector_weighted import WeightedIntegerVectors from sage.combinat.partition import Partitions, _Partitions from sage.combinat.sf.sf import SymmetricFunctions from sage.groups.perm_gps.constructor import PermutationGroupElement @@ -592,7 +593,7 @@ def _rename(self, n): self(PermutationGroup(gens), pi, check=False).rename(f"Pb_{n}" + sort) if self._arity == 1: - _ = _atomic_set_like_species(n) + _ = _atomic_set_like_species(n, self._names) def __contains__(self, x): r""" @@ -2513,7 +2514,7 @@ def factor(s, c, d): @cached_function -def _atomic_set_like_species(n): +def _atomic_set_like_species(n, names): r""" Return a list of the atomic set like species of degree `n`, and provide their traditional names. @@ -2521,31 +2522,48 @@ def _atomic_set_like_species(n): INPUT: - ``n`` -- positive integer, the degree + - ``names`` -- an iterable of strings for the sorts of the + species EXAMPLES:: sage: from sage.rings.species import _atomic_set_like_species - sage: _atomic_set_like_species(6) + sage: _atomic_set_like_species(6, "X") [E_2(E_3), E_2(X*E_2), E_2(X^3), E_3(E_2), E_3(X^2), E_6] - sage: oeis([len(_atomic_set_like_species(n)) for n in range(1,10)]) # optional - internet + sage: l = [len(_atomic_set_like_species(n, "X")) for n in range(12)] + sage: l + [0, 1, 1, 1, 3, 1, 6, 1, 10, 4, 12, 1] + sage: oeis(l) # optional - internet 0: A007650: Number of set-like atomic species of degree n. + + sage: _atomic_set_like_species(4, "U, V") + [E_2(E_2(V)), E_2(E_2(U)), E_2(V^2), E_2(U*V), E_2(U^2), E_4(U), E_4(V)] """ - M = MolecularSpecies("X") + if not n: + return [] + M1 = MolecularSpecies("X") + M = MolecularSpecies(names) if n == 1: - return [M(SymmetricGroup(1))] + return [M(SymmetricGroup(1), {s: [1]}) for s in range(M._arity)] result = [] for d in divisors(n): if d == 1: continue - E_d = M(SymmetricGroup(d)) if d == n: - result.append(E_d) + result.extend(M(SymmetricGroup(n), {s: range(1, n+1)}) + for s in range(M._arity)) continue - for pi in Partitions(n // d): - for l_F in cartesian_product([_atomic_set_like_species(p) for p in pi]): - G = prod(l_F) - F = E_d(G) - F.support()[0].rename(f"E_{d}({G})") - result.append(F) + E_d = M1(SymmetricGroup(d)) + l = [] + w = [] + for degree in range(1, n // d + 1): + a_degree = _atomic_set_like_species(degree, names) + l.extend(a_degree) + w.extend([degree]*len(a_degree)) + for a in WeightedIntegerVectors(n // d, w): + G = prod(F ** e for F, e in zip(l, a)) + F = E_d(G) + F.support()[0].rename(f"E_{d}({G})") + result.append(F) return result From f4bc04ca179f0e290801cd732578fa76e55a6ccd Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 18 Jan 2025 18:44:44 +0100 Subject: [PATCH 221/507] activate renaming also in the multisort case --- src/sage/rings/species.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index e747ec1eee3..810256b42b5 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -592,8 +592,7 @@ def _rename(self, n): [(i, i+1) for i in range(1, n, 2)]] self(PermutationGroup(gens), pi, check=False).rename(f"Pb_{n}" + sort) - if self._arity == 1: - _ = _atomic_set_like_species(n, self._names) + _atomic_set_like_species(n, self._names) def __contains__(self, x): r""" From 5f0537dd54e1ec4dd256e79a859efd45a58aeb1d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 20 Jan 2025 09:19:03 +0100 Subject: [PATCH 222/507] fix doctests --- src/sage/rings/species.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index 810256b42b5..10b4d7a4860 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -453,7 +453,7 @@ def _element_constructor_(self, G, pi=None, check=True): sage: G = PermutationGroup([[(1,3),(5,7)]], domain=[1,3,5,7]) sage: A(G, ([1,3], [5,7])) - {((1,2)(3,4),): ({1, 2}, {3, 4})} + E_2(X*Y) Test that errors are raised on some possible misuses:: @@ -549,9 +549,9 @@ def _rename(self, n): sage: A(CyclicPermutationGroup(4), {1: range(1, 5)}) C_4(Y) sage: A(DihedralGroup(4), {0: range(1, 5)}) - P_4(X) + E_2(E_2(X)) sage: A(DihedralGroup(4), {1: range(1, 5)}) - P_4(Y) + E_2(E_2(Y)) sage: A(AlternatingGroup(4), {0: range(1, 5)}) Eo_4(X) sage: A(AlternatingGroup(4), {1: range(1, 5)}) @@ -722,11 +722,7 @@ def _an_element_(self): sage: A = AtomicSpecies("X, Y") sage: a = A.an_element(); a - {((1,2)(3,4),): ({1, 2}, {3, 4})} - - sage: a.rename("E_2(XY)") - sage: a - E_2(XY) + E_2(X*Y) """ G = PermutationGroup([[(2 * s + 1, 2 * s + 2) for s in range(self._arity)]]) m = {s: [2 * s + 1, 2 * s + 2] for s in range(self._arity)} @@ -850,7 +846,7 @@ class MolecularSpecies(IndexedFreeAbelianMonoid): sage: M = MolecularSpecies("X,Y") sage: G = PermutationGroup([[(1,2),(3,4)], [(5,6)]]) sage: M(G, {0: [5,6], 1: [1,2,3,4]}) - E_2(X)*{((1,2)(3,4),): ({}, {1, 2, 3, 4})} + E_2(X)*E_2(Y^2) """ @staticmethod def __classcall__(cls, names): @@ -961,7 +957,7 @@ def _element_constructor_(self, G, pi=None, check=True): sage: G = PermutationGroup([[(2,3),(4,5)]], domain=[2,3,4,5]) sage: M(G, {0: [2, 3], 1: [4, 5]}) - E_2(XY) + E_2(X*Y) sage: X = SetPartitions(4, 2) sage: a = lambda g, x: SetPartition([[g(e) for e in b] for b in x]) @@ -972,7 +968,7 @@ def _element_constructor_(self, G, pi=None, check=True): sage: G = PermutationGroup([[(1,3),(5,7)]], domain=[1,3,5,7]) sage: M(G, ([1,3], [5,7])) - E_2(XY) + E_2(X*Y) sage: G = PermutationGroup([[(1,2), (3,4,5,6)]]) sage: a = M(G, {0:[1,2], 1:[3,4,5,6]}) @@ -1254,7 +1250,7 @@ def permutation_group(self): sage: M = MolecularSpecies("X,Y") sage: G = PermutationGroup([[(1,2),(3,4)], [(5,6)]]) sage: A = M(G, {0: [5,6], 1: [1,2,3,4]}); A - E_2(X)*{((1,2)(3,4),): ({}, {1, 2, 3, 4})} + E_2(X)*E_2(Y^2) sage: A.permutation_group() (Permutation Group with generators [(3,4)(5,6), (1,2)], (frozenset({1, 2}), frozenset({3, 4, 5, 6}))) @@ -1279,7 +1275,7 @@ def permutation_group(self): sage: G = PermutationGroup([[(1,2),(3,4)], [(5,6)]]) sage: A = M(G, {0: [5,6], 1: [1,2,3,4]}) sage: A * B - E_2(X)*C_3(X)*{((1,2)(3,4),): ({}, {1, 2, 3, 4})} + E_2(X)*C_3(X)*E_2(Y^2) sage: (A*B).permutation_group() (Permutation Group with generators [(6,7)(8,9), (3,4,5), (1,2)], (frozenset({1, 2, 3, 4, 5}), frozenset({6, 7, 8, 9}))) @@ -1657,7 +1653,7 @@ def tilde(self): sage: P. = PolynomialSpecies(QQ) sage: E2 = PolynomialSpecies(QQ, "X")(SymmetricGroup(2)) sage: E2(X*Y).tilde() - 2*E_2(XY) + 2*E_2(X*Y) """ P = self.parent() M = P._indices @@ -1781,7 +1777,7 @@ def _compose_with_singletons(self, names, args): sage: P = PolynomialSpecies(ZZ, "X") sage: C4 = P(CyclicPermutationGroup(4)) sage: C4._compose_with_singletons("X, Y", [[2, 2]]) - E_2(XY) + X^2*Y^2 + E_2(X*Y) + X^2*Y^2 sage: P = PolynomialSpecies(ZZ, ["X", "Y"]) sage: F = P(PermutationGroup([[(1,2,3), (4,5,6)]]), {0: [1,2,3], 1: [4,5,6]}) @@ -1874,7 +1870,7 @@ def _compose_with_weighted_singletons(self, names, multiplicities, degrees): Exercise (2.5.17) in [BLL1998]_:: sage: C4._compose_with_weighted_singletons(["X", "Y"], [1, 1], [[2, 2]]) - E_2(XY) + X^2*Y^2 + E_2(X*Y) + X^2*Y^2 sage: C4._compose_with_weighted_singletons(["X", "Y"], [1, 1], [[3, 1]]) X^3*Y sage: C4._compose_with_weighted_singletons(["X", "Y"], [1, 1], [[4, 0]]) @@ -1883,7 +1879,7 @@ def _compose_with_weighted_singletons(self, names, multiplicities, degrees): Equation (4.60) in [ALL2002]_:: sage: C4._compose_with_weighted_singletons(["X", "Y"], [1, -1], [[2, 2]]) - -E_2(XY) + 2*X^2*Y^2 + -E_2(X*Y) + 2*X^2*Y^2 A bivariate example:: @@ -1984,7 +1980,7 @@ def __call__(self, *args): E_2(X) + X*Y + E_2(Y) sage: E2(X*Y)(E2(X), E2(Y)) - {((7,8), (5,6), (3,4), (1,2), (1,3)(2,4)(5,7)(6,8)): ({1, 2, 3, 4}, {5, 6, 7, 8})} + E_2(E_2(X)*E_2(Y)) sage: R. = QQ[] sage: P = PolynomialSpecies(R, ["X"]) @@ -2193,7 +2189,7 @@ def _element_constructor_(self, G, pi=None, check=True): sage: X = SetPartitions(4, 2) sage: a = lambda g, x: SetPartition([[g(e) for e in b] for b in x]) sage: P((X, a, 'left'), {0: [1,2], 1: [3,4]}) - E_2(X)*E_2(Y) + X^2*E_2(Y) + E_2(XY) + Y^2*E_2(X) + E_2(X)*E_2(Y) + X^2*E_2(Y) + E_2(X*Y) + Y^2*E_2(X) sage: P = PolynomialSpecies(ZZ, ["X"]) sage: X = SetPartitions(4, 2) From 20ac0fb2c48731afab4870304c4bd31643a672c6 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:41:06 +0700 Subject: [PATCH 223/507] Allow coercion from Frac(QQ[x]) to LaurentSeriesRing(QQ) --- src/sage/rings/laurent_series_ring.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index 1feba6e675b..6cb56980190 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -649,6 +649,8 @@ def _coerce_map_from_(self, P): True sage: S.has_coerce_map_from(PolynomialRing(ZZ, 't')) True + sage: S.has_coerce_map_from(Frac(PolynomialRing(ZZ, 't'))) + False sage: S.has_coerce_map_from(LaurentPolynomialRing(ZZ, 't')) True sage: S.has_coerce_map_from(PowerSeriesRing(ZZ, 't')) @@ -671,6 +673,12 @@ def _coerce_map_from_(self, P): sage: S.has_coerce_map_from(LaurentSeriesRing(QQ, 't')) False + sage: T. = LaurentSeriesRing(QQ) + sage: T.has_coerce_map_from(QQ['t']) + True + sage: T.has_coerce_map_from(Frac(QQ['t'])) + True + sage: R. = LaurentSeriesRing(QQ['x']) sage: R.has_coerce_map_from(QQ[['t']]) True @@ -687,7 +695,10 @@ def _coerce_map_from_(self, P): sage: R.has_coerce_map_from(LazyLaurentSeriesRing(ZZ['x'], 't')) True """ + from sage.rings.fraction_field import FractionField_generic A = self.base_ring() + if isinstance(P, FractionField_generic) and A.is_field(): + return self.has_coerce_map_from(P.base()) if (isinstance(P, (LaurentSeriesRing, LazyLaurentSeriesRing, LaurentPolynomialRing_generic, PowerSeriesRing_generic, LazyPowerSeriesRing, From d11d805e4d36256507a7ebe78f11acd8c86e3646 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:47:30 +0700 Subject: [PATCH 224/507] Add documentation to LaurentSeries point to accessors --- src/sage/rings/laurent_series_ring_element.pyx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 405752c6191..85d716b8c7a 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -97,9 +97,11 @@ cdef class LaurentSeries(AlgebraElement): - ``parent`` -- a Laurent series ring - ``f`` -- a power series (or something can be coerced - to one); note that ``f`` does *not* have to be a unit + to one); note that ``f`` does *not* have to be a unit. + This can be accessed through :meth:`valuation_zero_part`. - - ``n`` -- (default: 0) integer + - ``n`` -- (default: 0) integer. This can be accessed + through :meth:`valuation`. """ def __init__(self, parent, f, n=0): r""" From 023415a4405361441b9fd7f9a36dfec03e243ad2 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 26 Jan 2025 15:49:37 +0100 Subject: [PATCH 225/507] Allow system python 3.13 Fixes archlinux CI --- build/pkgs/python3/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index c6938ea7080..a9433207975 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,8 +1,8 @@ SAGE_SPKG_CONFIGURE([python3], [ m4_pushdef([MIN_VERSION], [3.9.0]) m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.9.0]) - m4_pushdef([LT_STABLE_VERSION], [3.13.0]) - m4_pushdef([LT_VERSION], [3.13.0]) + m4_pushdef([LT_STABLE_VERSION], [3.14.0]) + m4_pushdef([LT_VERSION], [3.14.0]) AC_ARG_WITH([python], [AS_HELP_STRING([--with-python=PYTHON3], [Python 3 executable to use for the Sage venv; default: python3])]) From aca1905dd8b735deec586ad980b7b21a2f284b47 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 26 Jan 2025 18:38:21 +0100 Subject: [PATCH 226/507] Allow Python 3.13 for sage-setup --- pkgs/sage-setup/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/sage-setup/pyproject.toml b/pkgs/sage-setup/pyproject.toml index a8cac2c6a64..820da4fbbeb 100644 --- a/pkgs/sage-setup/pyproject.toml +++ b/pkgs/sage-setup/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", ] urls = {Homepage = "https://www.sagemath.org"} -requires-python = ">=3.9, <3.13" +requires-python = ">=3.9, <3.14" dependencies = [] dynamic = ["version"] From d332d958307ffc5f4df6bba10a9ab45106e08f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 27 Jan 2025 21:48:19 +0100 Subject: [PATCH 227/507] details in braid groups --- src/sage/groups/braid.py | 103 ++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index ce0375b121f..d67b4c8b8e5 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -4,7 +4,8 @@ Braid groups are implemented as a particular case of finitely presented groups, but with a lot of specific methods for braids. -A braid group can be created by giving the number of strands, and the name of the generators:: +A braid group can be created by giving the number of strands, and the name +of the generators:: sage: BraidGroup(3) Braid group on 3 strands @@ -15,8 +16,8 @@ sage: BraidGroup(3,'a,b').gens() (a, b) -The elements can be created by operating with the generators, or by passing a list -with the indices of the letters to the group:: +The elements can be created by operating with the generators, or by passing +a list with the indices of the letters to the group:: sage: B. = BraidGroup(4) sage: s0*s1*s0 @@ -81,7 +82,8 @@ GroupMorphismWithGensImages, ) from sage.groups.free_group import FreeGroup, is_FreeGroup -from sage.groups.perm_gps.permgroup_named import SymmetricGroup, SymmetricGroupElement +from sage.groups.perm_gps.permgroup_named import (SymmetricGroup, + SymmetricGroupElement) from sage.libs.gap.libgap import libgap from sage.matrix.constructor import identity_matrix, matrix from sage.misc.cachefunc import cached_method @@ -96,10 +98,11 @@ from sage.structure.richcmp import rich_to_bool, richcmp lazy_import('sage.libs.braiding', - ['leftnormalform', 'rightnormalform', 'centralizer', 'supersummitset', 'greatestcommondivisor', + ['leftnormalform', 'rightnormalform', 'centralizer', + 'supersummitset', 'greatestcommondivisor', 'leastcommonmultiple', 'conjugatingbraid', 'ultrasummitset', 'thurston_type', 'rigidity', 'sliding_circuits', 'send_to_sss', - 'send_to_uss', 'send_to_sc', 'trajectory', 'cyclic_slidings' ], + 'send_to_uss', 'send_to_sc', 'trajectory', 'cyclic_slidings'], feature=sage__libs__braiding()) lazy_import('sage.knots.knot', 'Knot') @@ -466,7 +469,8 @@ def permutation(self, W=None): """ return self.coxeter_group_element(W) - def plot(self, color='rainbow', orientation='bottom-top', gap=0.05, aspect_ratio=1, axes=False, **kwds): + def plot(self, color='rainbow', orientation='bottom-top', gap=0.05, + aspect_ratio=1, axes=False, **kwds): """ Plot the braid. @@ -598,7 +602,7 @@ def plot(self, color='rainbow', orientation='bottom-top', gap=0.05, aspect_ratio def plot3d(self, color='rainbow'): """ - Plots the braid in 3d. + Plot the braid in 3d. The following option is available: @@ -796,6 +800,7 @@ def links_gould_matrix(self, symbolics=False): r""" Return the representation matrix of ``self`` according to the R-matrix representation being attached to the quantum superalgebra `\mathfrak{sl}_q(2|1)`. + See [MW2012]_, section 3 and references given there. INPUT: @@ -827,15 +832,16 @@ def links_gould_matrix(self, symbolics=False): M = rep[0][0].parent().one() for i in self.Tietze(): if i > 0: - M = M*rep[i-1][0] + M = M * rep[i-1][0] if i < 0: - M = M*rep[-i-1][1] + M = M * rep[-i-1][1] return M @cached_method def links_gould_polynomial(self, varnames=None, use_symbolics=False): r""" Return the Links-Gould polynomial of the closure of ``self``. + See [MW2012]_, section 3 and references given there. INPUT: @@ -872,12 +878,13 @@ def links_gould_polynomial(self, varnames=None, use_symbolics=False): mu = rep[ln - 1] # quantum trace factor M = mu * self.links_gould_matrix(symbolics=use_symbolics) d1, d2 = M.dimensions() - e = d1//4 + e = d1 // 4 B = M.base_ring() R = LaurentPolynomialRing(ZZ, varnames) # partial quantum trace according to I. Marin section 2.5 - part_trace = matrix(B, 4, 4, lambda i, j: sum(M[e * i + k, e * j + k] for k in range(e))) + part_trace = matrix(B, 4, 4, lambda i, j: sum(M[e * i + k, e * j + k] + for k in range(e))) ptemp = part_trace[0, 0] # part_trace == psymb*M.parent().one() if use_symbolics: v1, v2 = R.variable_names() @@ -888,13 +895,13 @@ def links_gould_polynomial(self, varnames=None, use_symbolics=False): else: ltemp = ptemp.lift().constant_coefficient() # Since the result of the calculation is known to be a Laurent polynomial - # in t0 and t1 all exponents of ltemp must be divisable by 2 + # in t0 and t1 all exponents of ltemp must be divisible by 2 L = ltemp.parent() lred = L({(k[0]/2, k[1]/2): v for k, v in ltemp.monomial_coefficients().items()}) t0, t1 = R.gens() return lred(t0, t1) - def tropical_coordinates(self): + def tropical_coordinates(self) -> list: r""" Return the tropical coordinates of ``self`` in the braid group `B_n`. @@ -938,7 +945,7 @@ def tropical_coordinates(self): from sage.rings.semirings.tropical_semiring import TropicalSemiring T = TropicalSemiring(ZZ) - return [T(_) for _ in coord] + return [T(c) for c in coord] def markov_trace(self, variab=None, normalized=True): r""" @@ -1606,7 +1613,7 @@ def right_normal_form(self): B = self.parent() return tuple([B(b) for b in rnf[:-1]] + [B.delta()**rnf[-1][0]]) - def centralizer(self): + def centralizer(self) -> list: """ Return a list of generators of the centralizer of the braid. @@ -1621,7 +1628,7 @@ def centralizer(self): B = self.parent() return [B._element_from_libbraiding(b) for b in c] - def super_summit_set(self): + def super_summit_set(self) -> list: """ Return a list with the super summit set of the braid. @@ -1739,10 +1746,9 @@ def conjugating_braid(self, other): cb = conjugatingbraid(self, other) if not cb: return None - else: - B = self.parent() - cb[0][0] %= 2 - return B._element_from_libbraiding(cb) + B = self.parent() + cb[0][0] %= 2 + return B._element_from_libbraiding(cb) def is_conjugated(self, other) -> bool: """ @@ -1859,7 +1865,7 @@ def pure_conjugating_braid(self, other): n2 = len(b2.Tietze()) return b2 if n2 <= n0 else b0 - def ultra_summit_set(self): + def ultra_summit_set(self) -> list: """ Return a list with the orbits of the ultra summit set of ``self``. @@ -1888,7 +1894,7 @@ def ultra_summit_set(self): B = self.parent() return [[B._element_from_libbraiding(i) for i in s] for s in uss] - def thurston_type(self): + def thurston_type(self) -> str: """ Return the thurston_type of ``self``. @@ -1973,7 +1979,7 @@ def rigidity(self): """ return Integer(rigidity(self)) - def sliding_circuits(self): + def sliding_circuits(self) -> list: """ Return the sliding circuits of the braid. @@ -2270,7 +2276,7 @@ def ultra_summit_set_element(self): B = self.parent() return tuple([B._element_from_libbraiding(b) for b in to_uss]) - def sliding_circuits_element(self): + def sliding_circuits_element(self) -> tuple: r""" Return an element of the braid's sliding circuits, and the conjugating braid. @@ -2286,7 +2292,7 @@ def sliding_circuits_element(self): B = self.parent() return tuple([B._element_from_libbraiding(b) for b in to_sc]) - def trajectory(self): + def trajectory(self) -> list: r""" Return the braid's trajectory. @@ -2304,7 +2310,7 @@ def trajectory(self): B = self.parent() return [B._element_from_libbraiding(b) for b in traj] - def cyclic_slidings(self): + def cyclic_slidings(self) -> list: r""" Return the braid's cyclic slidings. @@ -2484,6 +2490,7 @@ def reduced_word(self): def tuple_to_word(q_tuple): return M.prod(self._gens[i]**exp for i, exp in enumerate(q_tuple)) + ret = {tuple_to_word(q_tuple): q_factor for q_tuple, q_factor in self.tuples.items() if q_factor} return self._algebra._from_dict(ret, remove_zeros=False) @@ -2546,7 +2553,7 @@ def eps_monom(q_tuple): return sum(q_factor * eps_monom(q_tuple) for q_tuple, q_factor in self.tuples.items()) - def __repr__(self): + def __repr__(self) -> str: r""" String representation of ``self``. @@ -2582,7 +2589,7 @@ class BraidGroup_class(FiniteTypeArtinGroup): """ Element = Braid - def __init__(self, names): + def __init__(self, names) -> None: """ Python constructor. @@ -2657,7 +2664,7 @@ def __init__(self, names): # For caching hermitian form of unitary Burau representation self._hermitian_form = None - def __reduce__(self): + def __reduce__(self) -> tuple: """ TESTS:: @@ -2670,7 +2677,7 @@ def __reduce__(self): """ return (BraidGroup_class, (self.variable_names(), )) - def _repr_(self): + def _repr_(self) -> str: """ Return a string representation. @@ -2761,7 +2768,7 @@ def an_element(self): """ return self.gen(0) - def some_elements(self): + def some_elements(self) -> list: """ Return a list of some elements of the braid group. @@ -2778,7 +2785,7 @@ def some_elements(self): elements_list.append(elements_list[-1]**self.strands()) return elements_list - def _standard_lift_Tietze(self, p): + def _standard_lift_Tietze(self, p) -> tuple: """ Helper for :meth:`_standard_lift_Tietze`. @@ -2806,8 +2813,7 @@ def _standard_lift_Tietze(self, p): (1, 2, 3, 4, 1, 2) """ G = SymmetricGroup(self.strands()) - pl = G(p) - return tuple(pl.reduced_word()) + return tuple(G(p).reduced_word()) @cached_method def _links_gould_representation(self, symbolics=False): @@ -2853,8 +2859,9 @@ def _links_gould_representation(self, symbolics=False): else: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing LR = LaurentPolynomialRing(ZZ, 's0r, s1r') + s0r, s1r = LR.gens() PR = PolynomialRing(LR, 'Yr') - s0r, s1r, Yr = PR.gens_dict_recursive().values() + Yr = PR.gens() pqr = Yr**2 + (s0r**2 - 1) * (s1r**2 - 1) BR = PR.quotient_ring(pqr) s0 = BR(s0r) @@ -3419,9 +3426,10 @@ def mirror_involution(self): r""" Return the mirror involution of ``self``. - This automorphism maps a braid to another one by replacing each generator - in its word by the inverse. In general this is different from the inverse - of the braid since the order of the generators in the word is not reversed. + This automorphism maps a braid to another one by replacing + each generator in its word by the inverse. In general this is + different from the inverse of the braid since the order of the + generators in the word is not reversed. EXAMPLES:: @@ -3485,7 +3493,7 @@ def presentation_two_generators(self, isomorphisms=False): h2 = G.hom(codomain=self, im_gens=[self(a) for a in L2], check=False) return (G, h1, h2) - def epimorphisms(self, H): + def epimorphisms(self, H) -> list: r""" Return the epimorphisms from ``self`` to ``H``, up to automorphism of `H` passing through the :meth:`two generator presentation @@ -3518,12 +3526,15 @@ def epimorphisms(self, H): Gg = libgap(G) Hg = libgap(H) gquotients = Gg.GQuotients(Hg) - hom1g = libgap.GroupHomomorphismByImagesNC(G0g, Gg, [libgap(hom1(u)) for u in self.gens()]) + hom1g = libgap.GroupHomomorphismByImagesNC(G0g, Gg, + [libgap(hom1(u)) + for u in self.gens()]) g0quotients = [hom1g * h for h in gquotients] res = [] # the following closure is needed to attach a specific value of quo to # each function in the different morphisms - fmap = lambda tup: (lambda a: H(prod(tup[abs(i)-1]**sign(i) for i in a.Tietze()))) + fmap = lambda tup: (lambda a: H(prod(tup[abs(i)-1]**sign(i) + for i in a.Tietze()))) for quo in g0quotients: tup = tuple(H(quo.ImageElm(i.gap()).sage()) for i in self.gens()) fhom = GroupMorphismWithGensImages(HomSpace, fmap(tup)) @@ -3651,7 +3662,8 @@ class group of the punctured disk. sage: A = B.mapping_class_action(F) sage: A - Right action by Braid group on 4 strands on Free Group on generators {x0, x1, x2, x3} + Right action by Braid group on 4 strands on Free Group + on generators {x0, x1, x2, x3} sage: A(x0, s1) x0 sage: A(x1, s1) @@ -3659,14 +3671,15 @@ class group of the punctured disk. sage: A(x1^-1, s1) x1*x2^-1*x1^-1 """ - def __init__(self, G, M): + def __init__(self, G, M) -> None: """ TESTS:: sage: B = BraidGroup(3) sage: G = FreeGroup('a, b, c') sage: B.mapping_class_action(G) # indirect doctest - Right action by Braid group on 3 strands on Free Group on generators {a, b, c} + Right action by Braid group on 3 strands on Free Group + on generators {a, b, c} """ import operator Action.__init__(self, G, M, False, operator.mul) From 56c0322fd852be7787dbb2832291fa3d45d06d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 27 Jan 2025 21:50:32 +0100 Subject: [PATCH 228/507] fix typo --- src/sage/groups/braid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index d67b4c8b8e5..3348d15b525 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -2861,7 +2861,7 @@ def _links_gould_representation(self, symbolics=False): LR = LaurentPolynomialRing(ZZ, 's0r, s1r') s0r, s1r = LR.gens() PR = PolynomialRing(LR, 'Yr') - Yr = PR.gens() + Yr = PR.gen() pqr = Yr**2 + (s0r**2 - 1) * (s1r**2 - 1) BR = PR.quotient_ring(pqr) s0 = BR(s0r) From ce8e75f247527b3dcc0faad733316e3746bb3d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 31 Jan 2025 15:09:15 +0100 Subject: [PATCH 229/507] adding the super delta continued fractions, after G.-N. Han --- src/doc/en/reference/references/index.rst | 3 + src/sage/rings/power_series_ring_element.pyx | 87 ++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 31cb3ec8074..94874de227f 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3257,6 +3257,9 @@ REFERENCES: .. [Haj2000] \M. Hajiaghayi, *Consecutive Ones Property*, 2000. http://www-math.mit.edu/~hajiagha/pp11.ps +.. [Han2016] \G.-N. Han, *Hankel continued fraction and its applications*, + Adv. in Math., 303, 2016, pp. 295-321. + .. [Han1960] Haim Hanani, *On quadruple systems*, pages 145--157, vol. 12, diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 1d24794eb37..2a5dedc43e1 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1384,6 +1384,93 @@ cdef class PowerSeries(AlgebraElement): serie = (u - 1 - A * t) / (B * t ** 2) return tuple(resu) + def super_fraction(self, delta): + """ + Return the super delta continued fraction of ``self``. + + This is a fraction of the following shape:: + + v0 x^k0 + -------------------------------------------------------- + v1 x^(k0 + k1 + delta) + U1(x) - ------------------------------------------- + v2 x^(k1 +k2 + delta) + U2(x) - ------------------------- + U3(x) - ... + + where each `U_j(x) = 1 + u_j(x)x`. + + INPUT: + + - ``delta`` -- positive integer + + OUTPUT: + + The list of ( vj, kj, U(j+1) ), j \geq 0. + + REFERENCES: + + - [Han2016]_ + + EXAMPLES:: + + sage: deg = 30 + sage: PS = PowerSeriesRing(QQ, 'q', default_prec=deg+1) + sage: q = PS.gen() + sage: F = prod([(1+q**k).add_bigoh(deg+1) for k in range(1,deg)]) + sage: F.super_fraction(2) + [(1, 0, -q + 1), + (1, 1, q + 1), + (-1, 2, -q^3 + q^2 - q + 1), + (1, 1, q^2 + q + 1), + (-1, 0, -q + 1), + (-1, 1, q^2 + q + 1), + (-1, 0, -q + 1), + (1, 1, 3*q^2 + 2*q + 1), + (-4, 0, -q + 1)] + + sage: t = PowerSeriesRing(QQ, 't').gen() + sage: s = sum(factorial(k) * t**k for k in range(12)).O(12) + sage: s.super_fraction(2) + [(1, 0, -t + 1), + (1, 0, -3*t + 1), + (4, 0, -5*t + 1), + (9, 0, -7*t + 1), + (16, 0, -9*t + 1), + (25, 0, -11*t + 1)] + """ + ring = self.parent() + q = ring.gen() + Gi, Gj = ring.one(), self + + deg = self.prec() + + vkU = [] + + di = Gi.valuation() + ci = Gi[di] + + while True: + dj = Gj.valuation() + cj = Gj[dj] + k, v = dj - di, cj / ci + c = v * q**k + gi = Gi.add_bigoh(dj + delta) + gj = Gj.add_bigoh(k + dj + delta) + U = (c * gi / gj).truncate() + Gk = (U * Gj - Gi * c) >> (k + delta) + deg = deg - 2 * k - delta + if deg < 0: + break + vkU.append((v, k, U)) + if deg == 0: + break + if Gk.degree() == -1: + break + di, ci, Gi, Gj = dj, cj, Gj, Gk + + return vkU + def stieltjes_continued_fraction(self): r""" Return the Stieltjes continued fraction of ``self``. From 3da874e561f5e2f288d915af36ba76c953f17ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 31 Jan 2025 17:42:51 +0100 Subject: [PATCH 230/507] tweaks and details --- src/sage/rings/power_series_ring_element.pyx | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 2a5dedc43e1..045e497307d 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1384,11 +1384,11 @@ cdef class PowerSeries(AlgebraElement): serie = (u - 1 - A * t) / (B * t ** 2) return tuple(resu) - def super_fraction(self, delta): + def super_delta_fraction(self, delta): """ Return the super delta continued fraction of ``self``. - This is a fraction of the following shape:: + This is a continued fraction of the following shape:: v0 x^k0 -------------------------------------------------------- @@ -1398,15 +1398,15 @@ cdef class PowerSeries(AlgebraElement): U2(x) - ------------------------- U3(x) - ... - where each `U_j(x) = 1 + u_j(x)x`. + where each `U_j(x) = 1 + u_j(x) x`. INPUT: - - ``delta`` -- positive integer + - ``delta`` -- positive integer, most usually 2 OUTPUT: - The list of ( vj, kj, U(j+1) ), j \geq 0. + list of `(v_j, k_j, U_{j+1}(x))_{j \geq 0}` REFERENCES: @@ -1418,7 +1418,7 @@ cdef class PowerSeries(AlgebraElement): sage: PS = PowerSeriesRing(QQ, 'q', default_prec=deg+1) sage: q = PS.gen() sage: F = prod([(1+q**k).add_bigoh(deg+1) for k in range(1,deg)]) - sage: F.super_fraction(2) + sage: F.super_delta_fraction(2) [(1, 0, -q + 1), (1, 1, q + 1), (-1, 2, -q^3 + q^2 - q + 1), @@ -1431,7 +1431,7 @@ cdef class PowerSeries(AlgebraElement): sage: t = PowerSeriesRing(QQ, 't').gen() sage: s = sum(factorial(k) * t**k for k in range(12)).O(12) - sage: s.super_fraction(2) + sage: s.super_delta_fraction(2) [(1, 0, -t + 1), (1, 0, -3*t + 1), (4, 0, -5*t + 1), @@ -1439,18 +1439,15 @@ cdef class PowerSeries(AlgebraElement): (16, 0, -9*t + 1), (25, 0, -11*t + 1)] """ - ring = self.parent() - q = ring.gen() - Gi, Gj = ring.one(), self - + q = self.parent().gen() + Gi, Gj = self.parent().one(), self deg = self.prec() - vkU = [] - + list_vkU = [] di = Gi.valuation() ci = Gi[di] - while True: + while deg >= 0: dj = Gj.valuation() cj = Gj[dj] k, v = dj - di, cj / ci @@ -1459,17 +1456,17 @@ cdef class PowerSeries(AlgebraElement): gj = Gj.add_bigoh(k + dj + delta) U = (c * gi / gj).truncate() Gk = (U * Gj - Gi * c) >> (k + delta) - deg = deg - 2 * k - delta + deg -= 2 * k + delta if deg < 0: break - vkU.append((v, k, U)) + list_vkU.append((v, k, U)) if deg == 0: break if Gk.degree() == -1: break di, ci, Gi, Gj = dj, cj, Gj, Gk - return vkU + return list_vkU def stieltjes_continued_fraction(self): r""" From b31e86cceaa3a6bfffba3d0c6172478f971edfd5 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:06:23 +0700 Subject: [PATCH 231/507] Remove usages of reproducible_repr --- src/doc/en/developer/doctesting.rst | 13 +++++++++++++ src/sage/doctest/fixtures.py | 6 ++++++ src/sage/rings/polynomial/multi_polynomial_ideal.py | 3 +-- .../rings/polynomial/multi_polynomial_sequence.py | 9 ++++----- src/sage/rings/polynomial/pbori/pbori.pyx | 7 +++---- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 71145b8082a..a8913b4813a 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -1479,6 +1479,19 @@ to save any changes made in the file. After running the doctest fixer, it is a good idea to use ``git diff`` to check all edits that the automated tool made. +Note that in some cases the output in doctest may be slightly different from +the output in the actual Sage command-line (see :func:`sage.doctest.forker.init_sage`):: + + sage: set_random_seed(1) + sage: randint(1, 100) + 41 # actual + 83 # in doctest + sage: {3: 4, 1: 2} + {3: 4, 1: 2} # actual + {1: 2, 3: 4} # in doctest + +.. this whole file is marked nodoctest, so the example above is not tested + An alternative to this workflow is to use the option ``--keep-both``. When expected and actual output of an example differ, it duplicates the example, marking the two copies ``# optional - EXPECTED`` and ``# optional - GOT``. (Thus, when re-running the doctester, diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index ee812a8ecc9..d656dadf1c8 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -50,6 +50,12 @@ def reproducible_repr(val): r""" String representation of an object in a reproducible way. + .. NOTE:: + + This function is mostly superseded by the automatic sorting + of dictionary keys by a displayhook. See + :func:`sage.doctest.forker.init_sage`. + This tries to ensure that the returned string does not depend on factors outside the control of the doctest. One example is the order of elements in a hash-based structure. diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index d8546e31b33..b8e8b8af501 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -5500,8 +5500,7 @@ def weil_restriction(self): of Multivariate Polynomial Ring in x0, x1, x2, x3, x4, y0, y1, y2, y3, y4, z0, z1, z2, z3, z4 over Finite Field of size 3 sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal - sage: from sage.doctest.fixtures import reproducible_repr - sage: print(reproducible_repr(J.variety())) + sage: J.variety() [{x0: 1, x1: 0, x2: 0, x3: 0, x4: 0, y0: 0, y1: 0, y2: 0, y3: 0, y4: 0, z0: 0, z1: 0, z2: 0, z3: 0, z4: 0}] diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index ca6f864f807..d04958664fb 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1552,11 +1552,10 @@ def solve(self, algorithm='polybori', n=1, Without argument, a single arbitrary solution is returned:: sage: # needs sage.rings.polynomial.pbori - sage: from sage.doctest.fixtures import reproducible_repr sage: R. = BooleanPolynomialRing() sage: S = Sequence([x*y + z, y*z + x, x + y + z + 1]) sage: sol = S.solve() - sage: print(reproducible_repr(sol)) + sage: sol [{x: 0, y: 1, z: 0}] We check that it is actually a solution:: @@ -1567,7 +1566,7 @@ def solve(self, algorithm='polybori', n=1, We obtain all solutions:: sage: sols = S.solve(n=Infinity) # needs sage.rings.polynomial.pbori - sage: print(reproducible_repr(sols)) # needs sage.rings.polynomial.pbori + sage: sols # needs sage.rings.polynomial.pbori [{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}] sage: [S.subs(x) for x in sols] # needs sage.rings.polynomial.pbori [[0, 0, 0], [0, 0, 0]] @@ -1576,7 +1575,7 @@ def solve(self, algorithm='polybori', n=1, package ``FES`` is present:: sage: sol = S.solve(algorithm='exhaustive_search') # optional - fes # needs sage.rings.polynomial.pbori - sage: print(reproducible_repr(sol)) # optional - fes # needs sage.rings.polynomial.pbori + sage: sol # optional - fes # needs sage.rings.polynomial.pbori [{x: 1, y: 1, z: 1}] sage: S.subs(sol[0]) # optional - fes # needs sage.rings.polynomial.pbori [0, 0, 0] @@ -1584,7 +1583,7 @@ def solve(self, algorithm='polybori', n=1, And we may use SAT-solvers if they are available:: sage: sol = S.solve(algorithm='sat') # optional - pycryptosat # needs sage.rings.polynomial.pbori - sage: print(reproducible_repr(sol)) # optional - pycryptosat # needs sage.rings.polynomial.pbori + sage: sol # optional - pycryptosat # needs sage.rings.polynomial.pbori [{x: 0, y: 1, z: 0}] sage: S.subs(sol[0]) # needs sage.rings.polynomial.pbori [0, 0, 0] diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 404771a2c85..5745ec219fc 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -5031,10 +5031,9 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): A simple example:: - sage: from sage.doctest.fixtures import reproducible_repr sage: R. = BooleanPolynomialRing() sage: I = ideal( [ x*y*z + x*z + y + 1, x+y+z+1 ] ) - sage: print(reproducible_repr(I.variety())) + sage: I.variety() [{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}] TESTS: @@ -5052,13 +5051,13 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): ....: x1*x2 + x1*x4 + x1*x5 + x1*x6 + x2*x3 + x2*x4 + x2*x5 + x3*x5 + x5*x6 + x5 + x6, ....: x1*x2 + x1*x6 + x2*x4 + x2*x5 + x2*x6 + x3*x6 + x4*x6 + x5*x6 + x5] sage: I = R.ideal( polys ) - sage: print(reproducible_repr(I.variety())) + sage: I.variety() [{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}] sage: R = PolynomialRing(GF(2), 6, ['x%d'%(i+1) for i in range(6)], order='lex') sage: I = R.ideal( polys ) sage: v = (I + sage.rings.ideal.FieldIdeal(R)).variety() - sage: print(reproducible_repr(v)) + sage: v [{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}] From ef6e60f8630d332937f82a6f174494a61709627c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:24:39 +0700 Subject: [PATCH 232/507] Implement _repr_pretty_ for KeyConvertingDict --- src/sage/misc/converting_dict.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 684b729ea8c..14dc00eb94e 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -266,3 +266,27 @@ def update(self, *args, **kwds): if kwds: seq = ((f(k), v) for k, v in kwds.items()) u(seq) + + def _repr_pretty_(self, p, cycle): + """ + For pretty printing in the Sage command prompt. + + Since ``KeyConvertingDict`` inherits from ``dict``, we just use IPython's + built-in ``dict`` pretty printer. + When :issue:`36801` is fixed, this function will be redundant. + + EXAMPLES:: + + sage: from sage.misc.converting_dict import KeyConvertingDict + sage: d = KeyConvertingDict(int) + sage: d["3"] = 4 + sage: d["1"] = 2 + sage: repr(d) # dictionaries are insertion ordered since Python 3.6 + '{3: 4, 1: 2}' + sage: d # indirect doctest + {1: 2, 3: 4} + + The last example output will be ``{3: 4, 1: 2}`` outside of doctesting, + see :func:`sage.doctest.forker.init_sage`. + """ + p.pretty(dict(self)) From e4aba06f6ba536928cfe27828e327af0d179181c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:27:28 +0700 Subject: [PATCH 233/507] Fix doctests --- src/sage/doctest/fixtures.py | 2 +- .../rings/polynomial/multi_polynomial_ideal.py | 18 +++++++++++++++--- .../polynomial/multi_polynomial_sequence.py | 8 ++++---- src/sage/rings/polynomial/pbori/pbori.pyx | 8 +++++--- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index d656dadf1c8..b04316151eb 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -87,7 +87,7 @@ def reproducible_repr(val): frozenset(['a', 'b', 'c', 'd']) sage: print(reproducible_repr([1, frozenset("cab"), set("bar"), 0])) [1, frozenset(['a', 'b', 'c']), set(['a', 'b', 'r']), 0] - sage: print(reproducible_repr({3.0: "three", "2": "two", 1: "one"})) # optional - sage.rings.real_mpfr + sage: print(reproducible_repr({3.0: "three", "2": "two", 1: "one"})) # needs sage.rings.real_mpfr {'2': 'two', 1: 'one', 3.00000000000000: 'three'} sage: print(reproducible_repr("foo\nbar")) # demonstrate default case 'foo\nbar' diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index b8e8b8af501..e7d985ccb22 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -5501,9 +5501,21 @@ def weil_restriction(self): z0, z1, z2, z3, z4 over Finite Field of size 3 sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal sage: J.variety() - [{x0: 1, x1: 0, x2: 0, x3: 0, x4: 0, - y0: 0, y1: 0, y2: 0, y3: 0, y4: 0, - z0: 0, z1: 0, z2: 0, z3: 0, z4: 0}] + [{z4: 0, + z3: 0, + z2: 0, + z1: 0, + z0: 0, + y4: 0, + y3: 0, + y2: 0, + y1: 0, + y0: 0, + x4: 0, + x3: 0, + x2: 0, + x1: 0, + x0: 1}] Weil restrictions are often used to study elliptic curves over diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index d04958664fb..c68911d468b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -31,7 +31,7 @@ pair and study it:: sage: set_random_seed(1) - sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori + sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori ....: try: ....: F, s = sr.polynomial_system() ....: break @@ -122,7 +122,7 @@ easily:: sage: sr = mq.SR(1,1,1,4, gf2=True, polybori=True, order='lex') # needs sage.rings.polynomial.pbori - sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori + sage: while True: # workaround (see :issue:`31891`) # needs sage.rings.polynomial.pbori ....: try: ....: F, s = sr.polynomial_system() ....: break @@ -1556,7 +1556,7 @@ def solve(self, algorithm='polybori', n=1, sage: S = Sequence([x*y + z, y*z + x, x + y + z + 1]) sage: sol = S.solve() sage: sol - [{x: 0, y: 1, z: 0}] + [{z: 0, y: 1, x: 0}] We check that it is actually a solution:: @@ -1567,7 +1567,7 @@ def solve(self, algorithm='polybori', n=1, sage: sols = S.solve(n=Infinity) # needs sage.rings.polynomial.pbori sage: sols # needs sage.rings.polynomial.pbori - [{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}] + [{z: 0, y: 1, x: 0}, {z: 1, y: 1, x: 1}] sage: [S.subs(x) for x in sols] # needs sage.rings.polynomial.pbori [[0, 0, 0], [0, 0, 0]] diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 5745ec219fc..45a8fd1a82d 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -5034,7 +5034,7 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): sage: R. = BooleanPolynomialRing() sage: I = ideal( [ x*y*z + x*z + y + 1, x+y+z+1 ] ) sage: I.variety() - [{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}] + [{z: 0, y: 1, x: 0}, {z: 1, y: 1, x: 1}] TESTS: @@ -5052,13 +5052,15 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): ....: x1*x2 + x1*x6 + x2*x4 + x2*x5 + x2*x6 + x3*x6 + x4*x6 + x5*x6 + x5] sage: I = R.ideal( polys ) sage: I.variety() - [{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}] + [{x6: 0, x5: 0, x4: 0, x3: 0, x2: 0, x1: 0}, + {x6: 1, x5: 0, x4: 0, x3: 1, x2: 1, x1: 1}] sage: R = PolynomialRing(GF(2), 6, ['x%d'%(i+1) for i in range(6)], order='lex') sage: I = R.ideal( polys ) sage: v = (I + sage.rings.ideal.FieldIdeal(R)).variety() sage: v - [{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0}, {x1: 1, x2: 1, x3: 1, x4: 0, x5: 0, x6: 1}] + [{x6: 0, x5: 0, x4: 0, x3: 0, x2: 0, x1: 0}, + {x6: 1, x5: 0, x4: 0, x3: 1, x2: 1, x1: 1}] Check that :issue:`13976` is fixed:: From cbf590668f25b3738c7d78bb06c70ea16f87521a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:01:49 +0700 Subject: [PATCH 234/507] Use import_module instead of find_spec --- src/sage/misc/dev_tools.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index b11b2078129..2a49310d66a 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -171,7 +171,7 @@ def load_submodules(module=None, exclude_pattern=None): load sage.geometry.polyhedron.ppl_lattice_polygon... succeeded """ from .package_dir import walk_packages - import importlib.util + import importlib if module is None: import sage @@ -195,12 +195,8 @@ def load_submodules(module=None, exclude_pattern=None): try: sys.stdout.write("load %s..." % module_name) sys.stdout.flush() - # see - # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly - spec = importer.find_spec(module_name) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) + module = importlib.import_module(module_name) + assert sys.modules[module_name] is module sys.stdout.write(" succeeded\n") except (ValueError, AttributeError, TypeError, ImportError): # we might get error because of cython code that has been From 8c6b117fdab8651099aefadab21204cdce5478e1 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 1 Feb 2025 18:10:45 +0100 Subject: [PATCH 235/507] Minor code improvement in `sage.doctest` --- src/sage/doctest/control.py | 41 ++++++++++++------------ src/sage/doctest/external.py | 4 +-- src/sage/doctest/forker.py | 2 +- src/sage/doctest/parsing.py | 60 ++++++++++++++++++++---------------- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 49df68fe66e..bf12c78906c 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -32,28 +32,31 @@ # **************************************************************************** import importlib -import random +import json import os +import random +import shlex import sys import time -import json -import shlex import types + +from cysignals.signals import AlarmInterrupt, init_cysignals + import sage.misc.flatten import sage.misc.randstate as randstate -from sage.structure.sage_object import SageObject -from sage.env import DOT_SAGE, SAGE_LIB, SAGE_SRC, SAGE_VENV, SAGE_EXTCODE +from sage.doctest.external import available_software +from sage.doctest.forker import DocTestDispatcher +from sage.doctest.parsing import ( + optional_tag_regex, + parse_file_optional_tags, + unparse_optional_tags, +) +from sage.doctest.reporting import DocTestReporter +from sage.doctest.sources import DictAsObject, FileDocTestSource, get_basename +from sage.doctest.util import Timer, count_noun, dict_difference +from sage.env import DOT_SAGE, SAGE_EXTCODE, SAGE_LIB, SAGE_SRC from sage.misc.temporary_file import tmp_dir -from cysignals.signals import AlarmInterrupt, init_cysignals - -from .sources import FileDocTestSource, DictAsObject, get_basename -from .forker import DocTestDispatcher -from .reporting import DocTestReporter -from .util import Timer, count_noun, dict_difference -from .external import available_software -from .parsing import parse_optional_tags, parse_file_optional_tags, unparse_optional_tags, \ - nodoctest_regex, optionaltag_regex, optionalfiledirective_regex - +from sage.structure.sage_object import SageObject # Optional tags which are always automatically added @@ -465,7 +468,7 @@ def __init__(self, options, args): s = options.hide.lower() options.hide = set(s.split(',')) for h in options.hide: - if not optionaltag_regex.search(h): + if not optional_tag_regex.search(h): raise ValueError('invalid optional tag {!r}'.format(h)) if 'all' in options.hide: options.hide.discard('all') @@ -508,10 +511,10 @@ def __init__(self, options, args): # Check that all tags are valid for o in options.optional: if o.startswith('!'): - if not optionaltag_regex.search(o[1:]): + if not optional_tag_regex.search(o[1:]): raise ValueError('invalid optional tag {!r}'.format(o)) options.disabled_optional.add(o[1:]) - elif not optionaltag_regex.search(o): + elif not optional_tag_regex.search(o): raise ValueError('invalid optional tag {!r}'.format(o)) options.optional |= auto_optional_tags @@ -531,7 +534,7 @@ def __init__(self, options, args): else: # Check that all tags are valid for o in options.probe: - if not optionaltag_regex.search(o): + if not optional_tag_regex.search(o): raise ValueError('invalid optional tag {!r}'.format(o)) self.options = options diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 45b3987de05..6e06e87b6aa 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -387,7 +387,7 @@ def external_features(): yield Gurobi() -def external_software() -> list[str]: +def _external_software() -> list[str]: """ Return the alphabetical list of external software supported by this module. @@ -400,7 +400,7 @@ def external_software() -> list[str]: return sorted(f.name for f in external_features()) -external_software = external_software() +external_software: list[str] = _external_software() class AvailableSoftware: diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 962c21e12cd..9094dc9ac15 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -248,7 +248,7 @@ def init_sage(controller: DocTestController | None = None) -> None: try: import sympy - except ImportError: + except (ImportError, AttributeError): # Do not require sympy for running doctests (Issue #25106). pass else: diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 79a92835b26..02bfa9ed233 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -39,36 +39,44 @@ import re from collections import defaultdict from functools import reduce +from re import Pattern from typing import Literal, Union, overload +from sage.doctest.check_tolerance import ( + ToleranceExceededError, + check_tolerance_complex_domain, + check_tolerance_real_domain, + float_regex, +) +from sage.doctest.external import available_software, external_software +from sage.doctest.marked_output import MarkedOutput +from sage.doctest.rif_tol import RIFtol, add_tolerance from sage.misc.cachefunc import cached_function from sage.repl.preparse import preparse, strip_string_literals -from sage.doctest.rif_tol import RIFtol, add_tolerance -from sage.doctest.marked_output import MarkedOutput -from sage.doctest.check_tolerance import ( - ToleranceExceededError, check_tolerance_real_domain, - check_tolerance_complex_domain, float_regex) - -from .external import available_software, external_software - # This is the correct pattern to match ISO/IEC 6429 ANSI escape sequences: -ansi_escape_sequence = re.compile(r"(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])") +ansi_escape_sequence: Pattern[str] = re.compile( + r"(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])" +) -special_optional_regex = ( +special_optional_regex_raw = ( "py2|long time|not implemented|not tested|optional|needs|known bug" ) -tag_with_explanation_regex = r"((?:!?\w|[.])*)\s*(?:\((?P.*?)\))?" -optional_regex = re.compile( - rf"[^ a-z]\s*(?P{special_optional_regex})(?:\s|[:-])*(?P(?:(?:{tag_with_explanation_regex})\s*)*)", +tag_with_explanation_regex_raw = r"((?:!?\w|[.])*)\s*(?:\((?P.*?)\))?" +optional_regex: Pattern[str] = re.compile( + rf"[^ a-z]\s*(?P{special_optional_regex_raw})(?:\s|[:-])*(?P(?:(?:{tag_with_explanation_regex_raw})\s*)*)", re.IGNORECASE, ) -special_optional_regex = re.compile(special_optional_regex, re.IGNORECASE) -tag_with_explanation_regex = re.compile(tag_with_explanation_regex, re.IGNORECASE) +special_optional_regex: Pattern[str] = re.compile( + special_optional_regex_raw, re.IGNORECASE +) +tag_with_explanation_regex: Pattern[str] = re.compile( + tag_with_explanation_regex_raw, re.IGNORECASE +) -nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') -optionaltag_regex = re.compile(r"^(\w|[.])+$") -optionalfiledirective_regex = re.compile( +no_doctest_regex: Pattern[str] = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') +optional_tag_regex: Pattern[str] = re.compile(r"^(\w|[.])+$") +optional_file_directive_regex: Pattern[str] = re.compile( r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)' ) @@ -167,7 +175,7 @@ def parse_optional_tags( ....: return_string_sans_tags=True) ({'scipy': None}, 'sage: #this is not \n....: import scipy', False) """ - safe, literals, state = strip_string_literals(string) + safe, literals, _ = strip_string_literals(string) split = safe.split('\n', 1) if len(split) > 1: first_line, rest = split @@ -231,7 +239,7 @@ def parse_optional_tags( return tags -def parse_file_optional_tags(lines): +def parse_file_optional_tags(lines) -> dict[str, str | None]: r""" Scan the first few lines for file-level doctest directives. @@ -261,11 +269,11 @@ def parse_file_optional_tags(lines): ....: parse_file_optional_tags(enumerate(f)) {'xyz': None} """ - tags = {} + tags: dict[str, str | None] = {} for line_count, line in lines: - if nodoctest_regex.match(line): + if no_doctest_regex.match(line): tags['not tested'] = None - if m := optionalfiledirective_regex.match(line): + if m := optional_file_directive_regex.match(line): file_tag_string = m.group(2) tags.update(parse_optional_tags('#' + file_tag_string)) if line_count >= 10: @@ -274,7 +282,7 @@ def parse_file_optional_tags(lines): @cached_function -def _standard_tags(): +def _standard_tags() -> frozenset[str]: r""" Return the set of the names of all standard features. @@ -321,7 +329,7 @@ def _tag_group(tag): return 'special' -def unparse_optional_tags(tags, prefix='# '): +def unparse_optional_tags(tags, prefix='# ') -> str: r""" Return a comment string that sets ``tags``. @@ -596,7 +604,7 @@ def parse_tolerance(source, want): return want -def pre_hash(s): +def pre_hash(s) -> str: """ Prepends a string with its length. From 20c722f835a4dc5f06e37b3e3d12fa6d39d6d162 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 1 Feb 2025 18:17:32 +0100 Subject: [PATCH 236/507] Remove deprecated sage-cython script --- src/bin/sage-cython | 39 --------------------------------------- src/setup.cfg.m4 | 2 -- 2 files changed, 41 deletions(-) delete mode 100755 src/bin/sage-cython diff --git a/src/bin/sage-cython b/src/bin/sage-cython deleted file mode 100755 index a52a729dd02..00000000000 --- a/src/bin/sage-cython +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env sage-python - -# This script is a deprecated wrapper around the "cython" program. -# It is deprecated since Issue #27041 (Sage 8.7) because one should -# simply use "cython" directly. We display deprecation messages whenever -# "sage-cython" does something different from plain "cython". -# -# A stronger deprecation warning was added in #29923 (Sage 9.2). - -import os -import sys -args = sys.argv[1:] - -sys.stderr.write("WARNING: the script sage-cython is deprecated; use cython instead.\n") - -from sage.env import SAGE_SRC - -# args can have length 0, in case we're printing a usage message (see trac 12207) -pyx_file = os.path.abspath(args[-1]) if len(args) else None -include_sage_flags = False - -if pyx_file and pyx_file.startswith(SAGE_SRC): - sys.stderr.write("WARNING: in the future, sage --cython will not add special flags for files in Sage sources.\n") - include_sage_flags = True - -if '-sage' in args: - sys.stderr.write("WARNING: sage --cython -sage is deprecated.\n") - include_sage_flags = True - args.remove('-sage') -elif '-no-sage' in args: - sys.stderr.write("WARNING: sage --cython -no-sage is deprecated, remove the -no-sage flag.\n") - include_sage_flags = False - args.remove('-no-sage') - -if include_sage_flags: - args = ['--embed-positions', '-I%s' % SAGE_SRC] + args - -args.insert(0, 'sage-cython') -os.execlp('cython', *args) diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 3a4ec930b8f..ba30dfd7523 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -57,8 +57,6 @@ scripts = bin/sage-runtests bin/sage-fixdoctests bin/sage-coverage - # The following is deprecated but might still be used in user package install scripts - bin/sage-cython # Helper scripts invoked by sage script # (they would actually belong to something like libexec) bin/sage-cachegrind From 4a832de6fa8173852baf2bfbf044d8d6893dbbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 3 Feb 2025 20:38:53 +0100 Subject: [PATCH 237/507] details --- src/sage/rings/power_series_ring_element.pyx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 045e497307d..72853f7969b 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1385,7 +1385,7 @@ cdef class PowerSeries(AlgebraElement): return tuple(resu) def super_delta_fraction(self, delta): - """ + r""" Return the super delta continued fraction of ``self``. This is a continued fraction of the following shape:: @@ -1460,9 +1460,7 @@ cdef class PowerSeries(AlgebraElement): if deg < 0: break list_vkU.append((v, k, U)) - if deg == 0: - break - if Gk.degree() == -1: + if deg == 0 or Gk.degree() == -1: break di, ci, Gi, Gj = dj, cj, Gj, Gk From 08f5d4ce90b70c386aaab614c98435ecda4d2611 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Mon, 3 Feb 2025 21:56:45 -0700 Subject: [PATCH 238/507] =?UTF-8?q?Fixed=20error=20where=20all=20random=20?= =?UTF-8?q?elements=20of=20a=20quadratic=20field=20were=20integral.=20Bug?= =?UTF-8?q?=20fix=20was=20done=20by=20Dave=20Morris=20in=20an=20old=20pull?= =?UTF-8?q?=20request=20at=20public/30017.=20Extra=20doctest=20and=20docte?= =?UTF-8?q?st=20changes=20were=20provided=20by=20Samuel=20Leli=C3=A8vre=20?= =?UTF-8?q?in=20their=20comments=20on=20the=20pull=20request.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/matrix/special.py | 4 ++ .../number_field/number_field_element.pyx | 36 ++++++++++++++++++ .../number_field_element_quadratic.pyx | 38 ++++++++++++++----- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index dbb0214994e..f9ff4840e2b 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -3009,6 +3009,10 @@ def random_unimodular_matrix(parent, upper_bound=None, max_tries=100): sage: y = polygen(ZZ, 'y') sage: K = NumberField(y^2 - 2*y - 2, 'y') sage: C = random_matrix(K, 3, algorithm='unimodular') + sage: C # random + [ -1/7*y + 47/35 3/5*y - 127/70 -2917/70*y + 4419/70] + [ 1 1/2*y - 1/2 -104/3*y + 211/6] + [ 1/3*y - 1/3 y - 1 -35/6*y - 149/6] sage: det(C) 1 sage: C.base_ring() is K diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 27432813b2b..506346edfcf 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -889,6 +889,42 @@ cdef class NumberFieldElement(NumberFieldElement_base): EXAMPLES:: + sage: x = polygen(ZZ, 'x') + sage: K. = NumberField(x^3 - 2) + sage: a._random_element() # random + -1/2*a^2 - 4 + sage: K. = NumberField(x^2 - 5) + sage: a._random_element() # random + -1/48*a - 1/2 + + With denominator bound one, we always get integral elements:: + + sage: K = QuadraticField(2) + sage: uu = [K.random_element(den_bound=1) for _ in range(5)] + sage: uu # random + [0, a + 2, -12*a - 2, -a + 1, -a - 2] + sage: all(u.is_integral() for u in uu) + True + + Without this constraint, we don't always get them:: + + sage: K = QuadraticField(2) + sage: uu = [K.random_element() for _ in range(100)] + sage: all(u.is_integral() for u in uu) + False + + Random integral elements can also be picked using the random_element + method of the number field's "ring of integers" or "maximal order" :: + sage: K = QuadraticField(2) + sage: O = K.maximal_order() + sage: O.random_element() # random + 5*a - 6 + sage: O = K.ring_of_integers() + sage: O.random_element() # random + -4*a + 1 + + TESTS:: + sage: x = polygen(ZZ, 'x') sage: K. = NumberField(x^3 - 2) sage: a._random_element().parent() is K diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index cc2e63ec8e7..ce06de83e42 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -437,6 +437,21 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): return __make_NumberFieldElement_quadratic1, (self._parent, type(self), a, b, denom) cdef int _randomize(self, num_bound, den_bound, distribution) except -1: + """ + TESTS :: + sage: a = ZZ.random_element(-100, 100) + sage: while a.is_square(): + ....: a = ZZ.random_element(-100, 100) + sage: K = QuadraticField(a) + sage: K.random_element().parent() is K # indirect doctest + True + sage: len(set(K.random_element() for _ in range(100))) >= 40 + True + + Verify that :trac:`30017` is fixed :: + sage: all(K.random_element().is_integral() for s in range(100)) + False + """ cdef Integer temp, denom1, denom2 # in theory, we could just generate two random numerators and @@ -447,17 +462,20 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): # of the random element code, it's worth doing slightly more # work to make this possible. - # normalize denominator bound - if den_bound is None or den_bound < 1: - den_bound = 1 - # generate denominators - denom1 = (ZZ.random_element(x=1, - y=den_bound+1, - distribution=distribution)) - denom2 = (ZZ.random_element(x=1, - y=den_bound+1, - distribution=distribution)) + if den_bound is None: + denom1 = (1 + abs(ZZ.random_element(distribution=distribution))) + denom2 = (1 + abs(ZZ.random_element(distribution=distribution))) + else: + # normalize denominator bound + if den_bound < 1: + den_bound = 1 + denom1 = (ZZ.random_element(x=1, + y=den_bound+1, + distribution=distribution)) + denom2 = (ZZ.random_element(x=1, + y=den_bound+1, + distribution=distribution)) # set a, b temp = (ZZ.random_element(x=num_bound, distribution=distribution)) From 127f80a49ed67de86e9559c0aa4c4e1a98ed87d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 15:00:30 +0100 Subject: [PATCH 239/507] add blank line in number_field_element.pyx --- src/sage/rings/number_field/number_field_element.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 506346edfcf..097552c5bca 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -906,7 +906,7 @@ cdef class NumberFieldElement(NumberFieldElement_base): sage: all(u.is_integral() for u in uu) True - Without this constraint, we don't always get them:: + Without this constraint, we do not always get them:: sage: K = QuadraticField(2) sage: uu = [K.random_element() for _ in range(100)] @@ -915,6 +915,7 @@ cdef class NumberFieldElement(NumberFieldElement_base): Random integral elements can also be picked using the random_element method of the number field's "ring of integers" or "maximal order" :: + sage: K = QuadraticField(2) sage: O = K.maximal_order() sage: O.random_element() # random From ebdb7909c85da8a9015a8c90469da1e4ab2e2415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 15:02:39 +0100 Subject: [PATCH 240/507] add blank lines in number_field_element_quadratic.pyx --- .../rings/number_field/number_field_element_quadratic.pyx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index ce06de83e42..9bbab40d011 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -419,7 +419,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): """ Used for pickling. - TESTS: + TESTS:: sage: x = polygen(ZZ, 'x') sage: K. = NumberField(x^2 - 13) @@ -438,7 +438,8 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cdef int _randomize(self, num_bound, den_bound, distribution) except -1: """ - TESTS :: + TESTS:: + sage: a = ZZ.random_element(-100, 100) sage: while a.is_square(): ....: a = ZZ.random_element(-100, 100) @@ -448,7 +449,8 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: len(set(K.random_element() for _ in range(100))) >= 40 True - Verify that :trac:`30017` is fixed :: + Verify that :trac:`30017` is fixed:: + sage: all(K.random_element().is_integral() for s in range(100)) False """ From c58d841a1702a5b4c42aa49655764297575430e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 15:39:14 +0100 Subject: [PATCH 241/507] conversion of padics rings from magma --- src/sage/ext_data/magma/sage/basic.m | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 678174dfd49..02a2680b7d9 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -125,7 +125,7 @@ intrinsic Sage(X::RngIntRes) -> MonStgElt, BoolElt return Sprintf("Zmod(%o)", Characteristic(X)), false; end intrinsic; -/* Approximate fields */ +/* Approximate real and complex fields */ intrinsic Sage(X::FldRe) -> MonStgElt, BoolElt {} @@ -147,6 +147,34 @@ intrinsic Sage(X::FldComElt) -> MonStgElt, BoolElt return Sprintf("%o([%o, %o])", Sage(Parent(X)), Sage(Real(X)), Sage(Imaginary(X))), true; end intrinsic; +/* p-adic rings and fields */ + +intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt +{p-adic rings, either free precision model or exact model} + prec := Precision(X); + if prec eq Infinity then; + return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; + else: + return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; + end if; +end intrinsic; + +intrinsic Sage(X::FldPAd) -> MonStgElt, BoolElt +{p-adic fields, either free precision model or exact model} + prec := Precision(X); + if prec eq Infinity then; + return Sprintf("Qp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; + else: + return Sprintf("Qp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; + end if; +end intrinsic; + +intrinsic Sage(X::RngPadRes) -> MonStgElt, BoolElt +{fixed precision model} + return Sprintf("Zp(%o, %o, 'fixed-mod')", Sage(Prime(X)), Sage(Precision(X))), false; +end intrinsic; + + /* Polynomials */ intrinsic SageNamesHelper(X::.) -> MonStgElt From bb05936f1a89f98971e2fb7ef3aa60c8b88accf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 16:15:11 +0100 Subject: [PATCH 242/507] fix --- src/sage/ext_data/magma/sage/basic.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 02a2680b7d9..823660077cb 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -154,7 +154,7 @@ intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt prec := Precision(X); if prec eq Infinity then; return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; - else: + else return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; end if; end intrinsic; @@ -164,7 +164,7 @@ intrinsic Sage(X::FldPAd) -> MonStgElt, BoolElt prec := Precision(X); if prec eq Infinity then; return Sprintf("Qp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; - else: + else return Sprintf("Qp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; end if; end intrinsic; From 311d8a6524e8444378704dc38f39d84e6f7d184f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 16:18:46 +0100 Subject: [PATCH 243/507] fix --- src/sage/ext_data/magma/sage/basic.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 823660077cb..57aba52270d 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -152,7 +152,7 @@ intrinsic Sage(X::FldComElt) -> MonStgElt, BoolElt intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt {p-adic rings, either free precision model or exact model} prec := Precision(X); - if prec eq Infinity then; + if Type(prec) eq Infty then return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; else return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; @@ -162,7 +162,7 @@ intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt intrinsic Sage(X::FldPAd) -> MonStgElt, BoolElt {p-adic fields, either free precision model or exact model} prec := Precision(X); - if prec eq Infinity then; + if Type(prec) eq Infty then return Sprintf("Qp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; else return Sprintf("Qp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; From fd93751270cfe95ec5110d6269e4bc7fbb72a499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 16:22:22 +0100 Subject: [PATCH 244/507] fix --- src/sage/ext_data/magma/sage/basic.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 57aba52270d..bcbe795bb45 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -159,7 +159,7 @@ intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt end if; end intrinsic; -intrinsic Sage(X::FldPAd) -> MonStgElt, BoolElt +intrinsic Sage(X::FldPad) -> MonStgElt, BoolElt {p-adic fields, either free precision model or exact model} prec := Precision(X); if Type(prec) eq Infty then From eab6095787316841bf155366e41934f33e2df9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 16:31:24 +0100 Subject: [PATCH 245/507] conversion of padics rings to magma --- src/sage/rings/padics/padic_base_leaves.py | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 4eb9e584de1..1aea397227a 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -304,6 +304,19 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + def _magma_init_(self, magma): + """ + Conversion to magma. + + EXAMPLES:: + + sage: # optional - magma + sage: F = Qp(5,7,"capped-rel") + sage: magma(F) + ? + """ + return f"pAdicRing({self.prime()},{self.precision_cap()})" + class pAdicRingCappedAbsolute(pAdicRingBaseGeneric, pAdicCappedAbsoluteRingGeneric): r""" @@ -607,6 +620,19 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + def _magma_init_(self, magma): + """ + Conversion to magma. + + EXAMPLES:: + + sage: # optional - magma + sage: F = Zp(5,7,"fixed-mod") + sage: magma(F) + ? + """ + return f"pAdicQuotientRing({self.prime()},{self.precision_cap()})" + class pAdicFieldCappedRelative(pAdicFieldBaseGeneric, pAdicCappedRelativeFieldGeneric): r""" @@ -719,6 +745,19 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + def _magma_init_(self, magma): + """ + Conversion to magma. + + EXAMPLES:: + + sage: # optional - magma + sage: F = Qp(5,7,"capped-rel") + sage: magma(F) + ? + """ + return f"pAdicField({self.prime()},{self.precision_cap()})" + def random_element(self, algorithm='default'): r""" Return a random element of ``self``, optionally using the ``algorithm`` From fb6375c25e5def3332dc68a2d78c6c24c7510d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Feb 2025 16:33:42 +0100 Subject: [PATCH 246/507] add doctests --- src/sage/rings/padics/padic_base_leaves.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 1aea397227a..878c54b748f 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -313,7 +313,7 @@ def _magma_init_(self, magma): sage: # optional - magma sage: F = Qp(5,7,"capped-rel") sage: magma(F) - ? + 5-adic field mod 5^7 """ return f"pAdicRing({self.prime()},{self.precision_cap()})" @@ -629,7 +629,7 @@ def _magma_init_(self, magma): sage: # optional - magma sage: F = Zp(5,7,"fixed-mod") sage: magma(F) - ? + Quotient of the 5-adic ring modulo the ideal generated by 5^7 """ return f"pAdicQuotientRing({self.prime()},{self.precision_cap()})" @@ -754,7 +754,7 @@ def _magma_init_(self, magma): sage: # optional - magma sage: F = Qp(5,7,"capped-rel") sage: magma(F) - ? + 5-adic field mod 5^7 """ return f"pAdicField({self.prime()},{self.precision_cap()})" From 15188988b11166b4900dba683c87d525a8cbcffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 5 Feb 2025 11:46:31 +0100 Subject: [PATCH 247/507] remove deprecated stuff in permutation.py --- src/sage/combinat/permutation.py | 54 ++------------------------------ 1 file changed, 2 insertions(+), 52 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index a1043f59646..8df962d5779 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -7758,7 +7758,7 @@ def reflection(self, i): return self.element_class(self, data, check=False) class Element(Permutation): - def has_left_descent(self, i, mult=None): + def has_left_descent(self, i): r""" Check if ``i`` is a left descent of ``self``. @@ -7789,23 +7789,7 @@ def has_left_descent(self, i, mult=None): [1, 2] sage: [i for i in P.index_set() if x.has_left_descent(i)] [1, 2] - - TESTS:: - - sage: P = Permutations(4) - sage: x = P([3, 2, 4, 1]) - sage: x.has_left_descent(2, mult='l2r') - doctest:warning - ... - DeprecationWarning: The mult option is deprecated and ignored. - See https://github.com/sagemath/sage/issues/27467 for details. - True - sage: x.has_left_descent(2, mult='r2l') - True """ - if mult is not None: - from sage.misc.superseded import deprecation - deprecation(27467, "The mult option is deprecated and ignored.") for val in self._list: if val == i: return False @@ -7843,24 +7827,8 @@ def has_right_descent(self, i, mult=None): [1, 3] sage: [i for i in P.index_set() if x.has_right_descent(i)] [1, 3] - - TESTS:: - - sage: P = Permutations(4) - sage: x = P([3, 2, 4, 1]) - sage: x.has_right_descent(3, mult='l2r') - doctest:warning - ... - DeprecationWarning: The mult option is deprecated and ignored. - See https://github.com/sagemath/sage/issues/27467 for details. - True - sage: x.has_right_descent(3, mult='r2l') - True """ - if mult is not None: - from sage.misc.superseded import deprecation - deprecation(27467, "The mult option is deprecated and ignored.") - return self[i-1] > self[i] + return self[i - 1] > self[i] def __mul__(self, other): r""" @@ -9602,24 +9570,6 @@ def __init__(self, n, a): StandardPermutations_n_abstract.__init__(self, n) self._a = a - @property - def a(self): - r""" - ``self.a`` is deprecated; use :meth:`patterns` instead. - - TESTS:: - - sage: P = Permutations(3, avoiding=[[2,1,3],[1,2,3]]) - sage: P.a - doctest:...: DeprecationWarning: The attribute a for the list of patterns to avoid is deprecated, use the method patterns instead. - See https://github.com/sagemath/sage/issues/26810 for details. - ([2, 1, 3], [1, 2, 3]) - """ - from sage.misc.superseded import deprecation - deprecation(26810, "The attribute a for the list of patterns to avoid is " - "deprecated, use the method patterns instead.") - return self.patterns() - def patterns(self): """ Return the patterns avoided by this class of permutations. From 7c5d6dcf1892b04f311c6f6c21121ad6dc584c33 Mon Sep 17 00:00:00 2001 From: rhinopotamus Date: Thu, 10 Aug 2023 15:12:19 -0600 Subject: [PATCH 248/507] Trivial simplifications for arccos This fixes a couple of places where arccos doesn't automatically simplify for ratios arising from the unit circle (but arcsin does). Turns out that asin_eval has some logic handling unit circle cases directly that was not replicated in acos_eval. I copy-pasted the logic from asin_eval and modified appropriately. https://github.com/sagemath/sage/issues/24211 --- src/doc/de/thematische_anleitungen/sage_gymnasium.rst | 8 +------- src/sage/functions/trig.py | 3 +++ src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py | 4 ++-- src/sage/symbolic/ginac/inifcns_trig.cpp | 7 +++++++ .../premierspas_doctest.py | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/doc/de/thematische_anleitungen/sage_gymnasium.rst b/src/doc/de/thematische_anleitungen/sage_gymnasium.rst index 9dc38d8bf0b..dfeb3e13c82 100644 --- a/src/doc/de/thematische_anleitungen/sage_gymnasium.rst +++ b/src/doc/de/thematische_anleitungen/sage_gymnasium.rst @@ -774,8 +774,7 @@ falls wir im Bogenmass rechnen möchten. Ansonsten müssen wir wie oben beschrie Ihre Umkehrfunktionen sind auch mit den nicht sehr überraschenden Namen ``asin()``, ``acos()``, ``atan()`` und ``acot()`` versehen. Sie geben uns aber wie oben erklärt nur Winkel im Bogenmass zurück. Möchten wir im Gradmass rechnen, müssen wir wieder -konvertieren. Die exakte Berechnung der Werte funktioniert in die Gegenrichtung nur, falls im ursprünglichen Wert keine -Wurzeln vorkommen:: +konvertieren. Exakte Berechnung der Werte funktioniert, wenn es möglich ist:: sage: atan(1) 1/4*pi @@ -784,12 +783,7 @@ Wurzeln vorkommen:: sage: rad2deg(x) = x*(180/pi) sage: rad2deg(acos(-1/2)) 120 - -Falls wir Wurzelterme verwenden, müssen wir mit der Funktion ``simplify_full()`` vereinfachen:: - sage: acos(sqrt(3)/2) - arccos(1/2*sqrt(3)) - sage: (acos(sqrt(3)/2)).simplify_full() 1/6*pi Sage kann auch weitere Regeln für trigonometrische Funktionen anwenden, um Terme zu vereinfachen. Es kennt zum Beispiel auch die diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 8f390205cae..242b07f9cd4 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -642,6 +642,9 @@ def __init__(self): (0.9045568943023814-1.0612750619050357j) sage: acos(SR(2.1)) # needs sage.symbolic 1.37285914424258*I + + sage: arcsin(sqrt(2)/2) + 1/4*pi """ GinacFunction.__init__(self, 'arccos', latex_name=r"\arccos", conversions=dict(maxima='acos', sympy='acos', diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index b9488e77b16..52f889f30d1 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1854,9 +1854,9 @@ def angle(self, other): # UHP sage: g = HyperbolicPlane().UHP().get_geodesic(1, 1 + I) sage: h = HyperbolicPlane().UHP().get_geodesic(-sqrt(2), sqrt(2)) sage: g.angle(h) - arccos(1/2*sqrt(2)) + 1/4*pi sage: h.angle(g) - arccos(1/2*sqrt(2)) + 1/4*pi Angle is unoriented, as opposed to oriented. :: diff --git a/src/sage/symbolic/ginac/inifcns_trig.cpp b/src/sage/symbolic/ginac/inifcns_trig.cpp index 71ee9978b9d..37bd7507d05 100644 --- a/src/sage/symbolic/ginac/inifcns_trig.cpp +++ b/src/sage/symbolic/ginac/inifcns_trig.cpp @@ -1229,6 +1229,13 @@ static ex acos_eval(const ex & x) return UnsignedInfinity; throw (std::runtime_error("arccos_eval(): arccos(infinity) encountered")); } + + if (x.is_equal(mul(pow(_ex2, _ex1_2), _ex1_2))) + return mul(Pi, _ex1_4); + + if (x.is_equal(mul(pow(_ex3, _ex1_2), _ex1_2))) + return numeric(1,6)*Pi; + return acos(x).hold(); } diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py index feb5391b4d6..027c31eecfe 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/premierspas_doctest.py @@ -65,7 +65,7 @@ Sage example in ./premierspas.tex, line 967:: sage: arccos(sin(pi/3)) - arccos(1/2*sqrt(3)) + 1/6*pi sage: sqrt(2) sqrt(2) sage: exp(I*pi/7) From 098d8d34c90650fa9f25022fb9f1f4863b708633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 6 Feb 2025 13:42:41 +0100 Subject: [PATCH 249/507] remove deprecated method in normal_form_game --- src/sage/game_theory/normal_form_game.py | 93 ++---------------------- 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 39622aaf3ef..c4200c9b75b 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -890,7 +890,7 @@ def __len__(self): """ return len(self.utilities) - def _repr_(self): + def _repr_(self) -> str: r""" Return the strategy_profiles of the game. @@ -909,7 +909,7 @@ def _repr_(self): base_str = "Normal Form Game with the following utilities: {}" return base_str.format(pformat(self.utilities)) - def _latex_(self): + def _latex_(self) -> str: r""" Return the LaTeX code representing the ``NormalFormGame``. @@ -1194,7 +1194,7 @@ def is_constant_sum(self): return False m1, m2 = self.payoff_matrices() c = m1 + m2 - t = c[0,0] + t = c[0, 0] for row in c: for i in row: @@ -2021,7 +2021,7 @@ def _solve_enumeration(self, maximization=True): powerset(range(player.num_strategies))] for player in self.players] - potential_support_pairs = [pair for pair in product(*potential_supports) if len(pair[0]) == len(pair[1])] + potential_support_pairs = (pair for pair in product(*potential_supports) if len(pair[0]) == len(pair[1])) equilibria = [] for pair in potential_support_pairs: @@ -2230,7 +2230,7 @@ def _is_NE(self, a, b, p1_support, p2_support, M1, M2): p2_payoffs = [sum(v * col[j] for j, v in enumerate(a)) for col in M2.columns()] - #if p1_payoffs.index(max(p1_payoffs)) not in p1_support: + # if p1_payoffs.index(max(p1_payoffs)) not in p1_support: if not any(i in p1_support for i, x in enumerate(p1_payoffs) if x == max(p1_payoffs)): return False @@ -2240,89 +2240,6 @@ def _is_NE(self, a, b, p1_support, p2_support, M1, M2): return True - def _Hrepresentation(self, m1, m2): - r""" - Create the H-representation strings required to use ``lrsnash``. - - Since lrslib 6.1, this format is referred to as "legacy format". - - This method is deprecated. - - EXAMPLES:: - - sage: A = matrix([[1, 2], [3, 4]]) - sage: B = matrix([[3, 3], [1, 4]]) - sage: C = NormalFormGame([A, B]) - sage: print(C._Hrepresentation(A, B)[0]) - doctest:warning... - DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it - creates the legacy input format. Use NormalFormGame._lrs_nash_format instead - See https://github.com/sagemath/sage/issues/27745 for details. - H-representation - linearity 1 5 - begin - 5 4 rational - 0 1 0 0 - 0 0 1 0 - 0 -3 -1 1 - 0 -3 -4 1 - -1 1 1 0 - end - - sage: print(C._Hrepresentation(A, B)[1]) - H-representation - linearity 1 5 - begin - 5 4 rational - 0 -1 -2 1 - 0 -3 -4 1 - 0 1 0 0 - 0 0 1 0 - -1 1 1 0 - end - - """ - from sage.misc.superseded import deprecation - deprecation(27745, - "NormalFormGame._Hrepresentation is deprecated as it " - "creates the legacy input format. " - "Use NormalFormGame._lrs_nash_format instead") - - from sage.geometry.polyhedron.misc import _to_space_separated_string - m = self.players[0].num_strategies - n = self.players[1].num_strategies - midentity = list(matrix.identity(m)) - nidentity = list(matrix.identity(n)) - - s = 'H-representation\n' - s += 'linearity 1 ' + str(m + n + 1) + '\n' - s += 'begin\n' - s += str(m + n + 1) + ' ' + str(m + 2) + ' rational\n' - for f in list(midentity): - s += '0 ' + _to_space_separated_string(f) + ' 0 \n' - for e in list(m2.transpose()): - s += '0 ' + _to_space_separated_string(-e) + ' 1 \n' - s += '-1 ' - for g in range(m): - s += '1 ' - s += '0 \n' - s += 'end\n' - - t = 'H-representation\n' - t += 'linearity 1 ' + str(m + n + 1) + '\n' - t += 'begin\n' - t += str(m + n + 1) + ' ' + str(n + 2) + ' rational\n' - for e in list(m1): - t += '0 ' + _to_space_separated_string(-e) + ' 1 \n' - for f in list(nidentity): - t += '0 ' + _to_space_separated_string(f) + ' 0 \n' - t += '-1 ' - for g in range(n): - t += '1 ' - t += '0 \n' - t += 'end\n' - return s, t - def _lrs_nash_format(self, m1, m2): r""" Create the input format for ``lrsnash``, version 6.1 or newer. From 00dcf7aa81996569a389aee1ee0f60222626b8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 6 Feb 2025 14:44:33 +0100 Subject: [PATCH 250/507] remove some deprecations in groups --- src/sage/groups/matrix_gps/homset.py | 53 ------------------------- src/sage/groups/matrix_gps/morphism.py | 54 ------------------------- src/sage/groups/perm_gps/permgroup.py | 55 ++++---------------------- 3 files changed, 8 insertions(+), 154 deletions(-) delete mode 100644 src/sage/groups/matrix_gps/homset.py delete mode 100644 src/sage/groups/matrix_gps/morphism.py diff --git a/src/sage/groups/matrix_gps/homset.py b/src/sage/groups/matrix_gps/homset.py deleted file mode 100644 index 02818275424..00000000000 --- a/src/sage/groups/matrix_gps/homset.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Matrix Group Homsets - -AUTHORS: - -- William Stein (2006-05-07): initial version - -- Volker Braun (2013-1) port to new Parent, libGAP -""" - -############################################################################## -# Copyright (C) 2006 David Joyner and William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -############################################################################## - -from sage.misc.lazy_import import lazy_import -from sage.misc.superseded import deprecation - - -def is_MatrixGroupHomset(x): - r""" - Test whether ``x`` is a matrix group homset. - - EXAMPLES:: - - sage: from sage.groups.matrix_gps.homset import is_MatrixGroupHomset - sage: is_MatrixGroupHomset(4) - doctest:...: DeprecationWarning: - Importing MatrixGroupHomset from here is deprecated; please use - "from sage.groups.libgap_morphism import GroupHomset_libgap as MatrixGroupHomset" instead. - See https://github.com/sagemath/sage/issues/25444 for details. - False - - sage: F = GF(5) - sage: gens = [matrix(F,2,[1,2, -1, 1]), matrix(F,2, [1,1, 0,1])] - sage: G = MatrixGroup(gens) - sage: from sage.groups.matrix_gps.homset import MatrixGroupHomset - sage: M = MatrixGroupHomset(G, G) - sage: is_MatrixGroupHomset(M) - True - """ - deprecation(25444, "MatrixGroupHomset is deprecated. " - "Use GroupHomset_libgap instead.") - return isinstance(x, MatrixGroupHomset) - - -lazy_import('sage.groups.libgap_morphism', 'GroupHomset_libgap', - 'MatrixGroupHomset', deprecation=25444) diff --git a/src/sage/groups/matrix_gps/morphism.py b/src/sage/groups/matrix_gps/morphism.py deleted file mode 100644 index e73ba7e945a..00000000000 --- a/src/sage/groups/matrix_gps/morphism.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Homomorphisms Between Matrix Groups - -Deprecated May, 2018; use :class:`sage.groups.libgap_morphism` instead. -""" - -#***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.misc.lazy_import import lazy_import - - -def to_libgap(x): - """ - Helper to convert ``x`` to a LibGAP matrix or matrix group - element. - - Deprecated; use the ``x.gap()`` method or ``libgap(x)`` instead. - - EXAMPLES:: - - sage: from sage.groups.matrix_gps.morphism import to_libgap - sage: to_libgap(GL(2,3).gen(0)) - doctest:...: DeprecationWarning: this function is deprecated. - Use x.gap() or libgap(x) instead. - See https://github.com/sagemath/sage/issues/25444 for details. - [ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ] - sage: to_libgap(matrix(QQ, [[1,2],[3,4]])) - [ [ 1, 2 ], [ 3, 4 ] ] - """ - from sage.misc.superseded import deprecation - deprecation(25444, "this function is deprecated." - " Use x.gap() or libgap(x) instead.") - try: - return x.gap() - except AttributeError: - from sage.libs.gap.libgap import libgap - return libgap(x) - - -lazy_import('sage.groups.libgap_morphism', 'GroupMorphism_libgap', - 'MatrixGroupMorphism_im_gens', deprecation=25444) - -lazy_import('sage.categories.morphism', 'Morphism', - 'MatrixGroupMap', deprecation=25444) - -lazy_import('sage.categories.morphism', 'Morphism', - 'MatrixGroupMorphism', deprecation=25444) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index a56b233c4db..f8e6c6730d1 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -380,13 +380,12 @@ def PermutationGroup(gens=None, *args, **kwds): ... TypeError: gens must be a tuple, list, or GapElement - This will raise an error after the deprecation period:: + This now raises an error:: sage: G = PermutationGroup([(1,2,3,4)], [(1,7,3,5)]) - doctest:warning + Traceback (most recent call last): ... - DeprecationWarning: gap_group, domain, canonicalize, category will become keyword only - See https://github.com/sagemath/sage/issues/31510 for details. + ValueError: please use keywords gap_group=, domain=, canonicalize=, category= in the input """ if not isinstance(gens, ExpectElement) and hasattr(gens, '_permgroup_'): return gens._permgroup_() @@ -402,18 +401,7 @@ def PermutationGroup(gens=None, *args, **kwds): raise ValueError("you must specify the domain for an action") return PermutationGroup_action(gens, action, domain, gap_group=gap_group) if args: - from sage.misc.superseded import deprecation - deprecation(31510, "gap_group, domain, canonicalize, category will become keyword only") - if len(args) > 4: - raise ValueError("invalid input") - args = list(args) - gap_group = args.pop(0) - if args: - domain = args.pop(0) - if args: - canonicalize = args.pop(0) - if args: - category = args.pop(0) + raise ValueError("please use keywords gap_group=, domain=, canonicalize=, category= in the input") return PermutationGroup_generic(gens=gens, gap_group=gap_group, domain=domain, canonicalize=canonicalize, category=category) @@ -1068,7 +1056,7 @@ def list(self): """ return list(self) - def __contains__(self, item): + def __contains__(self, item) -> bool: """ Return whether ``item`` is an element of this group. @@ -1105,33 +1093,6 @@ def __contains__(self, item): return False return True - def has_element(self, item): - """ - Return whether ``item`` is an element of this group - - however *ignores* parentage. - - EXAMPLES:: - - sage: G = CyclicPermutationGroup(4) - sage: gens = G.gens() - sage: H = DihedralGroup(4) - sage: g = G([(1,2,3,4)]); g - (1,2,3,4) - sage: G.has_element(g) - doctest:warning - ... - DeprecationWarning: G.has_element(g) is deprecated; use :meth:`__contains__`, i.e., `g in G` instead - See https://github.com/sagemath/sage/issues/33831 for details. - True - sage: h = H([(1,2),(3,4)]); h - (1,2)(3,4) - sage: G.has_element(h) - False - """ - from sage.misc.superseded import deprecation - deprecation(33831, "G.has_element(g) is deprecated; use :meth:`__contains__`, i.e., `g in G` instead") - return item in self - def __iter__(self): r""" Return an iterator going through all elements in ``self``. @@ -1161,9 +1122,9 @@ def __iter__(self): """ if len(self._gens) == 1: return self._iteration_monogen() - else: - # TODO: this is too slow for moderatly small permutation groups - return self.iteration(algorithm='SGS') + + # TODO: this is too slow for moderately small permutation groups + return self.iteration(algorithm='SGS') def _iteration_monogen(self): r""" From 98534b10338f905ab89dce82838212d3e178d492 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:51:50 +0700 Subject: [PATCH 251/507] Move mention of accessor functions to implementation section --- src/sage/rings/laurent_series_ring_element.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 85d716b8c7a..408b7810c3f 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -40,6 +40,10 @@ as a power of the variable times the unit part (which need not be a unit - it's a polynomial with nonzero constant term). The zero Laurent series has unit part 0. +For a Laurent series internally represented as `t^n \cdot f` where +`t` is the variable, `f` can be accessed through :meth:`valuation_zero_part` +and `n` can be accessed through :meth:`valuation`. + AUTHORS: - William Stein: original version @@ -97,11 +101,9 @@ cdef class LaurentSeries(AlgebraElement): - ``parent`` -- a Laurent series ring - ``f`` -- a power series (or something can be coerced - to one); note that ``f`` does *not* have to be a unit. - This can be accessed through :meth:`valuation_zero_part`. + to one); note that ``f`` does *not* have to be a unit - - ``n`` -- (default: 0) integer. This can be accessed - through :meth:`valuation`. + - ``n`` -- (default: 0) integer """ def __init__(self, parent, f, n=0): r""" @@ -1303,7 +1305,7 @@ cdef class LaurentSeries(AlgebraElement): sage: g.valuation() 0 - Note that the valuation of an element undistinguishable from + Note that the valuation of an element indistinguishable from zero is infinite:: sage: h = f - f; h From 48a510e26a35e19de6e03dba109fc8b18c6b9272 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 6 Feb 2025 21:17:14 +0700 Subject: [PATCH 252/507] Deprecate reproducible_repr --- src/sage/doctest/fixtures.py | 42 ++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index b04316151eb..277443f57f1 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -52,9 +52,12 @@ def reproducible_repr(val): .. NOTE:: - This function is mostly superseded by the automatic sorting - of dictionary keys by a displayhook. See - :func:`sage.doctest.forker.init_sage`. + This function is deprecated, in most cases it suffices to use + the automatic sorting of dictionary keys and set items by a displayhook. + See :func:`sage.doctest.forker.init_sage`. + If used in a format string, use :func:`IPython.lib.pretty.pretty`. + In the rare cases where the ordering of the elements is not reliable + or transitive, ``sorted`` with a sane key can be used instead. This tries to ensure that the returned string does not depend on factors outside the control of the doctest. @@ -80,6 +83,7 @@ def reproducible_repr(val): EXAMPLES:: + sage: # not tested (test fails because of deprecation warning) sage: from sage.doctest.fixtures import reproducible_repr sage: print(reproducible_repr(set(["a", "c", "b", "d"]))) set(['a', 'b', 'c', 'd']) @@ -91,7 +95,20 @@ def reproducible_repr(val): {'2': 'two', 1: 'one', 3.00000000000000: 'three'} sage: print(reproducible_repr("foo\nbar")) # demonstrate default case 'foo\nbar' + + TESTS: + + Ensures deprecation warning is printed out:: + + sage: from sage.doctest.fixtures import reproducible_repr + sage: print(reproducible_repr(set(["a", "c", "b", "d"]))) + doctest:warning... + DeprecationWarning: reproducible_repr is deprecated, see its documentation for details + See https://github.com/sagemath/sage/issues/39420 for details. + set(['a', 'b', 'c', 'd']) """ + from sage.misc.superseded import deprecation + deprecation(39420, 'reproducible_repr is deprecated, see its documentation for details') def sorted_pairs(iterable, pairs=False): # We don't know whether container data structures will have @@ -187,22 +204,23 @@ def get(self, name): 4 """ val = getattr(self.delegate, name) + from IPython.lib.pretty import pretty if callable(val) and name not in self.delegate.__dict__: @wraps(val) def wrapper(*args, **kwds): - arglst = [reproducible_repr(arg) for arg in args] - arglst.extend("{}={}".format(k, reproducible_repr(v)) + arglst = [pretty(arg) for arg in args] + arglst.extend("{}={}".format(k, pretty(v)) for k, v in sorted(kwds.items())) res = val(*args, **kwds) print("{}call {}({}) -> {}" .format(self.prefix, name, ", ".join(arglst), - reproducible_repr(res))) + pretty(res))) return res return wrapper else: if self.reads: print("{}read {} = {}".format(self.prefix, name, - reproducible_repr(val))) + pretty(val))) return val def set(self, name, val): @@ -224,8 +242,9 @@ def set(self, name, val): sage: foo.x 2 """ + from IPython.lib.pretty import pretty print("{}write {} = {}".format(self.prefix, name, - reproducible_repr(val))) + pretty(val))) setattr(self.delegate, name, val) @@ -379,11 +398,12 @@ def trace_method(obj, meth, **kwds): @wraps(f) def g(*args, **kwds): - arglst = [reproducible_repr(arg) for arg in args] - arglst.extend("{}={}".format(k, reproducible_repr(v)) + from IPython.lib.pretty import pretty + arglst = [pretty(arg) for arg in args] + arglst.extend("{}={}".format(k, pretty(v)) for k, v in sorted(kwds.items())) print("enter {}({})".format(meth, ", ".join(arglst))) res = f(t, *args, **kwds) - print("exit {} -> {}".format(meth, reproducible_repr(res))) + print("exit {} -> {}".format(meth, pretty(res))) return res setattr(obj, meth, g) From 09db0b2e52e7d05536f503888f5f5354e3c2f584 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 6 Feb 2025 21:17:51 +0700 Subject: [PATCH 253/507] Fix lint --- src/sage/rings/laurent_series_ring_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 408b7810c3f..758b7012240 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -1,4 +1,4 @@ -""" +r""" Laurent Series EXAMPLES:: From f490f6dc06e3da37d50779a066d70a092d966739 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:58:19 +0700 Subject: [PATCH 254/507] Fix tests --- src/sage/plot/animate.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 84433c492e2..6a9454d04ab 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -1412,25 +1412,25 @@ def _add_png(self, pngfile): sage: from sage.plot.animate import APngAssembler sage: APngAssembler._testCase1("_add_png", reads=False) enter _add_png('...png') - write _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6') + write _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6') call _copy() -> None call _first_IHDR(...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00') -> None - write _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_') + write _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_') call _copy() -> None - write _current_chunk = (...'\x00\x00\x00\x07', ...'tIME', ...'\x07\xde\x06\x1b\x0b&$', ...'\x1f0z\xd5') - write _current_chunk = (...'\x00\x00\x00\x08', ...'IDAT', ...'img1data', ...'\xce\x8aI\x99') + write _current_chunk = (...'\x00\x00\x00\x07',...'tIME',...'\x07\xde\x06\x1b\x0b&$',...'\x1f0z\xd5') + write _current_chunk = (...'\x00\x00\x00\x08',...'IDAT',...'img1data',...'\xce\x8aI\x99') call _first_IDAT(...'img1data') -> None - write _current_chunk = (...'\x00\x00\x00\x00', ...'IEND', ...'', ...'\xaeB`\x82') + write _current_chunk = (...'\x00\x00\x00\x00',...'IEND',...'',...'\xaeB`\x82') write _first = False exit _add_png -> None enter _add_png('...png') - write _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6') - write _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_') - write _current_chunk = (...'\x00\x00\x00\x04', ...'IDAT', ...'img2', ...'\x0ei\xab\x1d') + write _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6') + write _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_') + write _current_chunk = (...'\x00\x00\x00\x04',...'IDAT',...'img2',...'\x0ei\xab\x1d') call _next_IDAT(...'img2') -> None - write _current_chunk = (...'\x00\x00\x00\x04', ...'IDAT', ...'data', ...'f\x94\xcbx') + write _current_chunk = (...'\x00\x00\x00\x04',...'IDAT',...'data',...'f\x94\xcbx') call _next_IDAT(...'data') -> None - write _current_chunk = (...'\x00\x00\x00\x00', ...'IEND', ...'', ...'\xaeB`\x82') + write _current_chunk = (...'\x00\x00\x00\x00',...'IEND',...'',...'\xaeB`\x82') write _first = False exit _add_png -> None """ @@ -1553,16 +1553,16 @@ def _copy(self): sage: from sage.plot.animate import APngAssembler sage: APngAssembler._testCase1("_copy") enter _copy() - read _current_chunk = (...'\x00\x00\x00\r', ...'IHDR', ...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', ...'\xb8\x1f9\xc6') - read out = <_io.BytesIO object at ... - read out = <_io.BytesIO object at ... - read out = <_io.BytesIO object at ... - read out = <_io.BytesIO object at ... + read _current_chunk = (...'\x00\x00\x00\r',...'IHDR',...'\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00',...'\xb8\x1f9\xc6') + read out = <_io.BytesIO... at ... + read out = <_io.BytesIO... at ... + read out = <_io.BytesIO... at ... + read out = <_io.BytesIO... at ... exit _copy -> None enter _copy() - read _current_chunk = (...'\x00\x00\x00\x04', ...'gAMA', ...'\x00\x01\x86\xa0', ...'1\xe8\x96_') + read _current_chunk = (...'\x00\x00\x00\x04',...'gAMA',...'\x00\x01\x86\xa0',...'1\xe8\x96_') ... - read _current_chunk = (...'\x00\x00\x00\x08', ...'IDAT', ...'img1data', ...'\xce\x8aI\x99') + read _current_chunk = (...'\x00\x00\x00\x08',...'IDAT',...'img1data',...'\xce\x8aI\x99') ... exit _copy -> None """ @@ -1581,7 +1581,7 @@ def _actl(self): read _actl_written = False read num_frames = 2 read num_plays = 0 - call _chunk(...'acTL', ...'\x00\x00\x00\x02\x00\x00\x00\x00') -> None + call _chunk(...'acTL',...'\x00\x00\x00\x02\x00\x00\x00\x00') -> None write _actl_written = True exit _actl -> None """ From a18857ca4690aac9efb21fcf96578110d1259d0a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 7 Feb 2025 00:31:17 +0700 Subject: [PATCH 255/507] Retrigger CI From 85611d79251e4baae34a04da6a0ba03843152e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 6 Feb 2025 18:54:11 +0100 Subject: [PATCH 256/507] fix detail --- src/sage/groups/perm_gps/permgroup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index f8e6c6730d1..d48f1f8ec80 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -380,7 +380,7 @@ def PermutationGroup(gens=None, *args, **kwds): ... TypeError: gens must be a tuple, list, or GapElement - This now raises an error:: + This now raises an error (:issue:`31510`):: sage: G = PermutationGroup([(1,2,3,4)], [(1,7,3,5)]) Traceback (most recent call last): From c521adbaf7fe0f66c6459e265d85c435542e9faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 6 Feb 2025 19:18:02 +0100 Subject: [PATCH 257/507] fixing doctest and code detail --- src/sage/game_theory/normal_form_game.py | 45 +++++------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index c4200c9b75b..e362ea8f85d 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -2263,53 +2263,24 @@ def _lrs_nash_format(self, m1, m2): 4 3 - sage: legacy_format = C._Hrepresentation(A, B) - doctest:warning... - DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it - creates the legacy input format. Use NormalFormGame._lrs_nash_format instead - See https://github.com/sagemath/sage/issues/27745 for details. - sage: print('*game: player 1\n', legacy_format[0]) - *game: player 1 - H-representation - linearity 1 6 - begin - 6 5 rational - 0 1 0 0 0 - 0 0 1 0 0 - 0 0 0 1 0 - 0 -1 0 -4 1 - 0 0 -2 -3 1 - -1 1 1 1 0 - end - - sage: print('*game: player 2\n', legacy_format[1]) - *game: player 2 - H-representation - linearity 1 6 - begin - 6 4 rational - 0 0 -6 1 - 0 -2 -5 1 - 0 -3 -3 1 - 0 1 0 0 - 0 0 1 0 - -1 1 1 0 - end + .. NOTE:: + + The former legacy format has been removed in :issue:`39464`. """ from sage.geometry.polyhedron.misc import _to_space_separated_string m = self.players[0].num_strategies n = self.players[1].num_strategies s = f'{m} {n}\n\n' - for r in m1.rows(): - s += _to_space_separated_string(r) + '\n' + s += '\n'.join(_to_space_separated_string(r) for r in m1.rows()) + s += '\n\n' + s += '\n'.join(_to_space_separated_string(r) for r in m2.rows()) s += '\n' - for r in m2.rows(): - s += _to_space_separated_string(r) + '\n' return s - def is_degenerate(self, certificate=False): + def is_degenerate(self, certificate=False) -> bool: """ A function to check whether the game is degenerate or not. + Will return a boolean. A two-player game is called nondegenerate if no mixed strategy of From 6c6202216d64134060958a32201b174b17cf32b6 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Thu, 6 Feb 2025 19:34:07 -0700 Subject: [PATCH 258/507] Implemented MOLS construction for prime powers --- src/sage/combinat/designs/latin_squares.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index b117f76b8e0..0b63649c5bd 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -126,6 +126,8 @@ from sage.rings.integer import Integer from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown +from sage.arith.misc import is_prime_power +from sage.rings.finite_rings.finite_field_constructor import GF def are_mutually_orthogonal_latin_squares(l, verbose=False): @@ -366,6 +368,19 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): matrices = construction()[:k] + elif is_prime_power(n): + F = list(GF(n)) + + # We need the first element of the list to be 0 + assert F[0] == 0 + + # This dictionary is used to convert from field elements to integers + conv = {F[i] : i for i in range(n)} + + # Make the matrices + matrices = [Matrix([[conv[F[i] + F[r]*F[j]] for i in range(n)] + for j in range(n)]) for r in range(1, k+1)] + elif orthogonal_array(k + 2, n, existence=True) is not Unknown: # Forwarding non-existence results if orthogonal_array(k + 2, n, existence=True): From ed56b052849b4dad4a7b82917ef25ab62046e0b8 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:36:25 +0700 Subject: [PATCH 259/507] Fix doctest for optional tests --- .../rings/polynomial/multi_polynomial_ideal.py | 14 +++++++------- .../rings/polynomial/multi_polynomial_sequence.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index e7d985ccb22..62346ce0aa2 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2617,8 +2617,8 @@ def variety(self, ring=None, *, algorithm='triangular_decomposition', proof=True See :mod:`~sage.rings.polynomial.msolve` for more information. :: sage: I.variety(RBF, algorithm='msolve', proof=False) # optional - msolve - [{x: [2.76929235423863 +/- 2.08e-15], y: [0.361103080528647 +/- 4.53e-16]}, - {x: 1.000000000000000, y: 1.000000000000000}] + [{y: [0.361103080528647 +/- 4.53e-16], x: [2.76929235423863 +/- 2.08e-15]}, + {y: 1.000000000000000, x: 1.000000000000000}] Computation over floating point numbers may compute only a partial solution, or even none at all. Notice that x values are missing from the following variety:: @@ -2672,10 +2672,10 @@ def variety(self, ring=None, *, algorithm='triangular_decomposition', proof=True sage: sorted(I.variety(algorithm='msolve', # optional - msolve, needs sage.rings.finite_rings ....: proof=False), ....: key=str) - [{x: 1, y: 1}, - {x: 1, y: 536870908}, - {x: 536870908, y: 1}, - {x: 536870908, y: 536870908}] + [{y: 1, x: 1}, + {y: 1, x: 536870908}, + {y: 536870908, x: 1}, + {y: 536870908, x: 536870908}] but may fail in small characteristic, especially with ideals of high degree with respect to the characteristic:: @@ -3482,7 +3482,7 @@ def _reduce_using_macaulay2(self, f): sage: R. = PolynomialRing(ZZ, 4) sage: I = ideal(x*y-z^2, y^2-w^2) - sage: I._reduce_using_macaulay2(x*y-z^2 + y^2) # optional - macaulay2 + sage: I._reduce_using_macaulay2(x*y-z^2 + y^2) # optional - macaulay2 w^2 """ I = self._macaulay2_() diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index c68911d468b..6fd4c23b3ed 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1576,7 +1576,7 @@ def solve(self, algorithm='polybori', n=1, sage: sol = S.solve(algorithm='exhaustive_search') # optional - fes # needs sage.rings.polynomial.pbori sage: sol # optional - fes # needs sage.rings.polynomial.pbori - [{x: 1, y: 1, z: 1}] + [{z: 1, y: 1, x: 1}] sage: S.subs(sol[0]) # optional - fes # needs sage.rings.polynomial.pbori [0, 0, 0] @@ -1584,7 +1584,7 @@ def solve(self, algorithm='polybori', n=1, sage: sol = S.solve(algorithm='sat') # optional - pycryptosat # needs sage.rings.polynomial.pbori sage: sol # optional - pycryptosat # needs sage.rings.polynomial.pbori - [{x: 0, y: 1, z: 0}] + [{z: 0, y: 1, x: 0}] sage: S.subs(sol[0]) # needs sage.rings.polynomial.pbori [0, 0, 0] From f173264f694947fdd978f48bfff9b9e49ece0336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 11:33:14 +0100 Subject: [PATCH 260/507] remove legacy format in parser --- src/sage/game_theory/parser.py | 39 ++++------------------------------ 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 6ab62d5b7ff..d667adb427f 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -51,7 +51,7 @@ def __init__(self, raw_string): """ self.raw_string = raw_string - def format_lrs(self, legacy_format=False): + def format_lrs(self): r""" Parses the output of lrs so as to return vectors corresponding to equilibria. @@ -137,45 +137,14 @@ def format_lrs(self, legacy_format=False): [(1/3, 2/3, 0), (1/7, 0, 6/7)], [(1, 0, 0), (0, 0, 1)]] - TESTS: - - An example with the legacy format:: - - sage: from sage.cpython.string import bytes_to_str - sage: from sage.game_theory.parser import Parser - sage: from subprocess import Popen, PIPE - sage: A = matrix([[1, 2], [3, 2]]) - sage: g = NormalFormGame([A]) - sage: game1_str, game2_str = g._Hrepresentation(A, -A) - doctest:warning... - DeprecationWarning: NormalFormGame._Hrepresentation is deprecated as it - creates the legacy input format. Use NormalFormGame._lrs_nash_format instead - See https://github.com/sagemath/sage/issues/27745 for details. - sage: g1_name, g2_name = tmp_filename(), tmp_filename() - sage: g1_file, g2_file = open(g1_name, 'w'), open(g2_name, 'w') - sage: _ = g1_file.write(game1_str) - sage: g1_file.close() - sage: _ = g2_file.write(game2_str) - sage: g2_file.close() - sage: from sage.features.lrs import LrsNash - sage: process = Popen([LrsNash().absolute_filename(), g1_name, g2_name], # optional - lrslib - ....: stdout=PIPE, stderr=PIPE) - sage: lrs_output = [bytes_to_str(row) for row in process.stdout] # not tested, optional - lrslib - sage: nasheq = Parser(lrs_output).format_lrs(legacy_format=True) # not tested, optional - lrslib - sage: nasheq # not tested, optional - lrslib - [[(1/2, 1/2), (0, 1)], [(0, 1), (0, 1)]] + The former legacy format has been removed in :issue:`39464`. """ equilibria = [] from sage.misc.sage_eval import sage_eval from itertools import groupby, dropwhile lines = iter(self.raw_string) - if legacy_format: - # Skip until the magic stars announce the beginning of the real output - while not next(lines).startswith("*****"): - pass - else: - # Skip comment lines starting with a single star - lines = dropwhile(lambda line: line.startswith('*'), lines) + # Skip comment lines starting with a single star + lines = dropwhile(lambda line: line.startswith('*'), lines) for collection in [list(x[1]) for x in groupby(lines, lambda x: x == '\n')]: if collection[0].startswith('2'): s1 = tuple([sage_eval(k) for k in collection[-1].split()][1:-1]) From 77e830f72c4dcb32700b185ab44e60339315fc4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 14:02:29 +0100 Subject: [PATCH 261/507] convert gens method in modular to return tuple --- src/sage/modular/abvar/finite_subgroup.py | 6 +- src/sage/modular/abvar/homspace.py | 2 +- src/sage/modular/dirichlet.py | 4 +- src/sage/modular/drinfeld_modform/ring.py | 20 ++--- src/sage/modular/hecke/algebra.py | 8 +- src/sage/modular/hecke/module.py | 2 +- src/sage/modular/modform/space.py | 2 +- .../modform_hecketriangle/abstract_space.py | 89 +++++++++---------- .../modular/modform_hecketriangle/space.py | 59 ++++++------ .../modular/modform_hecketriangle/subspace.py | 16 ++-- src/sage/modular/overconvergent/genus0.py | 3 +- .../modular/pollack_stevens/fund_domain.py | 18 ++-- src/sage/modular/quasimodform/ring.py | 40 ++++----- 13 files changed, 135 insertions(+), 134 deletions(-) diff --git a/src/sage/modular/abvar/finite_subgroup.py b/src/sage/modular/abvar/finite_subgroup.py index cfaf762eae2..ecdfbeebf77 100644 --- a/src/sage/modular/abvar/finite_subgroup.py +++ b/src/sage/modular/abvar/finite_subgroup.py @@ -579,11 +579,13 @@ def order(self): self.__order = o return o - def gens(self): + def gens(self) -> Sequence: """ Return generators for this finite subgroup. - EXAMPLES: We list generators for several cuspidal subgroups:: + EXAMPLES: + + We list generators for several cuspidal subgroups:: sage: J0(11).cuspidal_subgroup().gens() [[(0, 1/5)]] diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 97200d8a6bc..c14830d8815 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -508,7 +508,7 @@ def ngens(self): self.calculate_generators() return len(self._gens) - def gens(self): + def gens(self) -> tuple: """ Return tuple of generators for this endomorphism ring. diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index f93984335bd..db1bd95d246 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -3104,7 +3104,7 @@ def gen(self, n=0): return g[n] @cached_method - def gens(self): + def gens(self) -> tuple: """ Return generators of ``self``. @@ -3117,7 +3117,7 @@ def gens(self): g = [] ord = self.zeta_order() M = self._module - zero = M(0) + zero = M.zero() orders = self.integers_mod().unit_group().gens_orders() for i in range(len(self.unit_gens())): z = zero.__copy__() diff --git a/src/sage/modular/drinfeld_modform/ring.py b/src/sage/modular/drinfeld_modform/ring.py index b027edc08ca..0106f3bdc24 100644 --- a/src/sage/modular/drinfeld_modform/ring.py +++ b/src/sage/modular/drinfeld_modform/ring.py @@ -119,7 +119,7 @@ class DrinfeldModularForms(Parent, UniqueRepresentation): Use the :meth:`gens` method to obtain the generators of the ring:: sage: M.gens() - [g1, g2, g3] + (g1, g2, g3) sage: M.inject_variables() # assign the variable g1, g2, g3 Defining g1, g2, g3 sage: T*g1*g2 + g3 @@ -130,23 +130,23 @@ class DrinfeldModularForms(Parent, UniqueRepresentation): sage: M. = DrinfeldModularForms(K) sage: M.gens() - [F, G, H] + (F, G, H) sage: M = DrinfeldModularForms(K, 5, names='f') # must specify the rank sage: M.gens() - [f1, f2, f3, f4, f5] + (f1, f2, f3, f4, f5) sage: M = DrinfeldModularForms(K, names='u, v, w, x') sage: M.gens() - [u, v, w, x] + (u, v, w, x) sage: M = DrinfeldModularForms(K, names=['F', 'G', 'H']) sage: M.gens() - [F, G, H] + (F, G, H) Set the keyword parameter ``has_type`` to ``True`` in order to create the ring of Drinfeld modular forms of arbitrary type:: sage: M = DrinfeldModularForms(K, 4, has_type=True) sage: M.gens() - [g1, g2, g3, h4] + (g1, g2, g3, h4) sage: h4 = M.3 sage: h4.type() 1 @@ -618,18 +618,18 @@ def gen(self, n): """ return self(self._poly_ring.gen(n)) - def gens(self): + def gens(self) -> tuple: r""" - Return a list of generators of this ring. + Return a tuple of generators of this ring. EXAMPLES:: sage: A = GF(3)['T']; K = Frac(A); T = K.gen() sage: M = DrinfeldModularForms(K, 5) sage: M.gens() - [g1, g2, g3, g4, g5] + (g1, g2, g3, g4, g5) """ - return [self(g) for g in self._poly_ring.gens()] + return tuple(self(g) for g in self._poly_ring.gens()) def ngens(self): r""" diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py index 7c209d146b6..f4f25ee3932 100644 --- a/src/sage/modular/hecke/algebra.py +++ b/src/sage/modular/hecke/algebra.py @@ -518,10 +518,12 @@ def discriminant(self): trace_matrix[i, j] = trace_matrix[j, i] = basis[i].matrix().trace_of_product(basis[j].matrix()) return trace_matrix.det() - def gens(self): + def gens(self) -> Iterator: r""" - Return a generator over all Hecke operator `T_n` for - `n = 1, 2, 3, \ldots`. This is infinite. + Return a generator over all Hecke operator `T_n` + for `n = 1, 2, 3, \ldots`. + + This is infinite. EXAMPLES:: diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index bd5e3be5733..2d1218c1f45 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -1341,7 +1341,7 @@ def factor_number(self): except AttributeError: return -1 - def gens(self): + def gens(self) -> tuple: """ Return a tuple of basis elements of ``self``. diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index 9a80d4c97aa..7ee965d2589 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1358,7 +1358,7 @@ def gen(self, n): except IndexError: raise ValueError("Generator %s not defined" % n) - def gens(self): + def gens(self) -> list: """ Return a complete set of generators for ``self``. diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index 8c9a5db1aac..6a970f28cc1 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -1352,7 +1352,7 @@ def _canonical_min_exp(self, min_exp, order_1): return (min_exp, order_1) - def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero()): + def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero()) -> tuple: r""" Return a basis in ``self`` of the subspace of (quasi) weakly holomorphic forms which satisfy the specified properties on @@ -1391,17 +1391,20 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() sage: QF = QuasiWeakModularForms(n=8, k=10/3, ep=-1) sage: QF.default_prec(1) sage: QF.quasi_part_gens(min_exp=-1) - [q^-1 + O(q), 1 + O(q), q^-1 - 9/(128*d) + O(q), 1 + O(q), q^-1 - 19/(64*d) + O(q), q^-1 + 1/(64*d) + O(q)] + (q^-1 + O(q), 1 + O(q), q^-1 - 9/(128*d) + O(q), + 1 + O(q), q^-1 - 19/(64*d) + O(q), q^-1 + 1/(64*d) + O(q)) sage: QF.quasi_part_gens(min_exp=-1, max_exp=-1) - [q^-1 + O(q), q^-1 - 9/(128*d) + O(q), q^-1 - 19/(64*d) + O(q), q^-1 + 1/(64*d) + O(q)] + (q^-1 + O(q), q^-1 - 9/(128*d) + O(q), + q^-1 - 19/(64*d) + O(q), q^-1 + 1/(64*d) + O(q)) sage: QF.quasi_part_gens(min_exp=-2, r=1) - [q^-2 - 9/(128*d)*q^-1 - 261/(131072*d^2) + O(q), q^-1 - 9/(128*d) + O(q), 1 + O(q)] + (q^-2 - 9/(128*d)*q^-1 - 261/(131072*d^2) + O(q), + q^-1 - 9/(128*d) + O(q), 1 + O(q)) sage: from sage.modular.modform_hecketriangle.space import ModularForms sage: MF = ModularForms(k=36) sage: MF.quasi_part_gens(min_exp=2) - [q^2 + 194184*q^4 + O(q^5), q^3 - 72*q^4 + O(q^5)] + (q^2 + 194184*q^4 + O(q^5), q^3 - 72*q^4 + O(q^5)) sage: from sage.modular.modform_hecketriangle.space import QuasiModularForms sage: MF = QuasiModularForms(n=5, k=6, ep=-1) @@ -1409,17 +1412,17 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() sage: MF.dimension() 3 sage: MF.quasi_part_gens(r=0) - [1 - 37/(200*d)*q + O(q^2)] + (1 - 37/(200*d)*q + O(q^2),) sage: MF.quasi_part_gens(r=0)[0] == MF.E6() True sage: MF.quasi_part_gens(r=1) - [1 + 33/(200*d)*q + O(q^2)] + (1 + 33/(200*d)*q + O(q^2),) sage: MF.quasi_part_gens(r=1)[0] == MF.E2()*MF.E4() True sage: MF.quasi_part_gens(r=2) - [] + () sage: MF.quasi_part_gens(r=3) - [1 - 27/(200*d)*q + O(q^2)] + (1 - 27/(200*d)*q + O(q^2),) sage: MF.quasi_part_gens(r=3)[0] == MF.E2()^3 True @@ -1429,18 +1432,18 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() sage: MF.dimension() 8 sage: MF.quasi_part_gens(r=0) - [q - 34743/(640000*d^2)*q^3 + O(q^4), q^2 - 69/(200*d)*q^3 + O(q^4)] + (q - 34743/(640000*d^2)*q^3 + O(q^4), q^2 - 69/(200*d)*q^3 + O(q^4)) sage: MF.quasi_part_gens(r=1) - [q - 9/(200*d)*q^2 + 37633/(640000*d^2)*q^3 + O(q^4), - q^2 + 1/(200*d)*q^3 + O(q^4)] + (q - 9/(200*d)*q^2 + 37633/(640000*d^2)*q^3 + O(q^4), + q^2 + 1/(200*d)*q^3 + O(q^4)) sage: MF.quasi_part_gens(r=2) - [q - 1/(4*d)*q^2 - 24903/(640000*d^2)*q^3 + O(q^4)] + (q - 1/(4*d)*q^2 - 24903/(640000*d^2)*q^3 + O(q^4),) sage: MF.quasi_part_gens(r=3) - [q + 1/(10*d)*q^2 - 7263/(640000*d^2)*q^3 + O(q^4)] + (q + 1/(10*d)*q^2 - 7263/(640000*d^2)*q^3 + O(q^4),) sage: MF.quasi_part_gens(r=4) - [q - 11/(20*d)*q^2 + 53577/(640000*d^2)*q^3 + O(q^4)] + (q - 11/(20*d)*q^2 + 53577/(640000*d^2)*q^3 + O(q^4),) sage: MF.quasi_part_gens(r=5) - [q - 1/(5*d)*q^2 + 4017/(640000*d^2)*q^3 + O(q^4)] + (q - 1/(5*d)*q^2 + 4017/(640000*d^2)*q^3 + O(q^4),) sage: MF.quasi_part_gens(r=1)[0] == MF.E2() * CuspForms(n=5, k=16, ep=1).gen(0) True @@ -1453,9 +1456,9 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() sage: MF.quasi_part_gens(r=1, min_exp=-2) == MF.quasi_part_gens(r=1, min_exp=1) True sage: MF.quasi_part_gens(r=1) - [q - 8*q^2 - 8*q^3 + 5952*q^4 + O(q^5), + (q - 8*q^2 - 8*q^3 + 5952*q^4 + O(q^5), q^2 - 8*q^3 + 208*q^4 + O(q^5), - q^3 - 16*q^4 + O(q^5)] + q^3 - 16*q^4 + O(q^5)) sage: MF = QuasiWeakModularForms(n=infinity, k=4, ep=1) sage: MF.quasi_part_gens(r=2, min_exp=2, order_1=-2)[0] == MF.E2()^2 * MF.E4()^(-2) * MF.f_inf()^2 @@ -1463,23 +1466,22 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() sage: [v.order_at(-1) for v in MF.quasi_part_gens(r=0, min_exp=2, order_1=-2)] [-2, -2] """ - - if (not self.is_weakly_holomorphic()): + if not self.is_weakly_holomorphic(): from warnings import warn warn("This function only determines generators of (quasi) weakly modular forms!") - (min_exp, order_1) = self._canonical_min_exp(min_exp, order_1) + min_exp, order_1 = self._canonical_min_exp(min_exp, order_1) # For modular forms spaces the quasi parts are all zero except for r=0 - if (self.is_modular()): + if self.is_modular(): r = ZZ(r) - if (r != 0): - return [] + if r: + return () # The lower bounds on the powers of f_inf and E4 determine # how large powers of E2 we can fit in... n = self.hecke_n() - if (n == infinity): + if n == infinity: max_numerator_weight = self._weight - 4*min_exp - 4*order_1 + 4 else: max_numerator_weight = self._weight - 4*n/(n-2)*min_exp + 4 @@ -1487,30 +1489,28 @@ def quasi_part_gens(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero() # If r is not specified we gather all generators for all possible r's if r is None: gens = [] - for rnew in range(QQ(max_numerator_weight/ZZ(2)).floor() + 1): - gens += self.quasi_part_gens(r=rnew, min_exp=min_exp, max_exp=max_exp, order_1=order_1) - return gens + for rnew in range(QQ(max_numerator_weight / ZZ(2)).floor() + 1): + gens.extend(self.quasi_part_gens(r=rnew, min_exp=min_exp, max_exp=max_exp, order_1=order_1)) + return tuple(gens) r = ZZ(r) if r < 0 or 2*r > max_numerator_weight: - return [] + return () E2 = self.E2() - ambient_weak_space = self.graded_ring().reduce_type("weak", degree=(self._weight-QQ(2*r), self._ep*(-1)**r)) + ambient_weak_space = self.graded_ring().reduce_type("weak", + degree=(self._weight-QQ(2*r), self._ep*(-1)**r)) order_inf = ambient_weak_space._l1 - order_1 - if (max_exp == infinity): + if max_exp == infinity: max_exp = order_inf - elif (max_exp < min_exp): - return [] + elif max_exp < min_exp: + return () else: max_exp = min(ZZ(max_exp), order_inf) - gens = [] - for m in range(min_exp, max_exp + 1): - gens += [ self(ambient_weak_space.F_basis(m, order_1=order_1)*E2**r) ] - - return gens + return tuple(self(ambient_weak_space.F_basis(m, order_1=order_1) * E2**r) + for m in range(min_exp, max_exp + 1)) def quasi_part_dimension(self, r=None, min_exp=0, max_exp=infinity, order_1=ZZ.zero()): r""" @@ -2096,7 +2096,7 @@ def q_basis(self, m=None, min_exp=0, order_1=ZZ.zero()): q^-1 + O(q^5) sage: MF = ModularForms(k=36) - sage: MF.q_basis() == MF.gens() + sage: MF.q_basis() == list(MF.gens()) True sage: QF = QuasiModularForms(k=6) @@ -2490,11 +2490,11 @@ def ambient_coordinate_vector(self, v): return self.module()(self.ambient_space().coordinate_vector(v)) - def gens(self): + def gens(self) -> tuple: r""" This method should be overloaded by subclasses. - Return a basis of ``self``. + Return a basis of ``self`` as a tuple. Note that the coordinate vector of elements of ``self`` are with respect to this basis. @@ -2503,11 +2503,10 @@ def gens(self): sage: from sage.modular.modform_hecketriangle.space import ModularForms sage: ModularForms(k=12).gens() # defined in space.py - [1 + 196560*q^2 + 16773120*q^3 + 398034000*q^4 + O(q^5), - q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5)] + (1 + 196560*q^2 + 16773120*q^3 + 398034000*q^4 + O(q^5), + q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5)) """ - - raise NotImplementedError("No generators are implemented yet for {}!".format(self)) + raise NotImplementedError(f"No generators are implemented yet for {self}!") def gen(self, k=0): r""" diff --git a/src/sage/modular/modform_hecketriangle/space.py b/src/sage/modular/modform_hecketriangle/space.py index 3a55142a63d..2e2093d4d04 100644 --- a/src/sage/modular/modform_hecketriangle/space.py +++ b/src/sage/modular/modform_hecketriangle/space.py @@ -219,9 +219,9 @@ def __init__(self, group, base_ring, k, ep, n): self._module = FreeModule(self.coeff_ring(), self.dimension()) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Return a basis of ``self`` as a list of basis elements. + Return a basis of ``self`` as a tuple of basis elements. EXAMPLES:: @@ -229,16 +229,15 @@ def gens(self): sage: MF = QuasiModularForms(n=5, k=6, ep=-1) sage: MF.default_prec(2) sage: MF.gens() - [1 - 37/(200*d)*q + O(q^2), + (1 - 37/(200*d)*q + O(q^2), 1 + 33/(200*d)*q + O(q^2), - 1 - 27/(200*d)*q + O(q^2)] + 1 - 27/(200*d)*q + O(q^2)) sage: MF = QuasiModularForms(n=infinity, k=2, ep=-1) sage: MF.default_prec(2) sage: MF.gens() - [1 - 24*q + O(q^2), 1 - 8*q + O(q^2)] + (1 - 24*q + O(q^2), 1 - 8*q + O(q^2)) """ - return self.quasi_part_gens() @cached_method @@ -255,7 +254,6 @@ def dimension(self): sage: len(MF.gens()) == MF.dimension() True """ - return self.quasi_part_dimension() @cached_method @@ -384,9 +382,9 @@ def __init__(self, group, base_ring, k, ep, n): self._module = FreeModule(self.coeff_ring(), self.dimension()) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Return a basis of ``self`` as a list of basis elements. + Return a basis of ``self`` as a tuple of basis elements. EXAMPLES:: @@ -396,19 +394,18 @@ def gens(self): sage: MF.dimension() 7 sage: MF.gens() - [q - 17535/(262144*d^2)*q^3 + O(q^4), + (q - 17535/(262144*d^2)*q^3 + O(q^4), q^2 - 47/(128*d)*q^3 + O(q^4), q - 9/(128*d)*q^2 + 15633/(262144*d^2)*q^3 + O(q^4), q^2 - 7/(128*d)*q^3 + O(q^4), q - 23/(64*d)*q^2 - 3103/(262144*d^2)*q^3 + O(q^4), q - 3/(64*d)*q^2 - 4863/(262144*d^2)*q^3 + O(q^4), - q - 27/(64*d)*q^2 + 17217/(262144*d^2)*q^3 + O(q^4)] + q - 27/(64*d)*q^2 + 17217/(262144*d^2)*q^3 + O(q^4)) sage: MF = QuasiCuspForms(n=infinity, k=10, ep=-1) sage: MF.gens() - [q - 16*q^2 - 156*q^3 - 256*q^4 + O(q^5), q - 60*q^3 - 256*q^4 + O(q^5)] + (q - 16*q^2 - 156*q^3 - 256*q^4 + O(q^5), q - 60*q^3 - 256*q^4 + O(q^5)) """ - return self.quasi_part_gens() @cached_method @@ -660,9 +657,9 @@ def __init__(self, group, base_ring, k, ep, n): self._module = FreeModule(self.coeff_ring(), self.dimension()) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Return a basis of ``self`` as a list of basis elements. + Return a basis of ``self`` as a tuple of basis elements. EXAMPLES:: @@ -671,15 +668,16 @@ def gens(self): sage: MF.dimension() 4 sage: MF.gens() - [1 + 360360*q^4 + O(q^5), + (1 + 360360*q^4 + O(q^5), q + 21742*q^4 + O(q^5), q^2 + 702*q^4 + O(q^5), - q^3 - 6*q^4 + O(q^5)] + q^3 - 6*q^4 + O(q^5)) sage: ModularForms(n=infinity, k=4).gens() - [1 + 240*q^2 + 2160*q^4 + O(q^5), q - 8*q^2 + 28*q^3 - 64*q^4 + O(q^5)] + (1 + 240*q^2 + 2160*q^4 + O(q^5), + q - 8*q^2 + 28*q^3 - 64*q^4 + O(q^5)) """ - return [self.F_basis(m) for m in range(self.dimension())] + return tuple(self.F_basis(m) for m in range(self.dimension())) @cached_method def dimension(self): @@ -800,9 +798,9 @@ def __init__(self, group, base_ring, k, ep, n): self._module = FreeModule(self.coeff_ring(), self.dimension()) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Return a basis of ``self`` as a list of basis elements. + Return a basis of ``self`` as a tuple of basis elements. EXAMPLES:: @@ -813,16 +811,16 @@ def gens(self): sage: MF.dimension() 3 sage: MF.gens() - [q + 296888795/(10319560704*d^3)*q^4 + O(q^5), + (q + 296888795/(10319560704*d^3)*q^4 + O(q^5), q^2 + 6629/(221184*d^2)*q^4 + O(q^5), - q^3 - 25/(96*d)*q^4 + O(q^5)] + q^3 - 25/(96*d)*q^4 + O(q^5)) sage: MF = CuspForms(n=infinity, k=8, ep=1) sage: MF.gen(0) == MF.E4()*MF.f_inf() True """ - return [self.F_basis(m, order_1=ZZ.one()) - for m in range(1, self.dimension() + 1)] + return tuple(self.F_basis(m, order_1=ZZ.one()) + for m in range(1, self.dimension() + 1)) @cached_method def dimension(self): @@ -978,18 +976,19 @@ def _change_degree(self, k, ep): k=k, ep=ep) @cached_method - def gens(self): + def gens(self) -> tuple: r""" - Return a basis of ``self`` as a list of basis elements. - Since this is the zero module an empty list is returned. + Return a basis of ``self`` as a tuple of basis elements. + + Since this is the zero module an empty tuple is returned. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.space import ZeroForm sage: ZeroForm(6, CC, 3, -1).gens() - [] + () """ - return [] + return () @cached_method def dimension(self): diff --git a/src/sage/modular/modform_hecketriangle/subspace.py b/src/sage/modular/modform_hecketriangle/subspace.py index b22840338dd..b67c3f47bc0 100644 --- a/src/sage/modular/modform_hecketriangle/subspace.py +++ b/src/sage/modular/modform_hecketriangle/subspace.py @@ -73,7 +73,8 @@ def ModularFormsSubSpace(*args, **kwargs): sage: subspace.ambient_space() ModularForms(n=3, k=12, ep=1) over Integer Ring sage: subspace.gens() - [1 + 720*q + 179280*q^2 + 16954560*q^3 + 396974160*q^4 + O(q^5), 1 - 1007*q + 220728*q^2 + 16519356*q^3 + 399516304*q^4 + O(q^5)] + (1 + 720*q + 179280*q^2 + 16954560*q^3 + 396974160*q^4 + O(q^5), + 1 - 1007*q + 220728*q^2 + 16519356*q^3 + 399516304*q^4 + O(q^5)) sage: ModularFormsSubSpace(MF.E4()^3-MF.E6()^2, reduce=True).ambient_space() CuspForms(n=3, k=12, ep=1) over Integer Ring sage: ModularFormsSubSpace(MF.E4()^3-MF.E6()^2, MF.J_inv()*MF.E4()^3, reduce=True) @@ -186,7 +187,8 @@ def __init__(self, ambient_space, basis, check): sage: subspace.basis()[0].parent() == MF True sage: subspace.gens() - [q + 78*q^2 + 2781*q^3 + 59812*q^4 + O(q^5), 1 + 360360*q^4 + O(q^5)] + (q + 78*q^2 + 2781*q^3 + 59812*q^4 + O(q^5), + 1 + 360360*q^4 + O(q^5)) sage: subspace.gens()[0].parent() == subspace True sage: subspace.is_ambient() @@ -200,7 +202,7 @@ def __init__(self, ambient_space, basis, check): sage: subspace Subspace of dimension 3 of QuasiCuspForms(n=+Infinity, k=12, ep=1) over Integer Ring sage: subspace.gens() - [q + 24*q^2 + O(q^3), q - 24*q^2 + O(q^3), q - 8*q^2 + O(q^3)] + (q + 24*q^2 + O(q^3), q - 24*q^2 + O(q^3), q - 8*q^2 + O(q^3)) """ FormsSpace_abstract.__init__(self, group=ambient_space.group(), base_ring=ambient_space.base_ring(), k=ambient_space.weight(), ep=ambient_space.ep(), n=ambient_space.hecke_n()) Module.__init__(self, base=ambient_space.base_ring()) @@ -208,7 +210,7 @@ def __init__(self, ambient_space, basis, check): self._ambient_space = ambient_space self._basis = list(basis) # self(v) instead would somehow mess up the coercion model - self._gens = [self._element_constructor_(v) for v in basis] + self._gens = tuple(self._element_constructor_(v) for v in basis) self._module = ambient_space._module.submodule([ambient_space.coordinate_vector(v) for v in basis]) # TODO: get the analytic type from the basis # self._analytic_type=self.AT(["quasi", "mero"]) @@ -306,7 +308,7 @@ def basis(self): return self._basis @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the basis of ``self``. @@ -316,11 +318,11 @@ def gens(self): sage: MF = ModularForms(n=6, k=20, ep=1) sage: subspace = MF.subspace([(MF.Delta()*MF.E4()^2).as_ring_element(), MF.gen(0)]) sage: subspace.gens() - [q + 78*q^2 + 2781*q^3 + 59812*q^4 + O(q^5), 1 + 360360*q^4 + O(q^5)] + (q + 78*q^2 + 2781*q^3 + 59812*q^4 + O(q^5), + 1 + 360360*q^4 + O(q^5)) sage: subspace.gens()[0].parent() == subspace True """ - return self._gens @cached_method diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index ef2d143b1af..f2a3df040cc 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -194,6 +194,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** +from typing import Iterator import weakref @@ -693,7 +694,7 @@ def radius(self): """ return self._radius - def gens(self): + def gens(self) -> Iterator: r""" Return a generator object that iterates over the (infinite) set of basis vectors of ``self``. diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index 99a8a772743..806b08709c3 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -129,20 +129,16 @@ def __init__(self, N, reps, indices, rels, equiv_ind): self._reps = reps self._indices = sorted(indices) - self._gens = [M2Z(reps[i]) for i in self._indices] + self._gens = tuple(M2Z(reps[i]) for i in self._indices) self._ngens = len(indices) if len(rels) != len(reps): raise ValueError("length of reps and length of rels must be equal") self._rels = rels - self._rel_dict = {} - for j, L in enumerate(rels): - self._rel_dict[reps[j]] = L + self._rel_dict = {reps[j]: L for j, L in enumerate(rels)} self._equiv_ind = equiv_ind - self._equiv_rep = {} - for ky in equiv_ind: - self._equiv_rep[ky] = reps[equiv_ind[ky]] + self._equiv_rep = {ky: reps[vy] for ky, vy in equiv_ind.items()} def _repr_(self): r""" @@ -203,19 +199,19 @@ def __iter__(self): """ return iter(self._reps) - def gens(self): + def gens(self) -> tuple: r""" - Return the list of coset representatives chosen as generators. + Return the tuple of coset representatives chosen as generators. EXAMPLES:: sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: A = ManinRelations(11) sage: A.gens() - [ + ( [1 0] [ 0 -1] [-1 -1] [0 1], [ 1 3], [ 3 2] - ] + ) """ return self._gens diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 29df3bfcde0..a1aa46b9be6 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -45,9 +45,9 @@ sage: QM.category() Category of commutative graded algebras over Rational Field sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) sage: E2 = QM.0; E4 = QM.1; E6 = QM.2 sage: E2 * E4 + E6 2 - 288*q - 20304*q^2 - 185472*q^3 - 855216*q^4 - 2697408*q^5 + O(q^6) @@ -92,10 +92,10 @@ :meth:`sage.modular.modform.ring.ModularFormsRing.gens`:: sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), 1 + 240*q^5 + O(q^6), - q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)) sage: QM.modular_forms_subring().gens() [1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), 1 + 240*q^5 + O(q^6), @@ -200,9 +200,9 @@ class QuasiModularForms(Parent, UniqueRepresentation): sage: QM = QuasiModularForms(1); QM Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) It is possible to access the weight 2 Eisenstein series:: @@ -454,9 +454,9 @@ def weight_2_eisenstein_series(self): """ return self(self.__polynomial_subring.gen()) - def gens(self): + def gens(self) -> tuple: r""" - Return a list of generators of the quasimodular forms ring. + Return a tuple of generators of the quasimodular forms ring. Note that the generators of the modular forms subring are the one given by the method :meth:`sage.modular.modform.ring.ModularFormsRing.gen_forms` @@ -465,30 +465,30 @@ def gens(self): sage: QM = QuasiModularForms(1) sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) sage: QM.modular_forms_subring().gen_forms() [1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] sage: QM = QuasiModularForms(5) sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), 1 + 240*q^5 + O(q^6), - q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)) An alias of this method is ``generators``:: sage: QuasiModularForms(1).generators() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) """ gen_list = [self.weight_2_eisenstein_series()] - for f in self.__modular_forms_subring.gen_forms(): - gen_list.append(self(f)) - return gen_list + gen_list.extend(self(f) + for f in self.__modular_forms_subring.gen_forms()) + return tuple(gen_list) generators = gens # alias @@ -529,7 +529,7 @@ def gen(self, n): sage: QM.4 Traceback (most recent call last): ... - IndexError: list index out of range + IndexError: tuple index out of range """ return self.gens()[n] @@ -568,9 +568,9 @@ def some_elements(self): EXAMPLES:: sage: QuasiModularForms(1).some_elements() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) """ return self.gens() From f910c5627df27122671c12023371420e21c6f8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 17:14:55 +0100 Subject: [PATCH 262/507] fixing doctests --- src/sage/modular/drinfeld_modform/tutorial.py | 2 +- src/sage/modular/modform_hecketriangle/readme.py | 14 +++++++------- src/sage/modular/pollack_stevens/manin_map.py | 8 ++++---- src/sage/modular/quasimodform/element.py | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/modular/drinfeld_modform/tutorial.py b/src/sage/modular/drinfeld_modform/tutorial.py index e4f64f10daf..3753e3e9ab7 100644 --- a/src/sage/modular/drinfeld_modform/tutorial.py +++ b/src/sage/modular/drinfeld_modform/tutorial.py @@ -213,7 +213,7 @@ sage: M = DrinfeldModularForms(K, 4, has_type=True) sage: M.gens() - [g1, g2, g3, h4] + (g1, g2, g3, h4) sage: h4 = M.3 sage: h4.weight() 40 diff --git a/src/sage/modular/modform_hecketriangle/readme.py b/src/sage/modular/modform_hecketriangle/readme.py index 60747e9578a..409a437473b 100644 --- a/src/sage/modular/modform_hecketriangle/readme.py +++ b/src/sage/modular/modform_hecketriangle/readme.py @@ -1015,12 +1015,12 @@ sage: QF = QuasiWeakModularForms(n=8, k=10/3, ep=-1) sage: QF.default_prec(1) sage: QF.quasi_part_gens(min_exp=-1) - [q^-1 + O(q), + (q^-1 + O(q), 1 + O(q), q^-1 - 9/(128*d) + O(q), 1 + O(q), q^-1 - 19/(64*d) + O(q), - q^-1 + 1/(64*d) + O(q)] + q^-1 + 1/(64*d) + O(q)) sage: QF.default_prec(QF.required_laurent_prec(min_exp=-1)) sage: QF.q_basis(min_exp=-1) # long time [q^-1 + O(q^5), @@ -1042,9 +1042,9 @@ 3 sage: MF.default_prec(2) sage: MF.gens() - [1 - 37/(200*d)*q + O(q^2), + (1 - 37/(200*d)*q + O(q^2), 1 + 33/(200*d)*q + O(q^2), - 1 - 27/(200*d)*q + O(q^2)] + 1 - 27/(200*d)*q + O(q^2)) - **Coordinate vectors for (quasi) holomorphic modular forms and (quasi) cusp forms:** @@ -1135,7 +1135,7 @@ sage: MF.dimension() 2 sage: MF.gens() - [1 + 240*q^2 + 2160*q^4 + O(q^5), q - 8*q^2 + 28*q^3 - 64*q^4 + O(q^5)] + (1 + 240*q^2 + 2160*q^4 + O(q^5), q - 8*q^2 + 28*q^3 - 64*q^4 + O(q^5)) sage: E4(i) 1.941017189... sage: E4.order_at(-1) @@ -1143,8 +1143,8 @@ sage: MF = (E2/E4).reduced_parent() sage: MF.quasi_part_gens(order_1=-1) - [1 - 40*q + 552*q^2 - 4896*q^3 + 33320*q^4 + O(q^5), - 1 - 24*q + 264*q^2 - 2016*q^3 + 12264*q^4 + O(q^5)] + (1 - 40*q + 552*q^2 - 4896*q^3 + 33320*q^4 + O(q^5), + 1 - 24*q + 264*q^2 - 2016*q^3 + 12264*q^4 + O(q^5)) sage: prec = MF.required_laurent_prec(order_1=-1) sage: qexp = (E2/E4).q_expansion(prec=prec) sage: qexp diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index 04120ba5d7d..9d22bdadbc5 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -315,13 +315,13 @@ def __getitem__(self, B): sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: S = Symk(0,QQ) sage: MR = ManinRelations(37); MR.gens() - [ + ( [1 0] [ 0 -1] [-1 -1] [-1 -2] [-2 -3] [-3 -1] [-1 -4] [-4 -3] [0 1], [ 1 4], [ 4 3], [ 3 5], [ 5 7], [ 7 2], [ 2 7], [ 7 5], [-2 -3] [ 3 4] - ] + ) sage: data = {M2Z([-2,-3,5,7]): S(0), M2Z([1,0,0,1]): S(0), M2Z([-1,-2,3,5]): S(0), M2Z([-1,-4,2,7]): S(1), M2Z([0,-1,1,4]): S(1), M2Z([-3,-1,7,2]): S(-1), M2Z([-2,-3,3,4]): S(0), M2Z([-4,-3,7,5]): S(0), M2Z([-1,-1,4,3]): S(0)} sage: D = OverconvergentDistributions(2, 37, 40) @@ -354,13 +354,13 @@ def compute_full_data(self): sage: from sage.modular.pollack_stevens.fund_domain import ManinRelations sage: S = Symk(0,QQ) sage: MR = ManinRelations(37); MR.gens() - [ + ( [1 0] [ 0 -1] [-1 -1] [-1 -2] [-2 -3] [-3 -1] [-1 -4] [-4 -3] [0 1], [ 1 4], [ 4 3], [ 3 5], [ 5 7], [ 7 2], [ 2 7], [ 7 5], [-2 -3] [ 3 4] - ] + ) sage: data = {M2Z([-2,-3,5,7]): S(0), M2Z([1,0,0,1]): S(0), M2Z([-1,-2,3,5]): S(0), M2Z([-1,-4,2,7]): S(1), M2Z([0,-1,1,4]): S(1), M2Z([-3,-1,7,2]): S(-1), M2Z([-2,-3,3,4]): S(0), M2Z([-4,-3,7,5]): S(0), M2Z([-1,-1,4,3]): S(0)} sage: f = ManinMap(S,MR,data) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index c8bbdd1a9a3..142d7ea885b 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -52,9 +52,9 @@ class QuasiModularFormsElement(ModuleElement): sage: QM = QuasiModularForms(1) sage: QM.gens() - [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + (1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)) sage: QM.0 + QM.1 2 + 216*q + 2088*q^2 + 6624*q^3 + 17352*q^4 + 30096*q^5 + O(q^6) sage: QM.0 * QM.1 From cc9d9b15455446e0bfdafb84302a254ebb8f104e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 13:26:19 +0100 Subject: [PATCH 263/507] add tuple typing to gens methods in algebras --- src/sage/algebras/askey_wilson.py | 2 +- src/sage/algebras/clifford_algebra.py | 4 ++-- src/sage/algebras/cluster_algebra.py | 2 +- src/sage/algebras/down_up_algebra.py | 2 +- src/sage/algebras/free_zinbiel_algebra.py | 2 +- .../algebras/hecke_algebras/ariki_koike_algebra.py | 2 +- .../algebras/hecke_algebras/cubic_hecke_algebra.py | 2 +- src/sage/algebras/jordan_algebra.py | 6 +++--- .../letterplace/free_algebra_letterplace.pyx | 2 +- .../algebras/lie_algebras/classical_lie_algebra.py | 2 +- src/sage/algebras/lie_algebras/free_lie_algebra.py | 2 +- src/sage/algebras/lie_algebras/heisenberg.py | 8 ++++---- src/sage/algebras/lie_algebras/lie_algebra.py | 2 +- src/sage/algebras/lie_algebras/subalgebra.py | 2 +- src/sage/algebras/lie_algebras/verma_module.py | 2 +- src/sage/algebras/octonion_algebra.pyx | 2 +- src/sage/algebras/q_commuting_polynomials.py | 2 +- src/sage/algebras/q_system.py | 2 +- src/sage/algebras/quantum_clifford.py | 2 +- .../algebras/quantum_groups/quantum_group_gap.py | 2 +- .../algebras/quantum_matrix_coordinate_algebra.py | 4 ++-- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- src/sage/algebras/steenrod/steenrod_algebra.py | 12 ++++++------ src/sage/algebras/yangian.py | 4 ++-- src/sage/algebras/yokonuma_hecke_algebra.py | 2 +- 25 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/sage/algebras/askey_wilson.py b/src/sage/algebras/askey_wilson.py index f8fc56f5f46..a2411f079cd 100644 --- a/src/sage/algebras/askey_wilson.py +++ b/src/sage/algebras/askey_wilson.py @@ -348,7 +348,7 @@ def build_monomial(g): return Family(A, build_monomial) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 091f5e0d559..aa19526e51c 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -787,7 +787,7 @@ def gen(self, i): """ return self._from_dict({FrozenBitset((i,)): self.base_ring().one()}, remove_zeros=False) - def algebra_generators(self): + def algebra_generators(self) -> Family: """ Return the algebra generators of ``self``. @@ -801,7 +801,7 @@ def algebra_generators(self): d = {x: self.gen(i) for i, x in enumerate(self.variable_names())} return Family(self.variable_names(), lambda x: d[x]) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` (as an algebra). diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index ca34a34acfc..5cb746983f7 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -2094,7 +2094,7 @@ def retract(self, x): return self(x) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the list of initial cluster variables and coefficients of ``self``. diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index 48e24c4fa2d..d7a8c3b14cd 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -319,7 +319,7 @@ def algebra_generators(self): return Family({'d': d, 'u': u}) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 875fde1fba9..1be0a4600be 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -343,7 +343,7 @@ def change_ring(self, R): return FreeZinbielAlgebra(R, n=len(A), names=A, side=self._side) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py index 2e6cba1e965..336e8c28a40 100644 --- a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py +++ b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py @@ -532,7 +532,7 @@ def cyclotomic_parameters(self): u = cyclotomic_parameters @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py index ac5d9b945f7..ecb9551561f 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py @@ -1415,7 +1415,7 @@ def algebra_generators(self): from sage.sets.family import Family return Family(self._cubic_braid_group.gens(), self.monomial) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index 0b6fc0111bf..f5881fbba88 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -328,7 +328,7 @@ def basis(self): algebra_generators = basis # TODO: Keep this until we can better handle R.<...> shorthand - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. @@ -763,7 +763,7 @@ def basis(self): algebra_generators = basis - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. @@ -1316,7 +1316,7 @@ def basis(self): algebra_generators = basis - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx index 10146c36aeb..188600b3006 100644 --- a/src/sage/algebras/letterplace/free_algebra_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_letterplace.pyx @@ -348,7 +348,7 @@ cdef class FreeAlgebra_letterplace(Parent): p *= self._current_ring.gen(j) return FreeAlgebraElement_letterplace(self, p) - def gens(self): + def gens(self) -> tuple: """ Return the tuple of generators. diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index e4e2eb75e68..b149a57576c 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -2170,7 +2170,7 @@ def _part_generators(self, positive=False): return Family(I, d.__getitem__) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` in the order of `e_i`, `f_i`, and `h_i`. diff --git a/src/sage/algebras/lie_algebras/free_lie_algebra.py b/src/sage/algebras/lie_algebras/free_lie_algebra.py index e841be77a59..08462de239b 100644 --- a/src/sage/algebras/lie_algebras/free_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/free_lie_algebra.py @@ -419,7 +419,7 @@ def lie_algebra_generators(self): """ return self.Lyndon().lie_algebra_generators() - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self`` in the Lyndon basis. diff --git a/src/sage/algebras/lie_algebras/heisenberg.py b/src/sage/algebras/lie_algebras/heisenberg.py index de4a6b27562..377ac303465 100644 --- a/src/sage/algebras/lie_algebras/heisenberg.py +++ b/src/sage/algebras/lie_algebras/heisenberg.py @@ -6,15 +6,15 @@ - Travis Scrimshaw (2013-08-13): Initial version """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2013-2017 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.misc.cachefunc import cached_method from sage.structure.indexed_generators import IndexedGenerators @@ -234,7 +234,7 @@ def n(self): return self._n @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the Lie algebra generators of ``self``. diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 12a992bbffd..11ebbeec026 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -823,7 +823,7 @@ def lie_algebra_generators(self): return Family(self._indices, self.monomial, name="monomial map") @cached_method - def gens(self): + def gens(self) -> tuple: """ Return a tuple whose entries are the generators for this object, in some order. diff --git a/src/sage/algebras/lie_algebras/subalgebra.py b/src/sage/algebras/lie_algebras/subalgebra.py index 1dd5444f766..84fb9addb5a 100644 --- a/src/sage/algebras/lie_algebras/subalgebra.py +++ b/src/sage/algebras/lie_algebras/subalgebra.py @@ -646,7 +646,7 @@ def retract(self, X): return self.element_class(self, X) - def gens(self): + def gens(self) -> tuple: r""" Return the generating set of ``self``. diff --git a/src/sage/algebras/lie_algebras/verma_module.py b/src/sage/algebras/lie_algebras/verma_module.py index 71ea8e68cb9..09962f11b6d 100644 --- a/src/sage/algebras/lie_algebras/verma_module.py +++ b/src/sage/algebras/lie_algebras/verma_module.py @@ -356,7 +356,7 @@ def highest_weight_vector(self): return self._from_dict({self._indices.one(): one}, remove_zeros=False, coerce=False) - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` as a `U(\mathfrak{g})`-module. diff --git a/src/sage/algebras/octonion_algebra.pyx b/src/sage/algebras/octonion_algebra.pyx index c38250b0be7..46e2028db53 100644 --- a/src/sage/algebras/octonion_algebra.pyx +++ b/src/sage/algebras/octonion_algebra.pyx @@ -952,7 +952,7 @@ class OctonionAlgebra(UniqueRepresentation, Parent): return 0 @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/q_commuting_polynomials.py b/src/sage/algebras/q_commuting_polynomials.py index f19eaa042e4..6e2647a1be2 100644 --- a/src/sage/algebras/q_commuting_polynomials.py +++ b/src/sage/algebras/q_commuting_polynomials.py @@ -147,7 +147,7 @@ def gen(self, i): return self.monomial(self._indices.gen(i)) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/q_system.py b/src/sage/algebras/q_system.py index a8342434847..dc4dd4d17f5 100644 --- a/src/sage/algebras/q_system.py +++ b/src/sage/algebras/q_system.py @@ -382,7 +382,7 @@ def algebra_generators(self): d = {a: self.Q(a, 1) for a in I} return Family(I, d.__getitem__) - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/algebras/quantum_clifford.py b/src/sage/algebras/quantum_clifford.py index 99c0c1e226f..60a280820c7 100644 --- a/src/sage/algebras/quantum_clifford.py +++ b/src/sage/algebras/quantum_clifford.py @@ -301,7 +301,7 @@ def algebra_generators(self): return Family(sorted(d), lambda i: d[i]) @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self``. diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index 931ee211ff1..4e95d692dd6 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -500,7 +500,7 @@ def zero(self): return self.element_class(self, self._libgap.ZeroImmutable()) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/algebras/quantum_matrix_coordinate_algebra.py b/src/sage/algebras/quantum_matrix_coordinate_algebra.py index 9ddde4376c8..c0bff39501e 100644 --- a/src/sage/algebras/quantum_matrix_coordinate_algebra.py +++ b/src/sage/algebras/quantum_matrix_coordinate_algebra.py @@ -188,7 +188,7 @@ def one_basis(self): return self._indices.one() @cached_method - def gens(self): + def gens(self) -> tuple: r""" Return the generators of ``self`` as a tuple. @@ -586,7 +586,7 @@ def m(self): return self._m @cached_method - def algebra_generators(self): + def algebra_generators(self) -> Family: """ Return the algebra generators of ``self``. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 2409db3840b..6c535334361 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1694,7 +1694,7 @@ def one(self): """ return self.quaternion_algebra().one() - def gens(self): + def gens(self) -> tuple: """ Return generators for ``self``. diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index e9b86a59098..953bfc00bbe 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -452,12 +452,13 @@ # Distributed under the terms of the GNU General Public License (GPL) # **************************************************************************** -from sage.combinat.free_module import CombinatorialFreeModule -from sage.misc.lazy_attribute import lazy_attribute -from sage.misc.cachefunc import cached_method +from sage.categories.homset import Hom from sage.categories.modules_with_basis import ModulesWithBasis from sage.categories.tensor import tensor -from sage.categories.homset import Hom +from sage.combinat.free_module import CombinatorialFreeModule +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.sets.family import Family ###################################################### # the main class @@ -2619,7 +2620,7 @@ def ngens(self): return sum(self._profile) return sum(self._profile[0]) + len([a for a in self._profile[1] if a == 2]) - def gens(self): + def gens(self) -> Family: r""" Family of generators for this algebra. @@ -2676,7 +2677,6 @@ def gens(self): sage: SteenrodAlgebra(p=5, profile=[[2,1], [2,2,2]]).algebra_generators() Family (Q_0, P(1), P(5)) """ - from sage.sets.family import Family from sage.sets.non_negative_integers import NonNegativeIntegers from sage.rings.infinity import Infinity n = self.ngens() diff --git a/src/sage/algebras/yangian.py b/src/sage/algebras/yangian.py index 748586a30b9..73e5698a8ec 100644 --- a/src/sage/algebras/yangian.py +++ b/src/sage/algebras/yangian.py @@ -832,13 +832,13 @@ def gen(self, r, i=None, j=None): 0 """ if i is None and j is None: - r,i,j = r + r, i, j = r if r > self._level: return self.zero() return Yangian.gen(self, r, i, j) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. diff --git a/src/sage/algebras/yokonuma_hecke_algebra.py b/src/sage/algebras/yokonuma_hecke_algebra.py index f8e8f724adc..00bdde88012 100644 --- a/src/sage/algebras/yokonuma_hecke_algebra.py +++ b/src/sage/algebras/yokonuma_hecke_algebra.py @@ -252,7 +252,7 @@ def algebra_generators(self): return Family(sorted(d), lambda i: d[i]) @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. From 450d6f60c00a4e4bd6fe0cd9a3171771a080c7f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 20:37:52 +0100 Subject: [PATCH 264/507] typing annotation for gens method in rings and groups (pyx files) --- src/sage/groups/libgap_wrapper.pyx | 2 +- src/sage/rings/complex_arb.pyx | 2 +- src/sage/rings/integer_ring.pyx | 2 +- src/sage/rings/polynomial/pbori/pbori.pyx | 4 ++-- src/sage/rings/polynomial/symmetric_reduction.pyx | 2 +- src/sage/rings/real_arb.pyx | 2 +- src/sage/rings/real_mpfi.pyx | 8 ++++---- src/sage/rings/real_mpfr.pyx | 8 ++++---- src/sage/rings/ring_extension.pyx | 4 ++-- src/sage/rings/semirings/tropical_semiring.pyx | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index fe09dc080c1..771280fdbf9 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -362,7 +362,7 @@ class ParentLibGAP(SageObject): for gap_subgroup in self._libgap.MaximalNormalSubgroups()] @cached_method - def gens(self): + def gens(self) -> tuple: """ Return the generators of the group. diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index fb9d821a413..89a91f10eab 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -492,7 +492,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): else: raise ValueError("only one generator") - def gens(self): + def gens(self) -> tuple: r""" Return the tuple of generators of this complex ball field, i.e. ``(i,)``. diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index d6555ac0eab..f565ef22430 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1036,7 +1036,7 @@ cdef class IntegerRing_class(CommutativeRing): from sage.rings.finite_rings.residue_field import ResidueField return ResidueField(p, names = None, check = check) - def gens(self): + def gens(self) -> tuple: """ Return the tuple ``(1,)`` containing a single element, the additive generator of the integers, which is 1. diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 404771a2c85..0f4fcd92b8d 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -528,7 +528,7 @@ cdef class BooleanPolynomialRing(BooleanPolynomialRing_base): raise ValueError("generator not defined") return new_BP_from_PBVar(self, self._pbring.variable(self.pbind[idx])) - def gens(self): + def gens(self) -> tuple: """ Return the tuple of variables in this ring. @@ -1973,7 +1973,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): return new_BM_from_PBVar(self, (self._ring), newvar) - def gens(self): + def gens(self) -> tuple: """ Return the tuple of generators of this monoid. diff --git a/src/sage/rings/polynomial/symmetric_reduction.pyx b/src/sage/rings/polynomial/symmetric_reduction.pyx index 0794ca8a941..0559aa77ca8 100644 --- a/src/sage/rings/polynomial/symmetric_reduction.pyx +++ b/src/sage/rings/polynomial/symmetric_reduction.pyx @@ -254,7 +254,7 @@ cdef class SymmetricReductionStrategy: return richcmp((left._parent, left._lm, left._tail), (right._parent, right._lm, right._tail), op) - def gens(self): + def gens(self) -> list: """ Return the list of Infinite Polynomials modulo which ``self`` reduces. diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 3c063f193a8..280e08ea1d7 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -577,7 +577,7 @@ class RealBallField(UniqueRepresentation, sage.rings.abc.RealBallField): return super()._repr_option(key) - def gens(self): + def gens(self) -> tuple: r""" EXAMPLES:: diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index ce9958ce7e7..63906f4e02b 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -950,16 +950,16 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField): """ return 1 - def gens(self): + def gens(self) -> tuple: """ - Return a list of generators. + Return a tuple of generators. EXAMPLES:: sage: RIF.gens() - [1] + (1,) """ - return [self.gen()] + return (self.gen(),) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 31d0bb96aed..1a6acd9a9f7 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -907,16 +907,16 @@ cdef class RealField_class(sage.rings.abc.RealField): """ return 1 - def gens(self): + def gens(self) -> tuple: """ - Return a list of generators. + Return a tuple of generators. EXAMPLES:: sage: RR.gens() - [1.00000000000000] + (1.00000000000000,) """ - return [self.gen()] + return (self.gen(),) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 9d8a626094a..96449d10537 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1314,7 +1314,7 @@ cdef class RingExtension_generic(Parent): elt = self._backend.an_element() return self.element_class(self, elt) - def gens(self, base=None): + def gens(self, base=None) -> tuple: r""" Return the generators of this extension over ``base``. @@ -2661,7 +2661,7 @@ cdef class RingExtensionWithGen(RingExtensionWithBasis): S = PolynomialRing(self._base, name=var) return S(coeffs) - def gens(self, base=None): + def gens(self, base=None) -> tuple: r""" Return the generators of this extension over ``base``. diff --git a/src/sage/rings/semirings/tropical_semiring.pyx b/src/sage/rings/semirings/tropical_semiring.pyx index 205966541ab..993086004c6 100644 --- a/src/sage/rings/semirings/tropical_semiring.pyx +++ b/src/sage/rings/semirings/tropical_semiring.pyx @@ -640,7 +640,7 @@ class TropicalSemiring(Parent, UniqueRepresentation): multiplicative_identity = one - def gens(self): + def gens(self) -> tuple: """ Return the generators of ``self``. From b95f344a698d26faae50de392c773fe53d70f777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 20:53:56 +0100 Subject: [PATCH 265/507] typing and details in tableaux files --- src/sage/combinat/skew_tableau.py | 18 +++++++++--------- src/sage/combinat/tableau.py | 26 +++++++++++++------------- src/sage/combinat/tableau_tuple.py | 12 ++++++------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 44189013d5c..469a84e9ee9 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -620,7 +620,7 @@ def weight(self): evaluation = weight - def is_standard(self): + def is_standard(self) -> bool: """ Return ``True`` if ``self`` is a standard skew tableau and ``False`` otherwise. @@ -640,7 +640,7 @@ def is_standard(self): w = [i for row in self for i in row if i is not None] return sorted(w) == list(range(1, len(w) + 1)) and self.is_semistandard() - def is_semistandard(self): + def is_semistandard(self) -> bool: """ Return ``True`` if ``self`` is a semistandard skew tableau and ``False`` otherwise. @@ -1324,7 +1324,7 @@ def shuffle(self, t2): corner = self.cells_containing(i)[0] # slide t2_new backwards, record i in the vacated square - (t2_new, (x, y)) = t2_new.slide(corner, True) + t2_new, (x, y) = t2_new.slide(corner, True) t1_new[x][y] = i t1_new = SkewTableau(t1_new) @@ -1582,10 +1582,12 @@ def to_expr(self): rows.reverse() return [self.inner_shape(), rows] - def is_ribbon(self): + def is_ribbon(self) -> bool: r""" Return ``True`` if and only if the shape of ``self`` is a - ribbon, that is, if it has exactly one cell in each of `q` + ribbon. + + This means that it has exactly one cell in each of `q` consecutive diagonals for some nonnegative integer `q`. EXAMPLES:: @@ -1824,7 +1826,7 @@ def cells_containing(self, i): cell_list.append((r, c)) return cell_list - def is_k_tableau(self, k): + def is_k_tableau(self, k) -> bool: r""" Check whether ``self`` is a valid skew weak `k`-tableau. @@ -1857,11 +1859,9 @@ def _label_skew(list_of_cells, sk): sage: skew_tableau._label_skew(l, empty) [[1, 4], [3, 2]] """ - i = 1 skew = [list(row) for row in sk] - for row, column in list_of_cells: + for i, (row, column) in enumerate(list_of_cells, start=1): skew[row][column] = i - i += 1 return skew diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index eef2536beba..8b33b1cbd66 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1694,7 +1694,7 @@ def weight(self): evaluation = weight - def is_row_strict(self): + def is_row_strict(self) -> bool: """ Return ``True`` if ``self`` is a row strict tableau and ``False`` otherwise. @@ -1715,7 +1715,7 @@ def is_row_strict(self): """ return all(row[i] < row[i+1] for row in self for i in range(len(row)-1)) - def is_row_increasing(self, weak=False): + def is_row_increasing(self, weak=False) -> bool: r""" Return ``True`` if the entries in each row are in increasing order, and ``False`` otherwise. @@ -1741,7 +1741,7 @@ def test(a, b): return a < b return all(test(a, b) for row in self for (a, b) in zip(row, row[1:])) - def is_column_increasing(self, weak=False): + def is_column_increasing(self, weak=False) -> bool: r""" Return ``True`` if the entries in each column are in increasing order, and ``False`` otherwise. @@ -1770,7 +1770,7 @@ def tworow(a, b): return all(test(a[i], b_i) for i, b_i in enumerate(b)) return all(tworow(self[r], self[r + 1]) for r in range(len(self) - 1)) - def is_column_strict(self): + def is_column_strict(self) -> bool: """ Return ``True`` if ``self`` is a column strict tableau and ``False`` otherwise. @@ -1801,7 +1801,7 @@ def tworow(a, b): return all(a[i] < b_i for i, b_i in enumerate(b)) return all(tworow(self[r], self[r+1]) for r in range(len(self)-1)) - def is_semistandard(self): + def is_semistandard(self) -> bool: r""" Return ``True`` if ``self`` is a semistandard tableau, and ``False`` otherwise. @@ -1824,7 +1824,7 @@ def is_semistandard(self): """ return self.is_row_increasing(weak=True) and self.is_column_increasing() - def is_standard(self): + def is_standard(self) -> bool: """ Return ``True`` if ``self`` is a standard tableau and ``False`` otherwise. @@ -1843,7 +1843,7 @@ def is_standard(self): entries = sorted(self.entries()) return entries == list(range(1, self.size() + 1)) and self.is_row_strict() and self.is_column_strict() - def is_increasing(self): + def is_increasing(self) -> bool: """ Return ``True`` if ``self`` is an increasing tableau and ``False`` otherwise. @@ -1865,7 +1865,7 @@ def is_increasing(self): """ return self.is_row_strict() and self.is_column_strict() - def is_rectangular(self): + def is_rectangular(self) -> bool: """ Return ``True`` if the tableau ``self`` is rectangular and ``False`` otherwise. @@ -2067,7 +2067,7 @@ def k_weight(self, k): return res - def is_k_tableau(self, k): + def is_k_tableau(self, k) -> bool: r""" Check whether ``self`` is a valid weak `k`-tableau. @@ -2518,7 +2518,7 @@ def reverse_bump(self, loc): if not (self.is_semistandard()): raise ValueError("reverse bumping is only defined for semistandard tableaux") try: - (r, c) = loc + r, c = loc if (r, c) not in self.corners(): raise ValueError("invalid corner") except TypeError: @@ -3177,7 +3177,7 @@ def add_entry(self, cell, m): IndexError: (2, 2) is not an addable cell of the tableau """ tab = self.to_list() - (r, c) = cell + r, c = cell try: tab[r][c] = m # will work if we are replacing an entry except IndexError: @@ -3616,7 +3616,7 @@ def symmetric_group_action_on_entries(self, w): except Exception: return Tableau([[w[entry-1] for entry in row] for row in self]) - def is_key_tableau(self): + def is_key_tableau(self) -> bool: r""" Return ``True`` if ``self`` is a key tableau or ``False`` otherwise. @@ -4788,7 +4788,7 @@ def dominates(self, t): return all(self.restrict(m).shape().dominates(t.restrict(m).shape()) for m in range(1, 1 + self.size())) - def is_standard(self): + def is_standard(self) -> bool: """ Return ``True`` since ``self`` is a standard tableau. diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 6c75106154b..863b47dc6a6 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -823,7 +823,7 @@ def entry(self, l, r, c): """ return self[l][r][c] - def is_row_strict(self): + def is_row_strict(self) -> bool: """ Return ``True`` if the tableau ``self`` is row strict and ``False`` otherwise. @@ -874,7 +874,7 @@ def first_row_descent(self): return (k, cell[0], cell[1]) return None - def is_column_strict(self): + def is_column_strict(self) -> bool: """ Return ``True`` if the tableau ``self`` is column strict and ``False`` otherwise. @@ -925,7 +925,7 @@ def first_column_descent(self): return (k, cell[0], cell[1]) return None - def is_standard(self): + def is_standard(self) -> bool: r""" Return ``True`` if the tableau ``self`` is a standard tableau and ``False`` otherwise. @@ -1173,7 +1173,7 @@ def add_entry(self, cell, m): ... IndexError: (2, 1, 2) is not an addable cell of the tableau """ - (k, r, c) = cell + k, r, c = cell tab = self.to_list() try: @@ -5017,7 +5017,7 @@ def random_element(self): while m < mu.size(): m += 1 i = randint(0, len(addables) - 1) # index for a random addable cell - (k, r, c) = addables[i] # the actual cell + k, r, c = addables[i] # the actual cell # remove the cell we just added from the list addable nodes addables.pop(i) # add m into the tableau @@ -5336,7 +5336,7 @@ def _add_entry_fast(T, cell, m): 6 8 12 14 2 11 10 """ - (k, r, c) = cell + k, r, c = cell tab = T.to_list() try: From 400bd424232aa508c34388a17249b311073a4f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 21:06:17 +0100 Subject: [PATCH 266/507] two more details in tableaux --- src/sage/combinat/ribbon_tableau.py | 4 ++-- src/sage/combinat/tableau.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 6a1643a46c7..66e6348e58b 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -854,8 +854,8 @@ def weight(self): sage: a.weight() [5, 3, 1] """ - weights = [x.weight() for x in self] - m = max([len(x) for x in weights]) + weights = (x.weight() for x in self) + m = max(len(x) for x in weights) weight = [0] * m for w in weights: for i in range(len(w)): diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 8b33b1cbd66..c9d1164d0a5 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1298,7 +1298,7 @@ def to_sign_matrix(self, max_entry=None): raise ValueError("the entries must be nonnegative integers") from sage.matrix.matrix_space import MatrixSpace if max_entry is None: - max_entry = max([max(c) for c in self]) + max_entry = max(max(c) for c in self) MS = MatrixSpace(ZZ, len(self[0]), max_entry) Tconj = self.conjugate() conj_len = len(Tconj) From 033c93b2b263cc61f00528658038023c6c762eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 Feb 2025 21:59:21 +0100 Subject: [PATCH 267/507] fix mistake --- src/sage/combinat/ribbon_tableau.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 66e6348e58b..a849c6ac88f 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -26,7 +26,8 @@ from sage.rings.integer import Integer from sage.combinat.combinat import CombinatorialElement from sage.combinat.skew_partition import SkewPartition, SkewPartitions -from sage.combinat.skew_tableau import SkewTableau, SkewTableaux, SemistandardSkewTableaux +from sage.combinat.skew_tableau import (SkewTableau, SkewTableaux, + SemistandardSkewTableaux) from sage.combinat.tableau import Tableaux from sage.combinat.partition import Partition, _Partitions from sage.combinat.permutation import to_standard @@ -94,7 +95,8 @@ def __classcall_private__(cls, rt=None, expr=None): try: rt = [tuple(row) for row in rt] except TypeError: - raise TypeError("each element of the ribbon tableau must be an iterable") + raise TypeError("each element of the ribbon tableau " + "must be an iterable") if not all(row for row in rt): raise TypeError("a ribbon tableau cannot have empty rows") # calls the inherited __init__ method (of SkewTableau ) @@ -195,9 +197,10 @@ class RibbonTableaux(UniqueRepresentation, Parent): REFERENCES: - .. [vanLeeuwen91] Marc. A. A. van Leeuwen, *Edge sequences, ribbon tableaux, - and an action of affine permutations*. Europe J. Combinatorics. **20** - (1999). http://wwwmathlabo.univ-poitiers.fr/~maavl/pdf/edgeseqs.pdf + .. [vanLeeuwen91] Marc. A. A. van Leeuwen, *Edge sequences, + ribbon tableaux, and an action of affine permutations*. + Europe J. Combinatorics. **20** (1999). + http://wwwmathlabo.univ-poitiers.fr/~maavl/pdf/edgeseqs.pdf """ @staticmethod def __classcall_private__(cls, shape=None, weight=None, length=None): @@ -318,10 +321,11 @@ def __iter__(self): sage: RibbonTableaux([[2,2],[]],[1,1],2).list() [[[0, 0], [1, 2]], [[1, 0], [2, 0]]] """ - for x in graph_implementation_rec(self._shape, self._weight, self._length, list_rec): + for x in graph_implementation_rec(self._shape, self._weight, + self._length, list_rec): yield self.from_expr(x) - def _repr_(self): + def _repr_(self) -> str: """ Return a string representation of ``self``. @@ -332,7 +336,7 @@ def _repr_(self): """ return "Ribbon tableaux of shape %s and weight %s with %s-ribbons" % (repr(self._shape), list(self._weight), self._length) - def __contains__(self, x): + def __contains__(self, x) -> bool: """ Note that this just checks to see if ``x`` appears in ``self``. @@ -854,7 +858,7 @@ def weight(self): sage: a.weight() [5, 3, 1] """ - weights = (x.weight() for x in self) + weights = [x.weight() for x in self] m = max(len(x) for x in weights) weight = [0] * m for w in weights: From 65c54ced6720ef81e5f84fe0cbb0c058e3b38283 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Sat, 8 Feb 2025 00:45:19 -0700 Subject: [PATCH 268/507] Possibly fix error in random seeded test in src/sage/rings/tests.py. Might be another error in the tests the is specific to certain computers as fixed test does not correspond to the test Dave Morris and I discussed. --- src/sage/rings/tests.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index 09e0d585078..2e5594672a2 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -444,16 +444,16 @@ def test_karatsuba_multiplication(base_ring, maxdeg1, maxdeg2, sage: from sage.rings.tests import test_karatsuba_multiplication sage: test_karatsuba_multiplication(ZZ, 6, 5, verbose=True, seed=42) test_karatsuba_multiplication: ring=Univariate Polynomial Ring in x over Integer Ring, threshold=2 - (x^6 + 4*x^5 + 4*x^4 - 3*x^3 - x^2 - x)*(2*x^4 + 3*x^3 - 20*x^2 - 2*x + 1) - (4*x^5 + 16*x^2 + x - 41)*(x^2 + x - 1) - (8*x^2 + 2*x + 1)*(3) - (-4*x - 1)*(-8*x^2 - x) - (-x^6 - x^3 - x^2 + x + 1)*(2*x^3 - x + 3) - (-x^2 + x + 1)*(x^4 + x^3 - x^2 - x + 76) - (4*x^3 + x^2 + 6)*(-x^2 - 5*x) - (x + 4)*(-x + 5) - (-2*x)*(3*x^2 - x) - (x^6 + 21*x^5 + x^4 + 4*x^3 - x^2)*(14*x^4 + x^3 + 2*x^2 - 12*x) + (2*x^6 - x^5 - x^4 - 3*x^3 + 4*x^2 + 4*x + 1)*(4*x^4 + x^3 - 2*x^2 - 20*x + 3) + (16*x^2)*(-41*x + 1) + (x^6 + 2*x^5 + 8*x^4 - x^3 + x^2 + x)*(-x^2 - 4*x + 3) + (-x^3 - x - 8)*(-1) + (x - 1)*(-x^5 + 3*x^4 - x^3 + 2*x + 1) + (x^3 + x^2 + x + 1)*(4*x^3 + 76*x^2 - x - 1) + (x^6 - 5*x^4 - x^3 + 6*x^2 + 1)*(5*x^2 - x + 4) + (3*x - 2)*(x - 1) + (21)*(14*x^5 - x^2 + 4*x + 1) + (12*x^5 - 12*x^2 + 2*x + 1)*(26*x^4 + x^3 + 1) Test Karatsuba multiplication of polynomials of small degree over some common rings:: From 3e386bb9bf4821f7da929758ba89d32c93f04424 Mon Sep 17 00:00:00 2001 From: mklss <59539887+mklss@users.noreply.github.com> Date: Sat, 8 Feb 2025 22:05:03 +0100 Subject: [PATCH 269/507] Address comments. --- src/sage/parallel/decorate.py | 8 ++++---- src/sage/parallel/use_fork.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index fc38410b7bb..a344ddcfa5a 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -308,7 +308,7 @@ def parallel(p_iter='fork', ncpus=None, **kwds): - ``ncpus`` -- integer; maximal number of subprocesses to use at the same time - ``timeout`` -- number of seconds until each subprocess is killed (only supported by ``'fork'``; zero means not at all) - - ``reseed_rng``: reseed the rng in each subprocess + - ``reseed_rng``: reseed the rng (random number generator) in each subprocess .. warning:: @@ -402,10 +402,10 @@ def parallel(p_iter='fork', ncpus=None, **kwds): By default, all subprocesses use the same random seed and therefore the same deterministic randomness. For functions that should be randomized, we can reseed the random seed in each subprocess:: - sage: @parallel(reseed_rng = True) - ....: def unif(n): return ZZ.random_element(x = 0, y = n) + sage: @parallel(reseed_rng=True) + ....: def unif(n): return ZZ.random_element(x=0, y=n) sage: set_random_seed(42) - sage: sorted(unif([1000]*3)) + sage: sorted(unif([1000]*3)) # random [(((1000,), {}), 444), (((1000,), {}), 597), (((1000,), {}), 640)] .. warning:: diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index 89aa1c42146..f83ebbc44e6 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -70,7 +70,7 @@ class p_iter_fork: about what the iterator does (e.g., killing subprocesses) - ``reset_interfaces`` -- boolean (default: ``True``); whether to reset all pexpect interfaces - - ``reseed_rng`` -- booolean (default: ``False``); whether or not to reseed the rng in the subprocesses + - ``reseed_rng`` -- boolean (default: ``False``); whether or not to reseed the rng in the subprocesses EXAMPLES:: @@ -164,7 +164,7 @@ def __call__(self, f, inputs): n = self.ncpus inputs = list(inputs) if self.reseed_rng: - seeds = [getrandbits(512) for _ in range(0, len(inputs))] + seeds = [getrandbits(512) for _ in range(len(inputs))] vs = list(zip(inputs, seeds)) else: vs = list(zip(inputs, [None]*len(inputs))) @@ -173,7 +173,7 @@ def __call__(self, f, inputs): while vs or workers: # Spawn up to n subprocesses while vs and len(workers) < n: - (v0, seed0) = vs.pop(0) # Input value and seed for the next subprocess + v0, seed0 = vs.pop(0) # Input value and seed for the next subprocess with ContainChildren(): pid = os.fork() # The way fork works is that pid returns the @@ -181,7 +181,8 @@ def __call__(self, f, inputs): # process and returns 0 for the subprocess. if not pid: # This is the subprocess. - self.worker_seed = seed0 if self.reseed_rng else None + if self.reseed_rng: + self.worker_seed = seed0 self._subprocess(f, dir, *v0) workers[pid] = WorkerData(v0) From 23c9a11c9adc9e6d4cec001b3c37c3043cc3fb24 Mon Sep 17 00:00:00 2001 From: mklss <59539887+mklss@users.noreply.github.com> Date: Sun, 9 Feb 2025 00:15:38 +0100 Subject: [PATCH 270/507] Split line. --- src/sage/parallel/use_fork.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index f83ebbc44e6..b3bca5ad5b6 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -70,7 +70,8 @@ class p_iter_fork: about what the iterator does (e.g., killing subprocesses) - ``reset_interfaces`` -- boolean (default: ``True``); whether to reset all pexpect interfaces - - ``reseed_rng`` -- boolean (default: ``False``); whether or not to reseed the rng in the subprocesses + - ``reseed_rng`` -- boolean (default: ``False``); whether or not to reseed + the rng in the subprocesses EXAMPLES:: From 3ace18782d748cd0dc054574b0c9871f551a47e0 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Sat, 8 Feb 2025 16:24:48 -0700 Subject: [PATCH 271/507] Revert "Possibly fix error in random seeded test in src/sage/rings/tests.py. Might be another error in the tests the is" This reverts commit 65c54ced6720ef81e5f84fe0cbb0c058e3b38283. --- src/sage/rings/tests.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index 2e5594672a2..09e0d585078 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -444,16 +444,16 @@ def test_karatsuba_multiplication(base_ring, maxdeg1, maxdeg2, sage: from sage.rings.tests import test_karatsuba_multiplication sage: test_karatsuba_multiplication(ZZ, 6, 5, verbose=True, seed=42) test_karatsuba_multiplication: ring=Univariate Polynomial Ring in x over Integer Ring, threshold=2 - (2*x^6 - x^5 - x^4 - 3*x^3 + 4*x^2 + 4*x + 1)*(4*x^4 + x^3 - 2*x^2 - 20*x + 3) - (16*x^2)*(-41*x + 1) - (x^6 + 2*x^5 + 8*x^4 - x^3 + x^2 + x)*(-x^2 - 4*x + 3) - (-x^3 - x - 8)*(-1) - (x - 1)*(-x^5 + 3*x^4 - x^3 + 2*x + 1) - (x^3 + x^2 + x + 1)*(4*x^3 + 76*x^2 - x - 1) - (x^6 - 5*x^4 - x^3 + 6*x^2 + 1)*(5*x^2 - x + 4) - (3*x - 2)*(x - 1) - (21)*(14*x^5 - x^2 + 4*x + 1) - (12*x^5 - 12*x^2 + 2*x + 1)*(26*x^4 + x^3 + 1) + (x^6 + 4*x^5 + 4*x^4 - 3*x^3 - x^2 - x)*(2*x^4 + 3*x^3 - 20*x^2 - 2*x + 1) + (4*x^5 + 16*x^2 + x - 41)*(x^2 + x - 1) + (8*x^2 + 2*x + 1)*(3) + (-4*x - 1)*(-8*x^2 - x) + (-x^6 - x^3 - x^2 + x + 1)*(2*x^3 - x + 3) + (-x^2 + x + 1)*(x^4 + x^3 - x^2 - x + 76) + (4*x^3 + x^2 + 6)*(-x^2 - 5*x) + (x + 4)*(-x + 5) + (-2*x)*(3*x^2 - x) + (x^6 + 21*x^5 + x^4 + 4*x^3 - x^2)*(14*x^4 + x^3 + 2*x^2 - 12*x) Test Karatsuba multiplication of polynomials of small degree over some common rings:: From edc1a122cd2067f4b58f5cb332b4fd69cbf6e0e5 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Sat, 8 Feb 2025 17:00:23 -0700 Subject: [PATCH 272/507] Fixed errors in tests found in src/sage/rings/tests.py. --- src/sage/rings/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/tests.py b/src/sage/rings/tests.py index 09e0d585078..43eea1bd452 100644 --- a/src/sage/rings/tests.py +++ b/src/sage/rings/tests.py @@ -359,7 +359,7 @@ def test_random_elements(level=MAX_LEVEL, trials=1): ---- survived 1 tests Number Field in a with defining polynomial x^2 - 61891 with a = 248.7790184079036? - -12 + -6 ---- sage: # needs sage.rings.finite_rings sage.rings.number_field sage.rings.padics @@ -409,8 +409,8 @@ def test_random_arith(level=MAX_LEVEL, trials=1): 49/95 survived 1 tests Number Field in a with defining polynomial x^2 - 15083 with a = 122.81286577553673? - a -2*a - 1 - 2*a - 30164 + a -a - 1/2 + 3/2*a - 30163/2 sage: sage.rings.tests.test_random_arith(trials=10) survived 0 tests... sage: sage.rings.tests.test_random_arith(trials=1000) # long time (5 seconds?) From ada208e705d660f62ad218d2504a625808ddd162 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Sat, 8 Feb 2025 17:24:27 -0700 Subject: [PATCH 273/507] Fixed crash when exp(0) of p-adic numbers is called --- src/sage/rings/padics/padic_generic_element.pyx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 7a103be85b3..3f77660f4a7 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2959,6 +2959,14 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: x.exp(algorithm='generic') # indirect doctest # needs sage.libs.ntl 1 + w*7 + (4*w + 2)*7^2 + (w + 6)*7^3 + 5*7^4 + O(7^5) + TESTS:: + + Verify that :trac:`38037` is fixed:: + + sage: R. = Zq(9) + sage: exp(R.zero()) + 1 + O(3^20) + AUTHORS: - Genya Zaytman (2007-02-15) @@ -2973,6 +2981,8 @@ cdef class pAdicGenericElement(LocalGenericElement): R=self.parent() p=self.parent().prime() e=self.parent().absolute_e() + if self._is_exact_zero(): + return R.one() x_unit=self.unit_part() p_unit=R(p).unit_part().lift_to_precision() x_val=self.valuation() From 349bc2d69c5ea6549f3245657504a12242e82f92 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Sat, 8 Feb 2025 21:20:40 -0700 Subject: [PATCH 274/507] Fixed issue in list_plot where it assumed data had been enumerated when it might not have been --- src/sage/plot/plot.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index c5ab3b74aed..cfe7779b009 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -3107,6 +3107,22 @@ def list_plot(data, plotjoined=False, **kwargs): 100.0 sage: d['ymin'] 100.0 + + Verify that :trac:`38037` is fixed:: + + sage: list_plot([(0,-1),(1,-2),(2,-3),(3,-4),(4,None)]) + Traceback (most recent call last): + ... + TypeError: unable to coerce to a ComplexNumber: + + + #Non enumerated list example + sage: list_plot([3+I, 4, I, 1+5*i, None, 1+i]) + Graphics object consisting of 1 graphics primitive + + #Enumerated list example + sage: list_plot([4, 3+I, I, 1+5*i, None, 1+i]) + Graphics object consisting of 1 graphics primitive """ from sage.plot.all import point try: @@ -3124,10 +3140,12 @@ def list_plot(data, plotjoined=False, **kwargs): else: list_data = list(data.items()) return list_plot(list_data, plotjoined=plotjoined, **kwargs) + listEnumerated = False try: from sage.rings.real_double import RDF RDF(data[0]) data = list(enumerate(data)) + listEnumerated = True except TypeError: # we can get this TypeError if the element is a list # or tuple or numpy array, or an element of CC, CDF # We also want to avoid doing CC(data[0]) here since it will go @@ -3138,6 +3156,7 @@ def list_plot(data, plotjoined=False, **kwargs): # element of the Symbolic Ring. if isinstance(data[0], Expression): data = list(enumerate(data)) + listEnumerated = True try: if plotjoined: @@ -3150,9 +3169,11 @@ def list_plot(data, plotjoined=False, **kwargs): # point3d() throws an IndexError on the (0,1) before it ever # gets to (1, I). from sage.rings.cc import CC - # if we get here, we already did "list(enumerate(data))", - # so look at z[1] in inner list - data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]] + # It is not guaranteed that we enumerated the data so we have two cases + if listEnumerated: + data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]] + else: + data = [(z.real(), z.imag()) for z in [CC(z) for z in data]] if plotjoined: return line(data, **kwargs) else: From 8e0678d78397d5eefe3134ed2b86c6465de7ecd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 9 Feb 2025 18:17:54 +0100 Subject: [PATCH 275/507] fix one typo --- src/sage/graphs/cographs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index a78e8a24376..30569f63130 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -395,7 +395,7 @@ def change_label(tree, status, counter): - ``tree`` -- the tree to relabel - - ``status`` -- boolean; used to to detect series (``True``) and parallel + - ``status`` -- boolean; used to detect series (``True``) and parallel (``False``) internal nodes - ``counter`` -- list; the first integer of the list is used to assign a From d9d5cc62fe2993d72a4f894cc00889dff78815cb Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sun, 9 Feb 2025 13:52:48 -0700 Subject: [PATCH 276/507] Added reference to index.rst --- src/doc/en/reference/references/index.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 31cb3ec8074..207e588b1ea 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3903,6 +3903,11 @@ REFERENCES: Algebra i Analiz, 1995, Volume 7, Issue 1, pp. 92--152. http://math.uoregon.edu/~arkadiy/bk1.pdf +.. [KD2015] Donald Keedwell and József Dénes, Latin Squares and their + Applications (2nd ed.), Elsevier B.V., Amsterdam-Netherlands, + 2015. ISBN 978-0-444-63555-6, :mathscinet:`MR3495977`, + :doi:`10.1016/C2014-0-03412-0` + .. [Ke1991] \A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. BI-Wissenschaftsverlag, Mannheim, 1991. From c48aa1e4c94344635d65bbe011fd12c57a6f53ad Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sun, 9 Feb 2025 13:55:30 -0700 Subject: [PATCH 277/507] Added source for construction --- src/sage/combinat/designs/latin_squares.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 0b63649c5bd..7ed07ac222b 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -368,6 +368,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): matrices = construction()[:k] + # Implements the construction from Theorem 5.2.4 of [KD2015]_. elif is_prime_power(n): F = list(GF(n)) From 2fa785e5bcf7d514296b4646c35f64e831b7c189 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sun, 9 Feb 2025 14:03:44 -0700 Subject: [PATCH 278/507] Added Docted, fixed failing doctests --- src/sage/combinat/designs/latin_squares.py | 55 ++++++++++++++-------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 7ed07ac222b..631f19e9dda 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -241,22 +241,22 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): sage: designs.mutually_orthogonal_latin_squares(4,5) # needs sage.schemes [ - [0 2 4 1 3] [0 3 1 4 2] [0 4 3 2 1] [0 1 2 3 4] - [4 1 3 0 2] [3 1 4 2 0] [2 1 0 4 3] [4 0 1 2 3] - [3 0 2 4 1] [1 4 2 0 3] [4 3 2 1 0] [3 4 0 1 2] - [2 4 1 3 0] [4 2 0 3 1] [1 0 4 3 2] [2 3 4 0 1] - [1 3 0 2 4], [2 0 3 1 4], [3 2 1 0 4], [1 2 3 4 0] + [0 1 2 3 4] [0 1 2 3 4] [0 1 2 3 4] [0 1 2 3 4] + [1 2 3 4 0] [2 3 4 0 1] [3 4 0 1 2] [4 0 1 2 3] + [2 3 4 0 1] [4 0 1 2 3] [1 2 3 4 0] [3 4 0 1 2] + [3 4 0 1 2] [1 2 3 4 0] [4 0 1 2 3] [2 3 4 0 1] + [4 0 1 2 3], [3 4 0 1 2], [2 3 4 0 1], [1 2 3 4 0] ] sage: designs.mutually_orthogonal_latin_squares(3,7) # needs sage.schemes [ - [0 2 4 6 1 3 5] [0 3 6 2 5 1 4] [0 4 1 5 2 6 3] - [6 1 3 5 0 2 4] [5 1 4 0 3 6 2] [4 1 5 2 6 3 0] - [5 0 2 4 6 1 3] [3 6 2 5 1 4 0] [1 5 2 6 3 0 4] - [4 6 1 3 5 0 2] [1 4 0 3 6 2 5] [5 2 6 3 0 4 1] - [3 5 0 2 4 6 1] [6 2 5 1 4 0 3] [2 6 3 0 4 1 5] - [2 4 6 1 3 5 0] [4 0 3 6 2 5 1] [6 3 0 4 1 5 2] - [1 3 5 0 2 4 6], [2 5 1 4 0 3 6], [3 0 4 1 5 2 6] + [0 1 2 3 4 5 6] [0 1 2 3 4 5 6] [0 1 2 3 4 5 6] + [1 2 3 4 5 6 0] [2 3 4 5 6 0 1] [3 4 5 6 0 1 2] + [2 3 4 5 6 0 1] [4 5 6 0 1 2 3] [6 0 1 2 3 4 5] + [3 4 5 6 0 1 2] [6 0 1 2 3 4 5] [2 3 4 5 6 0 1] + [4 5 6 0 1 2 3] [1 2 3 4 5 6 0] [5 6 0 1 2 3 4] + [5 6 0 1 2 3 4] [3 4 5 6 0 1 2] [1 2 3 4 5 6 0] + [6 0 1 2 3 4 5], [5 6 0 1 2 3 4], [4 5 6 0 1 2 3] ] sage: designs.mutually_orthogonal_latin_squares(2,5,partitions=True) # needs sage.schemes @@ -270,16 +270,16 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]], - [[0, 8, 11, 19, 22], - [3, 6, 14, 17, 20], - [1, 9, 12, 15, 23], - [4, 7, 10, 18, 21], - [2, 5, 13, 16, 24]], [[0, 9, 13, 17, 21], - [2, 6, 10, 19, 23], - [4, 8, 12, 16, 20], [1, 5, 14, 18, 22], - [3, 7, 11, 15, 24]]] + [2, 6, 10, 19, 23], + [3, 7, 11, 15, 24], + [4, 8, 12, 16, 20]], + [[0, 8, 11, 19, 22], + [1, 9, 12, 15, 23], + [2, 5, 13, 16, 24], + [3, 6, 14, 17, 20], + [4, 7, 10, 18, 21]]] What is the maximum number of MOLS of size 8 that Sage knows how to build?:: @@ -343,6 +343,21 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): [3 4 5 6 7 1 2 0 8 9] [5 6 7 1 2 3 4 0 9 8] [4 5 6 7 1 2 3 9 0 8], [7 1 2 3 4 5 6 9 8 0] ] + + Verify the construction from [KD2015]_:: + + sage: designs.mutually_orthogonal_latin_squares(2, 9) + [ + [0 1 2 3 4 5 6 7 8] [0 1 2 3 4 5 6 7 8] + [2 3 6 4 1 8 0 5 7] [3 8 4 7 5 2 1 0 6] + [3 8 4 7 5 2 1 0 6] [4 7 1 5 8 6 3 2 0] + [4 7 1 5 8 6 3 2 0] [5 0 8 2 6 1 7 4 3] + [5 0 8 2 6 1 7 4 3] [6 4 0 1 3 7 2 8 5] + [6 4 0 1 3 7 2 8 5] [7 6 5 0 2 4 8 3 1] + [7 6 5 0 2 4 8 3 1] [8 2 7 6 0 3 5 1 4] + [8 2 7 6 0 3 5 1 4] [1 5 3 8 7 0 4 6 2] + [1 5 3 8 7 0 4 6 2], [2 3 6 4 1 8 0 5 7] + ] """ from sage.combinat.designs.orthogonal_arrays import orthogonal_array from sage.matrix.constructor import Matrix From 6184ac651af340e3afe245e87bf999365bcc0b33 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sun, 9 Feb 2025 17:04:19 -0700 Subject: [PATCH 279/507] Added check parameter to discrete_log, added doctest to verify --- src/sage/groups/generic.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index 5b207f423c6..e28fcd0c9bb 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -686,7 +686,7 @@ def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=No raise ValueError("Pollard rho algorithm failed to find a logarithm") -def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs'): +def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs', check=True): r""" Totally generic discrete log function. @@ -702,6 +702,8 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i - ``op`` -- function of 2 arguments ``x``, ``y``, returning ``x*y`` in the group - ``algorithm`` -- string denoting what algorithm to use for prime-order logarithms: ``'bsgs'``, ``'rho'``, ``'lambda'`` + - ``check`` -- boolean (default: ``True``); whether to check that output is + correct before returning it. ``a`` and ``base`` must be elements of some group with identity given by ``identity``, inverse of ``x`` by ``inverse(x)``, and group @@ -888,6 +890,15 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i ....: else: ....: assert res == sol + Verify that :issue:`38316` is fixed:: + sage: F = GF(5) + sage: base = F(3) + sage: a = F(1) + sage: discrete_log(a, base, bounds=(1,2), operation="*") + Traceback (most recent call last): + ... + ValueError: no discrete log of 2 found to base 3 + AUTHORS: - William Stein and David Joyner (2005-01-05) @@ -897,6 +908,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i from operator import mul, add, pow power = mul if operation in addition_names else pow mult = add if operation in addition_names else mul + originalA = a # Stores the original value of a so we can check the answer if op: mult = op power = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op) @@ -963,7 +975,10 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i break # we have log%running_mod. if we know that log Date: Mon, 10 Feb 2025 09:45:05 +0700 Subject: [PATCH 280/507] Add back is_approximate as deprecated property --- .../schemes/elliptic_curves/period_lattice.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 050243cb45f..d341ab74dba 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1167,6 +1167,38 @@ def curve(self): """ return self.E + @property + def is_approximate(self): + """ + ``self.is_approximate`` is deprecated, use ``not self.curve().is_exact()`` instead. + + TESTS:: + + sage: E = EllipticCurve(ComplexField(100), [I, 3*I+4]) + sage: L = E.period_lattice() + sage: L.is_approximate + doctest:...: DeprecationWarning: The attribute is_approximate for period lattice is deprecated, + use self.curve().is_exact() instead. + See https://github.com/sagemath/sage/issues/39212 for details. + True + sage: L.curve() is E + True + sage: E.is_exact() + False + sage: E = EllipticCurve(QQ, [0, 2]) + sage: L = E.period_lattice() + sage: L.is_approximate + False + sage: L.curve() is E + True + sage: E.is_exact() + True + """ + from sage.misc.superseded import deprecation + deprecation(39212, "The attribute is_approximate for period lattice is " + "deprecated, use self.curve().is_exact() instead.") + return not self._is_exact + def ei(self): r""" Return the x-coordinates of the 2-division points of the elliptic curve associated From 0e54be2b1608f9158c462df48c41c8b0d5651e27 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:07:40 +0700 Subject: [PATCH 281/507] Apply suggestions --- .../rings/laurent_series_ring_element.pyx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 758b7012240..cd33190e935 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -1,6 +1,12 @@ r""" Laurent Series +Laurent series in Sage are represented internally as a power of the variable +times the power series part. If a Laurent series `f` is represented as +`f = t^n \cdot u` where `t` is the variable and `u` has nonzero constant term, +`u` can be accessed through :meth:`valuation_zero_part` and `n` can be accessed +through :meth:`valuation`. + EXAMPLES:: sage: R. = LaurentSeriesRing(GF(7), 't'); R @@ -35,15 +41,6 @@ Saving and loading. sage: loads(K.dumps()) == K # needs sage.rings.real_mpfr True -IMPLEMENTATION: Laurent series in Sage are represented internally -as a power of the variable times the unit part (which need not be a -unit - it's a polynomial with nonzero constant term). The zero -Laurent series has unit part 0. - -For a Laurent series internally represented as `t^n \cdot f` where -`t` is the variable, `f` can be accessed through :meth:`valuation_zero_part` -and `n` can be accessed through :meth:`valuation`. - AUTHORS: - William Stein: original version @@ -93,8 +90,8 @@ cdef class LaurentSeries(AlgebraElement): r""" A Laurent Series. - We consider a Laurent series of the form `t^n \cdot f` where `f` is a - power series. + We consider a Laurent series of the form `f = t^n \cdot u` where `u` is a + power series with nonzero constant term. INPUT: From 3f4805290586b5af86485fa5402528bf72463a4b Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:20:55 +0700 Subject: [PATCH 282/507] Add test for the current situation --- src/sage/rings/fraction_field.py | 58 ++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 4e3e923c263..7b51ad23261 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -653,6 +653,61 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: x = FF(elt) sage: F(x) -1/2/(a^2 + a) + + Conversion from power series to rational function field truncates, but is deprecated:: + + sage: F. = Frac(QQ['x']) + sage: R. = QQ[[]] + sage: f = 1/(x+1) + sage: f.parent() + Power Series Ring in x over Rational Field + sage: F(f) + -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 + + Conversion from Laurent series to rational function field gives an approximation:: + + sage: F. = Frac(QQ['x']) + sage: R. = QQ[[]] + sage: f = Frac(R)(1/(x+1)) + sage: f.parent() + Laurent Series Ring in x over Rational Field + sage: F(f) + Traceback (most recent call last): + ... + TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + sage: f = f.truncate(20); f # infinite precision + 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + sage: f.parent() + Laurent Series Ring in x over Rational Field + sage: F(f) + Traceback (most recent call last): + ... + TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + sage: f = 1/(x*(x+1)) + sage: f.parent() + Laurent Series Ring in x over Rational Field + sage: F(f) + Traceback (most recent call last): + ... + TypeError: cannot convert x^-1 - 1 + x - x^2 + x^3 - x^4 + x^5 - x^6 + x^7 - x^8 + x^9 - x^10 + x^11 - x^12 + x^13 - x^14 + x^15 - x^16 + x^17 - x^18 + O(x^19)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + + :: + + sage: K. = FunctionField(QQ) + sage: R. = QQ[[]] + sage: f = 1/(x+1) + sage: K(f) + -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 + sage: f = Frac(R)(1/(x+1)) + sage: K(f) + Traceback (most recent call last): + ... + TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + sage: f = 1/(x*(x+1)) + sage: K(f) + Traceback (most recent call last): + ... + TypeError: cannot convert x^-1 - 1 + x - x^2 + x^3 - x^4 + x^5 - x^6 + x^7 - x^8 + x^9 - x^10 + x^11 - x^12 + x^13 - x^14 + x^15 - x^16 + x^17 - x^18 + O(x^19)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field """ if isinstance(x, (list, tuple)) and len(x) == 1: x = x[0] @@ -664,8 +719,7 @@ def _element_constructor_(self, x, y=None, coerce=True): return self._element_class(self, x, ring_one, coerce=coerce) except (TypeError, ValueError): pass - y = self._element_class(self, ring_one, ring_one, - coerce=False, reduce=False) + y = self.one() else: if parent(x) is self: y = self(y) From 428abde2ac8ce92a7bb44edcd432ca3e041d17f4 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:58:32 +0700 Subject: [PATCH 283/507] Implement conversion from laurent series to rational function field --- src/sage/rings/fraction_field.py | 65 ++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 7b51ad23261..ba374d69994 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -557,6 +557,30 @@ def is_exact(self): """ return self.ring().is_exact() + def _convert_from_finite_precision_laurent_series(self, x): + """ + Construct an element of this fraction field approximating a Laurent series. + + INPUT: + + - ``x`` -- a Laurent series, must have finite precision + + OUTPUT: Element of ``self`` + + This internal method should not be used directly, use :meth:`__call__` instead, + which will delegates to :meth:`_element_constructor_`. There are some tests there. + + .. NOTE:: + + Uses the algorithm described in ``_. + This may be changed to use Berlekamp--Massey algorithm or something else + to compute Padé approximant in the future. + """ + integral_part, fractional_part = self(x.truncate(1)), x.truncate_neg(1) + if fractional_part.is_zero(): + return integral_part + return integral_part + ~self._convert_from_finite_precision_laurent_series(~fractional_part) + def _element_constructor_(self, x, y=None, coerce=True): """ Construct an element of this fraction field. @@ -662,6 +686,9 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: f.parent() Power Series Ring in x over Rational Field sage: F(f) + doctest:warning... + DeprecationWarning: Conversion from power series to rational function field is deprecated, use .truncate() instead + See https://github.com/sagemath/sage/issues/39485 for details. -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 Conversion from Laurent series to rational function field gives an approximation:: @@ -672,24 +699,18 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: f.parent() Laurent Series Ring in x over Rational Field sage: F(f) - Traceback (most recent call last): - ... - TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + 1/(x + 1) sage: f = f.truncate(20); f # infinite precision 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 sage: f.parent() Laurent Series Ring in x over Rational Field sage: F(f) - Traceback (most recent call last): - ... - TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 sage: f = 1/(x*(x+1)) sage: f.parent() Laurent Series Ring in x over Rational Field sage: F(f) - Traceback (most recent call last): - ... - TypeError: cannot convert x^-1 - 1 + x - x^2 + x^3 - x^4 + x^5 - x^6 + x^7 - x^8 + x^9 - x^10 + x^11 - x^12 + x^13 - x^14 + x^15 - x^16 + x^17 - x^18 + O(x^19)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + 1/(x^2 + x) :: @@ -697,23 +718,37 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: R. = QQ[[]] sage: f = 1/(x+1) sage: K(f) + doctest:warning... + DeprecationWarning: Conversion from power series to rational function field is deprecated, use .truncate() instead + See https://github.com/sagemath/sage/issues/39485 for details. -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 sage: f = Frac(R)(1/(x+1)) sage: K(f) - Traceback (most recent call last): - ... - TypeError: cannot convert 1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 - x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + 1/(x + 1) sage: f = 1/(x*(x+1)) sage: K(f) - Traceback (most recent call last): - ... - TypeError: cannot convert x^-1 - 1 + x - x^2 + x^3 - x^4 + x^5 - x^6 + x^7 - x^8 + x^9 - x^10 + x^11 - x^12 + x^13 - x^14 + x^15 - x^16 + x^17 - x^18 + O(x^19)/1 to an element of Fraction Field of Univariate Polynomial Ring in x over Rational Field + 1/(x^2 + x) """ if isinstance(x, (list, tuple)) and len(x) == 1: x = x[0] if y is None: if parent(x) is self: return x + from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic + if isinstance(self.ring(), PolynomialRing_generic): + from sage.rings.power_series_ring_element import PowerSeries + from sage.rings.laurent_series_ring_element import LaurentSeries + if isinstance(x, PowerSeries): + from sage.misc.superseded import deprecation + deprecation( + 39485, + "Conversion from power series to rational function field is deprecated, use .truncate() instead", + ) + if isinstance(x, LaurentSeries): + from sage.rings.infinity import infinity + if x.prec() == infinity: + return self(x.laurent_polynomial()) + return self._convert_from_finite_precision_laurent_series(x) ring_one = self.ring().one() try: return self._element_class(self, x, ring_one, coerce=coerce) From d8dfdcb2aa129bd3c061a203cfc69b6679181ea8 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:25:04 +0700 Subject: [PATCH 284/507] Finish changing Rational's round method default rounding to even --- .../number_field_element_quadratic.pyx | 10 ++++------ src/sage/rings/rational.pyx | 18 ++++-------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index cc2e63ec8e7..ac28ba8f504 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -2382,8 +2382,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): TESTS:: - sage: import warnings - sage: warnings.filterwarnings("ignore", category=DeprecationWarning) sage: K2. = QuadraticField(2) sage: K3. = QuadraticField(3) sage: K5. = QuadraticField(5) @@ -2398,15 +2396,15 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): ....: assert round(a+b*sqrt(5.)) == round(a+b*sqrt5), (a, b) """ n = self.floor() - test = 2 * (self - n).abs() + test = 2 * (self - n) if test < 1: return n elif test > 1: return n + 1 - elif self > 0: - return n + 1 - else: + elif n % 2 == 0: return n + else: + return n + 1 cdef class NumberFieldElement_quadratic_sqrt(NumberFieldElement_quadratic): diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 68ee004a251..ccfd4f9ce52 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -3375,11 +3375,9 @@ cdef class Rational(sage.structure.element.FieldElement): mpz_tdiv_q(n.value, mpq_numref(self.value), mpq_denref(self.value)) return n - def round(Rational self, mode=None): + def round(Rational self, mode="even"): """ - Return the nearest integer to ``self``, rounding away by default. - Deprecation: in the future the default will be changed to rounding to - even, for consistency with the builtin Python :func:`round`. + Return the nearest integer to ``self``, rounding to even by default. INPUT: @@ -3399,15 +3397,13 @@ cdef class Rational(sage.structure.element.FieldElement): EXAMPLES:: sage: (9/2).round() - doctest:...: DeprecationWarning: the default rounding for rationals, currently `away`, will be changed to `even`. - See https://github.com/sagemath/sage/issues/35473 for details. - 5 + 4 sage: n = 4/3; n.round() 1 sage: n = -17/4; n.round() -4 sage: n = -5/2; n.round() - -3 + -2 sage: n.round("away") -3 sage: n.round("up") @@ -3419,12 +3415,6 @@ cdef class Rational(sage.structure.element.FieldElement): sage: n.round("odd") -3 """ - if mode is None: - if self.denominator() == 2: - from sage.misc.superseded import deprecation - deprecation(35473, - "the default rounding for rationals, currently `away`, will be changed to `even`.") - mode = "away" if not (mode in ['toward', 'away', 'up', 'down', 'even', 'odd']): raise ValueError("rounding mode must be one of 'toward', 'away', 'up', 'down', 'even', or 'odd'") if self.denominator() == 1: From ca13a623a20790efd62e2a584050ac0e7e29b741 Mon Sep 17 00:00:00 2001 From: Mercedes Haiech Date: Mon, 10 Feb 2025 13:53:30 +0100 Subject: [PATCH 285/507] improve LaTex formula --- src/sage/rings/polynomial/multi_polynomial_sequence.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index ca6f864f807..11224bf0acc 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1271,7 +1271,9 @@ def is_groebner(self, singular=singular): forms a Groebner basis if and only if for every element `S` in `Syz(LM(I))`: - `S * G = \sum_{i=0}^{m} h_ig_i ---->_G 0.` + .. math:: + + S \star G = \sum_{i=0}^{m} h_i g_i \longrightarrow_G 0. EXAMPLES:: From 602b67e1f1dabb253445c9ca7b21267949a0c0aa Mon Sep 17 00:00:00 2001 From: Hugo Passe Date: Mon, 10 Feb 2025 15:01:50 +0100 Subject: [PATCH 286/507] Removed python int to long cast raising error for large integers --- src/sage/matrix/matrix_modn_dense_template.pxi | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 03e60d56394..38836b1113f 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -525,10 +525,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense): se = t x = se.entry v = self._matrix[se.i] - if type(x) is int: - tmp = (x) % p - v[se.j] = tmp + (tmp<0)*p - elif type(x) is IntegerMod_int and (x)._parent is R: + if type(x) is IntegerMod_int and (x)._parent is R: v[se.j] = (x).ivalue elif type(x) is Integer: if coerce: From 287430a897555a205f6256165c54f794b7e8e52e Mon Sep 17 00:00:00 2001 From: MercedesHaiech <73114674+MercedesHaiech@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:10:30 +0100 Subject: [PATCH 287/507] Update src/sage/rings/polynomial/multi_polynomial_sequence.py Co-authored-by: Travis Scrimshaw --- src/sage/rings/polynomial/multi_polynomial_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 11224bf0acc..24acc8e1656 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1271,7 +1271,7 @@ def is_groebner(self, singular=singular): forms a Groebner basis if and only if for every element `S` in `Syz(LM(I))`: - .. math:: + .. MATH:: S \star G = \sum_{i=0}^{m} h_i g_i \longrightarrow_G 0. From d40c2a5cdc658ed97175e8e45a92cee159dbf66b Mon Sep 17 00:00:00 2001 From: Hugo Passe Date: Mon, 10 Feb 2025 15:53:57 +0100 Subject: [PATCH 288/507] Added test for patch of issue 36104 --- src/sage/matrix/matrix_modn_dense_template.pxi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 38836b1113f..dc3a2b651a6 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -514,6 +514,9 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sage: Matrix(Integers(4618990), 2, 2, [-1, int(-2), GF(7)(-3), 1/7]) # needs sage.rings.finite_rings [4618989 4618988] [ 4 2639423] + + sage: Matrix(IntegerModRing(200), [[int(2**128+1), int(2**256+1), int(2**1024+1)]]) # needs sage.rings.finite_rings + [ 57 137 17] """ ma = MatrixArgs_init(parent, entries) cdef long i, j From f7ae0c6788f4e7a12492d1c503a96f49a43a0992 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 10 Feb 2025 19:03:19 +0100 Subject: [PATCH 289/507] Apply suggestions from code review return tuples since the function value is cached. Co-authored-by: Travis Scrimshaw --- src/sage/rings/species.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index 10b4d7a4860..5ae953a32ef 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -2516,9 +2516,8 @@ def _atomic_set_like_species(n, names): INPUT: - - ``n`` -- positive integer, the degree - - ``names`` -- an iterable of strings for the sorts of the - species + - ``n`` -- positive integer, the degree + - ``names`` -- an iterable of strings for the sorts of the species EXAMPLES:: @@ -2533,14 +2532,14 @@ def _atomic_set_like_species(n, names): 0: A007650: Number of set-like atomic species of degree n. sage: _atomic_set_like_species(4, "U, V") - [E_2(E_2(V)), E_2(E_2(U)), E_2(V^2), E_2(U*V), E_2(U^2), E_4(U), E_4(V)] + (E_2(E_2(V)), E_2(E_2(U)), E_2(V^2), E_2(U*V), E_2(U^2), E_4(U), E_4(V)) """ if not n: - return [] + return () M1 = MolecularSpecies("X") M = MolecularSpecies(names) if n == 1: - return [M(SymmetricGroup(1), {s: [1]}) for s in range(M._arity)] + return tuple([M(SymmetricGroup(1), {s: [1]}) for s in range(M._arity)]) result = [] for d in divisors(n): if d == 1: @@ -2561,4 +2560,4 @@ def _atomic_set_like_species(n, names): F = E_d(G) F.support()[0].rename(f"E_{d}({G})") result.append(F) - return result + return tuple(result) From 6e20ba86f4ecc3490db801e3851379af23a33c0a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 10 Feb 2025 19:26:26 +0100 Subject: [PATCH 290/507] adapt a doctest --- src/sage/rings/species.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/species.py b/src/sage/rings/species.py index 5ae953a32ef..c2ae9df66fc 100644 --- a/src/sage/rings/species.py +++ b/src/sage/rings/species.py @@ -2523,7 +2523,7 @@ def _atomic_set_like_species(n, names): sage: from sage.rings.species import _atomic_set_like_species sage: _atomic_set_like_species(6, "X") - [E_2(E_3), E_2(X*E_2), E_2(X^3), E_3(E_2), E_3(X^2), E_6] + (E_2(E_3), E_2(X*E_2), E_2(X^3), E_3(E_2), E_3(X^2), E_6) sage: l = [len(_atomic_set_like_species(n, "X")) for n in range(12)] sage: l From 0a1dd77b31ae8d3ef316404e09a0a015ac7a52ba Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 10 Feb 2025 19:38:15 +0100 Subject: [PATCH 291/507] replace functor with dedicated action (authored by tscrim) --- src/sage/data_structures/stream.py | 101 +++++++++++++++++------------ src/sage/rings/lazy_series_ring.py | 2 +- 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index f69c243c83d..d777d22d12a 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -101,11 +101,8 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import lazy_import from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter -from sage.categories.fields import Fields -from sage.categories.functor import Functor -from sage.categories.integral_domains import IntegralDomains +from sage.categories.action import Action from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis -from sage.categories.pushout import ConstructionFunctor from sage.categories.quotient_fields import QuotientFields from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing @@ -1348,38 +1345,55 @@ def variables(self): return self._pool -class CoefficientRingFunctor(ConstructionFunctor): - r""" - A construction functor for the :class:`CoefficientRing`. - - The functor maps the integral domain of coefficients to the field - of unknown coefficients. +class DominatingAction(Action): """ - rank = 0 - - def __init__(self): - r""" - Initialize the functor. + The action defined by ``G`` acting on ``S`` by any operation such that + the result is either in ``G`` if ``S`` is in the base ring of ``G`` or + ``G`` is the coefficient ring of ``S`` otherwise. - EXAMPLES:: - - sage: from sage.data_structures.stream import CoefficientRingFunctor - sage: CoefficientRingFunctor() - CoefficientRingFunctor + This is meant specifically for use by :class:`CoefficientRing` as part + of the function solver. This is not a mathematically defined action of + ``G`` on ``S`` since the result might not be in ``S``. + """ + def _act_(self, g, x): """ - Functor.__init__(self, IntegralDomains(), Fields()) - - def _apply_functor(self, R): - r""" - Apply the functor to an integral domain. + Return the action of ``g`` on ``x``. EXAMPLES:: - sage: from sage.data_structures.stream import CoefficientRingFunctor - sage: CoefficientRingFunctor()(ZZ) # indirect doctest + sage: from sage.data_structures.stream import CoefficientRing + sage: PF = CoefficientRing(ZZ) + sage: g = PF.gen(0) + sage: x = g - 2; x + FESDUMMY_0 - 2 + sage: x.parent() CoefficientRing over Integer Ring - """ - return CoefficientRing(R) + sage: x = 2 - g; x + -FESDUMMY_0 + 2 + sage: x.parent() + CoefficientRing over Integer Ring + sage: R = QQ['a'] + sage: a = R.gen() + sage: S = ZZ['t']['b'] + sage: b = S.gen() + sage: x = a * g + b; x + FESDUMMY_0*a + b + sage: x.parent() + Univariate Polynomial Ring in a over + Fraction Field of Infinite polynomial ring in FESDUMMY over + Univariate Polynomial Ring in b over Univariate Polynomial Ring in t over Integer Ring + """ + G = g.parent() + if x in G.base_ring(): + if self.is_left(): + return self.operation()(g, G(x)) + return self.operation()(G(x), g) + if x.base_ring() is not G: + x = x.change_ring(G) + g = x.parent()(g) + if self.is_left(): + return self.operation()(g, x) + return self.operation()(x, g) class CoefficientRing(UniqueRepresentation, FractionField_generic): @@ -1473,25 +1487,26 @@ def gen(self, i): """ return self._element_class(self, self._R.gen()[i]) - def construction(self): - r""" - Return a pair ``(F, R)``, where ``F`` is a - :class:`CoefficientRingFunctor` and `R` is an integral - domain, such that ``F(R)`` returns ``self``. + def _get_action_(self, S, op, self_on_left): + """ + Return the left/right action of ``S`` on ``self`` given by ``op``. EXAMPLES:: sage: from sage.data_structures.stream import CoefficientRing + sage: R = ZZ['q'] + sage: S = QQ['t']['a','q'] sage: PF = CoefficientRing(ZZ["q"]) - sage: F, R = PF.construction() - sage: F, R - (CoefficientRingFunctor, - Univariate Polynomial Ring in q over Integer Ring) - - sage: F(R) - CoefficientRing over Univariate Polynomial Ring in q over Integer Ring - """ - return (CoefficientRingFunctor(), self.base_ring()) + sage: PF._get_action_(PF, operator.mul, True) is None + True + sage: type(PF._get_action_(R, operator.add, False)) + + sage: type(PF._get_action_(S, operator.mul, True)) + + """ + if S is not self: + return DominatingAction(self, S, op=op, is_left=self_on_left) + return super()._get_action_(S, op, self_on_left) class Stream_uninitialized(Stream): diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 1b3a87bf828..5f8635d9746 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1012,8 +1012,8 @@ def define_implicitly(self, series, equations, max_lookahead=1): equation 0: coefficient [x*y*t]: A[x*y] - A[t] == 0 equation 1: - coefficient [x*y*t]: B[x*y] - B[t] == 0 coefficient [x*t^2]: B[x*t] + B[t] == 0 + coefficient [x*y*t]: B[x*y] - B[t] == 0 Check the error message in the case of symmetric functions:: From bf658617995c4f39c83da21748f16677355ca105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 10 Feb 2025 22:05:42 +0100 Subject: [PATCH 292/507] fix two oeis related doctests --- src/doc/en/developer/coding_basics.rst | 4 +--- src/sage/combinat/quickref.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 02936d3e423..0ac10e44f12 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -1302,9 +1302,7 @@ framework. Here is a comprehensive list: For lines that require an internet connection:: sage: oeis(60843) # optional - internet - A060843: Busy Beaver problem: a(n) = maximal number of steps that an - n-state Turing machine can make on an initially blank tape before - eventually halting. + A060843: ... - **known bugs:** For lines that describe known bugs, you can use ``# optional - bug``, although ``# known bug`` is preferred. diff --git a/src/sage/combinat/quickref.py b/src/sage/combinat/quickref.py index ee7d9ca8ec1..dacdc266ef1 100644 --- a/src/sage/combinat/quickref.py +++ b/src/sage/combinat/quickref.py @@ -4,8 +4,7 @@ Integer Sequences:: sage: s = oeis([1,3,19,211]); s # optional - internet - 0: A000275: Coefficients of a Bessel function (reciprocal of J_0(z)); - also pairs of permutations with rise/rise forbidden. + 0: A000275: ... sage: s[0].programs() # optional - internet [('maple', ...), ('mathematica', ...), From 2a4c8cd1a125b8aa5f296b54b1012e35b997b061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 10 Feb 2025 22:11:42 +0100 Subject: [PATCH 293/507] fix one more --- src/sage/combinat/species/library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/species/library.py b/src/sage/combinat/species/library.py index c50a9fe75fa..4ab233c3bb3 100644 --- a/src/sage/combinat/species/library.py +++ b/src/sage/combinat/species/library.py @@ -56,7 +56,7 @@ def SimpleGraphSpecies(): sage: seq = S.isotype_generating_series().counts(6)[1:] # needs sage.modules sage: oeis(seq)[0] # optional - internet # needs sage.modules - A000088: Number of graphs on n unlabeled nodes. + A000088: ... :: From f7438fcfc713efb7f7aaf4262a9c092bb2e9bf46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 10 Feb 2025 22:15:14 +0100 Subject: [PATCH 294/507] one more fix --- src/sage/rings/lazy_series.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index b801c6f1b57..b1d370a3bd4 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1482,8 +1482,8 @@ def define(self, s): sage: f 1 + t + t^2 + 2*t^3 + 6*t^4 + 23*t^5 + 104*t^6 + O(t^7) sage: oeis(f[1:20]) # optional - internet - 0: A030266: Shifts left under COMPOSE transform with itself. - 1: A110447: Permutations containing 3241 patterns only as part of 35241 patterns. + 0: A030266: ... + 1: A110447: ... The following can only work for power series, where we have a minimal valuation of `0`:: From 89b74b56322acdec0874390854b014cee26d511c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Mon, 10 Feb 2025 22:41:33 +0100 Subject: [PATCH 295/507] fixing doctests failures in misc/latex*.py --- src/sage/misc/latex.py | 1 - src/sage/misc/latex_standalone.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 9d8fcd4a341..8fc6f04a313 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -1025,7 +1025,6 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, '' sage: latex.eval(r"\ThisIsAnInvalidCommand", {}) # optional -- latex ImageMagick An error occurred... - No pages of output... """ MACROS = latex_extra_preamble() diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index 483e8f52035..0fab052609d 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -676,7 +676,7 @@ def pdf(self, filename=None, view=True, program=None): Traceback (most recent call last): ... CalledProcessError: Command '['...latex', '-interaction=nonstopmode', - 'tikz_...tex']' returned nonzero exit status 1. + 'tikz_...tex']' returned non-zero exit status 1. """ from sage.features.latex import lualatex, pdflatex @@ -797,7 +797,7 @@ def dvi(self, filename=None, view=True, program='latex'): Traceback (most recent call last): ... CalledProcessError: Command '['latex', '-interaction=nonstopmode', - 'tikz_...tex']' returned nonzero exit status 1. + 'tikz_...tex']' returned non-zero exit status 1. We test the behavior when a wrong value is provided:: From 5aeb5c43707d2559ae22b640b6cdaddcdcadd167 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 11 Feb 2025 00:52:23 +0100 Subject: [PATCH 296/507] Extend matrix_ring/division_algebra examples --- src/sage/algebras/quatalg/quaternion_algebra.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index fd011fc358b..aef82c87feb 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -424,13 +424,16 @@ def is_division_algebra(self) -> bool: True sage: QuaternionAlgebra(2,9).is_division_algebra() False + sage: K. = QuadraticField(3) + sage: QuaternionAlgebra(K, 1+z, 3-z).is_division_algebra() + False By checking ramification, the method correctly recognizes division quaternion algebras over a number field even if they have trivial discriminant:: - sage: K = QuadraticField(3) - sage: A = QuaternionAlgebra(K, -1, -1) + sage: L = QuadraticField(5) + sage: A = QuaternionAlgebra(L, -1, -1) sage: A.discriminant() Fractional ideal (1) sage: A.is_division_algebra() @@ -462,13 +465,16 @@ def is_matrix_ring(self) -> bool: False sage: QuaternionAlgebra(2,9).is_matrix_ring() True + sage: K. = QuadraticField(3) + sage: QuaternionAlgebra(K, 1+z, 3-z).is_matrix_ring() + True By checking ramification, the method is able to recognize that quaternion algebras (defined over a number field) with trivial discriminant need not be matrix rings:: - sage: K = QuadraticField(3) - sage: A = QuaternionAlgebra(K, -1, -1) + sage: L = QuadraticField(5) + sage: A = QuaternionAlgebra(L, -1, -1) sage: A.discriminant() Fractional ideal (1) sage: A.is_matrix_ring() From 2c7615f22cb57cdc93d0a503dc3a559676beadd3 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Mon, 10 Feb 2025 20:45:33 -0700 Subject: [PATCH 297/507] Fixed semicolon in test block. --- src/sage/rings/padics/padic_generic_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 3f77660f4a7..da93110fe1b 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2959,7 +2959,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: x.exp(algorithm='generic') # indirect doctest # needs sage.libs.ntl 1 + w*7 + (4*w + 2)*7^2 + (w + 6)*7^3 + 5*7^4 + O(7^5) - TESTS:: + TESTS: Verify that :trac:`38037` is fixed:: From 33316d1ce481bdb5768fd63d7fbc1b51104e4580 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Mon, 10 Feb 2025 21:03:57 -0700 Subject: [PATCH 298/507] Changed listEnumerated to list_enumerated and made comments into seperate tests. --- src/sage/plot/plot.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index cfe7779b009..4c6ad2000b7 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -3108,7 +3108,7 @@ def list_plot(data, plotjoined=False, **kwargs): sage: d['ymin'] 100.0 - Verify that :trac:`38037` is fixed:: + Verify that :issue:`38037` is fixed:: sage: list_plot([(0,-1),(1,-2),(2,-3),(3,-4),(4,None)]) Traceback (most recent call last): @@ -3116,11 +3116,13 @@ def list_plot(data, plotjoined=False, **kwargs): TypeError: unable to coerce to a ComplexNumber: - #Non enumerated list example + Test the codepath where ``list_enumerated`` is ``False``:: + sage: list_plot([3+I, 4, I, 1+5*i, None, 1+i]) Graphics object consisting of 1 graphics primitive - #Enumerated list example + Test the codepath where ``list_enumerated`` is ``True``:: + sage: list_plot([4, 3+I, I, 1+5*i, None, 1+i]) Graphics object consisting of 1 graphics primitive """ @@ -3140,12 +3142,12 @@ def list_plot(data, plotjoined=False, **kwargs): else: list_data = list(data.items()) return list_plot(list_data, plotjoined=plotjoined, **kwargs) - listEnumerated = False + list_enumerated = False try: from sage.rings.real_double import RDF RDF(data[0]) data = list(enumerate(data)) - listEnumerated = True + list_enumerated = True except TypeError: # we can get this TypeError if the element is a list # or tuple or numpy array, or an element of CC, CDF # We also want to avoid doing CC(data[0]) here since it will go @@ -3156,7 +3158,7 @@ def list_plot(data, plotjoined=False, **kwargs): # element of the Symbolic Ring. if isinstance(data[0], Expression): data = list(enumerate(data)) - listEnumerated = True + list_enumerated = True try: if plotjoined: @@ -3170,7 +3172,7 @@ def list_plot(data, plotjoined=False, **kwargs): # gets to (1, I). from sage.rings.cc import CC # It is not guaranteed that we enumerated the data so we have two cases - if listEnumerated: + if list_enumerated: data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]] else: data = [(z.real(), z.imag()) for z in [CC(z) for z in data]] From 145daf914bce7fdc66dcc60dbc3590ad5100aef9 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Tue, 11 Feb 2025 00:25:11 -0700 Subject: [PATCH 299/507] Changed :trac: into :issue:. --- src/sage/rings/padics/padic_generic_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index da93110fe1b..cf233392cd3 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2961,7 +2961,7 @@ cdef class pAdicGenericElement(LocalGenericElement): TESTS: - Verify that :trac:`38037` is fixed:: + Verify that :issue:`38037` is fixed:: sage: R. = Zq(9) sage: exp(R.zero()) From 2eb46acd72ea48a0261abce9c98c54fc2aba8516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Feb 2025 09:54:24 +0100 Subject: [PATCH 300/507] fix documentation (removed files were still listed) --- src/doc/en/reference/groups/index.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index 97a7c126698..930406b336e 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -78,8 +78,6 @@ Matrix and Affine Groups sage/groups/matrix_gps/group_element_gap sage/groups/matrix_gps/finitely_generated sage/groups/matrix_gps/finitely_generated_gap - sage/groups/matrix_gps/morphism - sage/groups/matrix_gps/homset sage/groups/matrix_gps/binary_dihedral sage/groups/matrix_gps/coxeter_group sage/groups/matrix_gps/linear From c08051e98537b810d7ae29af9e85f5a609a837cb Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 11 Feb 2025 10:58:43 +0100 Subject: [PATCH 301/507] #39266: remove some dots in INPUT blocks --- src/sage/graphs/digraph_generators.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 70d8b05ba7d..90809e6c3fe 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -1022,7 +1022,7 @@ def DeBruijn(self, k, n, vertices='strings', immutable=False): (``vertices='string'``) - ``immutable`` -- boolean (default: ``False``); whether to return - an immutable or mutable digraph. + an immutable or mutable digraph EXAMPLES: @@ -1138,10 +1138,10 @@ def GeneralizedDeBruijn(self, n, d, immutable=False, name=None): - ``d`` -- integer; degree of the digraph (must be at least one) - ``immutable`` -- boolean (default: ``False``); whether to return - an immutable or mutable digraph. + an immutable or mutable digraph - ``name`` -- string (default: ``None``); when set, the specified name - is used instead of the default one. + is used instead of the default one .. SEEALSO:: @@ -1207,10 +1207,10 @@ def ImaseItoh(self, n, d, immutable=False, name=None): equal to one) - ``immutable`` -- boolean (default: ``False``); whether to return - an immutable or mutable digraph. + an immutable or mutable digraph - ``name`` -- string (default: ``None``); when set, the specified name - is used instead of the default one. + is used instead of the default one EXAMPLES:: @@ -1292,7 +1292,7 @@ def Kautz(self, k, D, vertices='strings', immutable=False): (``vertices='strings'``) - ``immutable`` -- boolean (default: ``False``); whether to return - an immutable or mutable digraph. + an immutable or mutable digraph EXAMPLES:: From 6531546a3203fd8faf3dbcd2725903adf6fa12f8 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Tue, 11 Feb 2025 12:28:28 -0500 Subject: [PATCH 302/507] First version of deformation cone for pc and polyhedron --- src/doc/en/reference/references/index.rst | 4 + src/sage/geometry/polyhedron/base5.py | 50 ++++++++ .../triangulation/point_configuration.py | 115 +++++++++++++++++- 3 files changed, 167 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4fef159bfd0..e4af2c346e0 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -104,6 +104,10 @@ REFERENCES: graphs and isoperimetric inequalities*, The Annals of Probability 32 (2004), no. 3A, 1727-1745. +.. [ACEP2020] Federico Ardila, Federico Castillo, Christopher Eur, Alexander Postnikov, + *Coxeter submodular functions and deformations of Coxeter permutahedra*, + Advances in Mathematics, Volume 365, 13 May 2020. + .. [ALL2002] P. Auger, G. Labelle and P. Leroux, *Combinatorial addition formulas and applications*, Advances in Applied Mathematics 28 (2002) 302-342. diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index c94efd9428c..d2f174a5975 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -659,6 +659,56 @@ def lawrence_polytope(self): parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim() + n) return parent.element_class(parent, [lambda_V, [], []], None) + def deformation_cone(self): + r""" + Return the deformation cone of ``self``. + + Let `P` be a `d`-polytope in `\RR^r` with `n` facets. The deformation + cone is a polyhedron in `\RR^n` who points are the right-hand side `b` + in `Ax\leq b` where `A` is the matrix of facet normals of ``self``, so + that the resulting polytope has a normal fan which is a coarsening of + the normal fan of ``self``. + + EXAMPLES:: + + sage: tc = Polyhedron([(1, -1), (1/3, 1), (1, 1/3), (-1, 1), (-1, -1)]) + sage: dc = tc.deformation_cone() + sage: dc.an_element() + (2, 1, 1, 0, 0) + sage: [_.A() for _ in tc.Hrepresentation()] + [(1, 0), (0, 1), (0, -1), (-3, -3), (-1, 0)] + sage: P = Polyhedron(rays=[(1, 0, 2), (0, 1, 1), (0, -1, 1), (-3, -3, 0), (-1, 0, 0)]) + sage: P.rays() + (A ray in the direction (-1, -1, 0), + A ray in the direction (-1, 0, 0), + A ray in the direction (0, -1, 1), + A ray in the direction (0, 1, 1), + A ray in the direction (1, 0, 2)) + + .. SEEALSO:: + + :meth:`~sage.schemes.toric.variety.Kaehler_cone` + + REFERENCES: + + For more information, see Section 5.4 of [DLRS2010]_ and Section + 2.2 of [ACEP2020]. + """ + from .constructor import Polyhedron + A = matrix([_.A() for _ in self.Hrepresentation()]) + A = A.transpose() + A_ker = A.right_kernel_matrix(basis='computed') + gale = tuple(A_ker.columns()) + collection = [_.ambient_H_indices() for _ in self.faces(0)] + n = len(gale) + K = None + for cone_indices in collection: + dual_cone = Polyhedron(rays=[gale[i] for i in range(n) if i not in + cone_indices]) + K = K.intersection(dual_cone) if K is not None else dual_cone + preimages = [A_ker.solve_right(r.vector()) for r in K.rays()] + return Polyhedron(lines=A.rows(), rays=preimages) + ########################################################### # Binary operations. ########################################################### diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 88aa25f6c46..5b242bffd67 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -2039,7 +2039,7 @@ def facets_of_simplex(simplex): pushing_triangulation = placing_triangulation @cached_method - def Gale_transform(self, points=None): + def Gale_transform(self, points=None, homogenize=True): r""" Return the Gale transform of ``self``. @@ -2049,6 +2049,9 @@ def Gale_transform(self, points=None): (default). A subset of points for which to compute the Gale transform. By default, all points are used. + - ``homogenize`` -- boolean (default: ``True``); whether to add a row + of 1's before taking the transform. + OUTPUT: a matrix over :meth:`base_ring` EXAMPLES:: @@ -2064,6 +2067,50 @@ def Gale_transform(self, points=None): sage: points = (pc.point(0), pc.point(1), pc.point(3), pc.point(4)) sage: pc.Gale_transform(points) [ 1 -1 1 -1] + + It is possible to take the inverse of the Gale transform, by specifying + whether to homogenize or not:: + + sage: pc2 = PointConfiguration([[0,0],[3,0],[0,3],[3,3],[1,1]]) + sage: pc2.Gale_transform(homogenize=False) + [-1 0 0 0 0] + [ 0 1 1 -1 0] + [ 0 1 1 0 -3] + sage: pc2.Gale_transform(homogenize=True) + [ 1 1 1 0 -3] + [ 0 2 2 -1 -3] + + It might not affect the dimension of the result:: + + sage: PC = PointConfiguration([[4,0,0],[0,4,0],[0,0,4],[2,1,1],[1,2,1],[1,1,2]]) + sage: GT = PC.Gale_transform(homogenize=False);GT + [ 2 1 1 -4 0 0] + [ 1 2 1 0 -4 0] + [-2 -2 -1 3 3 -1] + sage: GT = PC.Gale_transform(homogenize=True);GT + [ 1 0 0 -3 1 1] + [ 0 1 0 1 -3 1] + [ 0 0 1 1 1 -3] + + The following point configuration is totally cyclic (the cone spanned + by the vectors is equal to the vector space spanned by the points), + hence its Gale dual is acyclic (there is a linear functional that is + positive in all the points of the configuration) when not homogenized:: + + sage: pc3 = PointConfiguration([[-1, -1, -1], [-1, 0, 0], [0, -1, 0], [0, 0, -1], [1, 0, 0], [0, 0, 1], [0, 1, 0]]) + sage: g_hom = pc3.Gale_transform(homogenize=True);g_hom + [ 1 0 0 -2 1 -1 1] + [ 0 1 0 -1 1 -1 0] + [ 0 0 1 -1 0 -1 1] + sage: g_inhom = pc3.Gale_transform(homogenize=False);g_inhom + [-1 1 1 1 0 0 0] + [ 0 1 0 0 1 0 0] + [ 1 -1 -1 0 0 1 0] + [ 0 0 1 0 0 0 1] + sage: Polyhedron(rays=g_hom.columns()) + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex and 3 lines + sage: Polyhedron(rays=g_inhom.columns()) + A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 4 rays """ self._assert_is_affine() if points is None: @@ -2073,9 +2120,73 @@ def Gale_transform(self, points=None): points = [ self.point(ZZ(i)) for i in points ] except TypeError: pass - m = matrix([ (1,) + p.affine() for p in points]) + if homogenize: + m = matrix([ (1,) + p.affine() for p in points]) + else: + m = matrix([p.affine() for p in points]) return m.left_kernel().matrix() + def deformation_cone(self, collection): + r""" + Return the deformation cone for the ``collection`` of subconfigurations + of ``self``. + + INPUT: + + - ``collection`` -- a collection of subconfigurations of ``self``. + Subconfigurations are given as indices + + OUTPUT: a polyhedron. It contains the liftings of the point configuration + making the collection a regular (or coherent, or projective) + subdivision. + + EXAMPLES:: + + sage: PC = PointConfiguration([(-1, -1), (-1, 0), (0, -1), (1, 0), (0, 1)]) + sage: coll = [(1, 4), (0, 2), (0, 1), (2, 3), (3, 4)] + sage: dc = PC.deformation_cone(coll);dc + A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 3 rays, 2 lines + sage: dc.rays() + (A ray in the direction (1, 0, 1, 0, 0), + A ray in the direction (1, 1, 0, 0, 0), + A ray in the direction (1, 1, 1, 0, 0)) + sage: dc.lines() + (A line in the direction (1, 0, 1, 0, -1), + A line in the direction (1, 1, 0, -1, 0)) + sage: dc.an_element() + (3, 2, 2, 0, 0) + + We add to the interior element the first line and we verify that the + given rays are defining rays of the lower hull:: + + sage: P = Polyhedron(rays=[(-1, -1, 4), (-1, 0, 3), (0, -1, 2), (1, 0, -1), (0, 1, 0)]) + sage: P.rays() + (A ray in the direction (-1, -1, 4), + A ray in the direction (-1, 0, 3), + A ray in the direction (0, -1, 2), + A ray in the direction (0, 1, 0), + A ray in the direction (1, 0, -1)) + + .. SEEALSO:: + + :meth:`~sage.schemes.toric.variety.Kaehler_cone` + + REFERENCES: + + For more information, see Section 5.4 of [DLRS2010]_ and Section + 2.2 of [ACEP2020]. + """ + from sage.geometry.polyhedron.constructor import Polyhedron + gale = self.Gale_transform(homogenize=False) + dual_rays = gale.columns() + n = self.n_points() + K = None + for cone_indices in collection: + dual_cone = Polyhedron(rays=[dual_rays[i] for i in range(n) if i not in cone_indices]) + K = K.intersection(dual_cone) if K is not None else dual_cone + preimages = [gale.solve_right(r.vector()) for r in K.rays()] + return Polyhedron(lines=matrix(self.points()).transpose().rows(),rays=preimages) + def plot(self, **kwds): r""" Produce a graphical representation of the point configuration. From 12831d8d29c321ecc2202d86ac762bc623522a82 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 11 Feb 2025 18:33:59 +0100 Subject: [PATCH 303/507] provide gens for FiniteField_prime_modn --- .../finite_rings/finite_field_prime_modn.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/finite_rings/finite_field_prime_modn.py b/src/sage/rings/finite_rings/finite_field_prime_modn.py index d94b0a4335a..21e431745ab 100644 --- a/src/sage/rings/finite_rings/finite_field_prime_modn.py +++ b/src/sage/rings/finite_rings/finite_field_prime_modn.py @@ -300,6 +300,30 @@ def gen(self, n=0): self.__gen = self.one() return self.__gen + def gens(self) -> tuple: + r""" + Return a tuple containing the generator of ``self``. + + .. WARNING:: + + The generator is not guaranteed to be a generator for the + multiplicative group. To obtain the latter, use + :meth:`~sage.rings.finite_rings.finite_field_base.FiniteFields.multiplicative_generator()` + or use the ``modulus="primitive"`` option when constructing + the field. + + EXAMPLES:: + + sage: k = GF(1009, modulus='primitive') + sage: k.gens() + (11,) + + sage: k = GF(1009) + sage: k.gens() + (1,) + """ + return (self.gen(),) + def __iter__(self): """ Return an iterator over ``self``. From 7ed18733bc643fa9237135d724d037d3995791cf Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 11 Feb 2025 18:49:08 +0100 Subject: [PATCH 304/507] add link to documentation of Gabidulin codes --- conftest.py | 347 -------------------------- src/doc/en/reference/coding/index.rst | 1 + 2 files changed, 1 insertion(+), 347 deletions(-) delete mode 100644 conftest.py diff --git a/conftest.py b/conftest.py deleted file mode 100644 index 5307d7f6233..00000000000 --- a/conftest.py +++ /dev/null @@ -1,347 +0,0 @@ -# pyright: strict -"""Configuration and fixtures for pytest. - -This file configures pytest and provides some global fixtures. -See https://docs.pytest.org/en/latest/index.html for more details. -""" - -from __future__ import annotations - -import doctest -import inspect -import sys -import warnings -from pathlib import Path -from typing import Any, Iterable, Optional - -import pytest -from _pytest.doctest import ( - DoctestItem, - DoctestModule, - _get_continue_on_failure, - _get_runner, - _is_mocked, - _patch_unwrap_mock_aware, - get_optionflags, -) -from _pytest.pathlib import ImportMode, import_path - -from sage.doctest.forker import ( - init_sage, - showwarning_with_traceback, -) -from sage.doctest.parsing import SageDocTestParser, SageOutputChecker - - -class SageDoctestModule(DoctestModule): - """ - This is essentially a copy of `DoctestModule` from - https://github.com/pytest-dev/pytest/blob/main/src/_pytest/doctest.py. - The only change is that we use `SageDocTestParser` to extract the doctests - and `SageOutputChecker` to verify the output. - """ - - def collect(self) -> Iterable[DoctestItem]: - import doctest - - class MockAwareDocTestFinder(doctest.DocTestFinder): - """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. - https://github.com/pytest-dev/pytest/issues/3456 - https://bugs.python.org/issue25532 - """ - - def __init__(self) -> None: - super().__init__(parser=SageDocTestParser(set(["sage"]))) - - def _find_lineno(self, obj, source_lines): - """Doctest code does not take into account `@property`, this - is a hackish way to fix it. https://bugs.python.org/issue17446 - Wrapped Doctests will need to be unwrapped so the correct - line number is returned. This will be reported upstream. #8796 - """ - if isinstance(obj, property): - obj = getattr(obj, "fget", obj) - - if hasattr(obj, "__wrapped__"): - # Get the main obj in case of it being wrapped - obj = inspect.unwrap(obj) - - # Type ignored because this is a private function. - return super()._find_lineno( # type:ignore[misc] - obj, - source_lines, - ) - - def _find( - self, tests, obj, name, module, source_lines, globs, seen - ) -> None: - if _is_mocked(obj): - return - with _patch_unwrap_mock_aware(): - # Type ignored because this is a private function. - super()._find( # type:ignore[misc] - tests, obj, name, module, source_lines, globs, seen - ) - - if self.path.name == "conftest.py": - module = self.config.pluginmanager._importconftest( - self.path, - self.config.getoption("importmode"), - rootpath=self.config.rootpath, - consider_namespace_packages=True, - ) - else: - try: - module = import_path( - self.path, - mode=ImportMode.importlib, - root=self.config.rootpath, - consider_namespace_packages=True, - ) - except ImportError as exception: - if self.config.getvalue("doctest_ignore_import_errors"): - pytest.skip("unable to import module %r" % self.path) - else: - if isinstance(exception, ModuleNotFoundError): - # Ignore some missing features/modules for now - # TODO: Remove this once all optional things are using Features - if exception.name in ( - "valgrind", - "rpy2", - "sage.libs.coxeter3.coxeter", - ): - pytest.skip( - f"unable to import module { self.path } due to missing feature { exception.name }" - ) - raise - # Uses internal doctest module parsing mechanism. - finder = MockAwareDocTestFinder() - optionflags = get_optionflags(self.config) - from sage.features import FeatureNotPresentError - - runner = _get_runner( - verbose=False, - optionflags=optionflags, - checker=SageOutputChecker(), - continue_on_failure=_get_continue_on_failure(self.config), - ) - try: - for test in finder.find(module, module.__name__): - if test.examples: # skip empty doctests - yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test - ) - except FeatureNotPresentError as exception: - pytest.skip( - f"unable to import module { self.path } due to missing feature { exception.feature.name }" - ) - except ModuleNotFoundError as exception: - # TODO: Remove this once all optional things are using Features - pytest.skip( - f"unable to import module { self.path } due to missing module { exception.name }" - ) - - -class IgnoreCollector(pytest.Collector): - """ - Ignore a file. - """ - - def __init__(self, parent: pytest.Collector) -> None: - super().__init__("ignore", parent) - - def collect(self) -> Iterable[pytest.Item | pytest.Collector]: - return [] - - -def pytest_collect_file( - file_path: Path, parent: pytest.Collector -) -> pytest.Collector | None: - """ - This hook is called when collecting test files, and can be used to - modify the file or test selection logic by returning a list of - ``pytest.Item`` objects which the ``pytest`` command will directly - add to the list of test items. - - See `pytest documentation `_. - """ - if ( - file_path.parent.name == "combinat" - or file_path.parent.parent.name == "combinat" - ): - # Crashes CI for some reason - return IgnoreCollector.from_parent(parent) - if file_path.suffix == ".pyx": - # We don't allow pytests to be defined in Cython files. - # Normally, Cython files are filtered out already by pytest and we only - # hit this here if someone explicitly runs `pytest some_file.pyx`. - return IgnoreCollector.from_parent(parent) - elif file_path.suffix == ".py": - if parent.config.option.doctest: - if file_path.name == "__main__.py" or file_path.name == "setup.py": - # We don't allow tests to be defined in __main__.py/setup.py files (because their import will fail). - return IgnoreCollector.from_parent(parent) - if ( - ( - file_path.name == "postprocess.py" - and file_path.parent.name == "nbconvert" - ) - or ( - file_path.name == "giacpy-mkkeywords.py" - and file_path.parent.name == "autogen" - ) - or ( - file_path.name == "flint_autogen.py" - and file_path.parent.name == "autogen" - ) - ): - # This is an executable file. - return IgnoreCollector.from_parent(parent) - - if file_path.name == "conftest_inputtest.py": - # This is an input file for testing the doctest machinery (and contains broken doctests). - return IgnoreCollector.from_parent(parent) - - if ( - ( - file_path.name == "finite_dimensional_lie_algebras_with_basis.py" - and file_path.parent.name == "categories" - ) - or ( - file_path.name == "__init__.py" - and file_path.parent.name == "crypto" - ) - or (file_path.name == "__init__.py" and file_path.parent.name == "mq") - ): - # TODO: Fix these (import fails with "RuntimeError: dictionary changed size during iteration") - return IgnoreCollector.from_parent(parent) - - if ( - file_path.name in ("forker.py", "reporting.py") - ) and file_path.parent.name == "doctest": - # Fails with many errors due to different testing framework - return IgnoreCollector.from_parent(parent) - - if ( - ( - file_path.name == "arithgroup_generic.py" - and file_path.parent.name == "arithgroup" - ) - or ( - file_path.name == "pari.py" - and file_path.parent.name == "lfunctions" - ) - or ( - file_path.name == "permgroup_named.py" - and file_path.parent.name == "perm_gps" - ) - or ( - file_path.name == "finitely_generated.py" - and file_path.parent.name == "matrix_gps" - ) - or ( - file_path.name == "libgap_mixin.py" - and file_path.parent.name == "groups" - ) - or ( - file_path.name == "finitely_presented.py" - and file_path.parent.name == "groups" - ) - or ( - file_path.name == "classical_geometries.py" - and file_path.parent.name == "generators" - ) - ): - # Fails with "Fatal Python error" - return IgnoreCollector.from_parent(parent) - - return SageDoctestModule.from_parent(parent, path=file_path) - - -def pytest_addoption(parser): - # Add a command line option to run doctests - # (we don't use the built-in --doctest-modules option because then doctests are collected twice) - group = parser.getgroup("collect") - group.addoption( - "--doctest", - action="store_true", - default=False, - help="Run doctests in all .py modules", - dest="doctest", - ) - - -# Monkey patch exception printing to replace the full qualified name of the exception by its short name -# TODO: Remove this hack once migration to pytest is complete -import traceback - -old_format_exception_only = traceback.format_exception_only - - -def format_exception_only(etype: type, value: BaseException) -> list[str]: - formatted_exception = old_format_exception_only(etype, value) - exception_name = etype.__name__ - if etype.__module__: - exception_full_name = etype.__module__ + "." + etype.__qualname__ - else: - exception_full_name = etype.__qualname__ - - for i, line in enumerate(formatted_exception): - if line.startswith(exception_full_name): - formatted_exception[i] = line.replace( - exception_full_name, exception_name, 1 - ) - return formatted_exception - - -# Initialize Sage-specific doctest stuff -init_sage() - -# Monkey patch doctest to use our custom printer etc -old_run = doctest.DocTestRunner.run - - -def doctest_run( - self: doctest.DocTestRunner, - test: doctest.DocTest, - compileflags: Optional[int] = None, - out: Any = None, - clear_globs: bool = True, -) -> doctest.TestResults: - from sage.repl.rich_output import get_display_manager - from sage.repl.user_globals import set_globals - - traceback.format_exception_only = format_exception_only - - # Display warnings in doctests - warnings.showwarning = showwarning_with_traceback - setattr(sys, "__displayhook__", get_display_manager().displayhook) - - # Ensure that injecting globals works as expected in doctests - set_globals(test.globs) - return old_run(self, test, compileflags, out, clear_globs) - - -doctest.DocTestRunner.run = doctest_run - - -@pytest.fixture(autouse=True, scope="session") -def add_imports(doctest_namespace: dict[str, Any]): - """ - Add global imports for doctests. - - See `pytest documentation `. - """ - # Inject sage.all into each doctest - import sage.repl.ipython_kernel.all_jupyter - - dict_all = sage.repl.ipython_kernel.all_jupyter.__dict__ - - # Remove '__package__' item from the globals since it is not - # always in the globals in an actual Sage session. - dict_all.pop("__package__", None) - - sage_namespace = dict(dict_all) - sage_namespace["__name__"] = "__main__" - - doctest_namespace.update(**sage_namespace) diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 637d1a2e65f..c2c30ef8338 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -70,6 +70,7 @@ computations for structural invariants are available. sage/coding/goppa_code sage/coding/kasami_codes sage/coding/ag_code + sage/coding/gabidulin_code .. toctree:: :hidden: From fcf880214559e9f640f85b0bfdb412cb6bf75dc1 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 11 Feb 2025 18:52:24 +0100 Subject: [PATCH 305/507] add again conftest.py (which was gone) --- conftest.py | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000000..5307d7f6233 --- /dev/null +++ b/conftest.py @@ -0,0 +1,347 @@ +# pyright: strict +"""Configuration and fixtures for pytest. + +This file configures pytest and provides some global fixtures. +See https://docs.pytest.org/en/latest/index.html for more details. +""" + +from __future__ import annotations + +import doctest +import inspect +import sys +import warnings +from pathlib import Path +from typing import Any, Iterable, Optional + +import pytest +from _pytest.doctest import ( + DoctestItem, + DoctestModule, + _get_continue_on_failure, + _get_runner, + _is_mocked, + _patch_unwrap_mock_aware, + get_optionflags, +) +from _pytest.pathlib import ImportMode, import_path + +from sage.doctest.forker import ( + init_sage, + showwarning_with_traceback, +) +from sage.doctest.parsing import SageDocTestParser, SageOutputChecker + + +class SageDoctestModule(DoctestModule): + """ + This is essentially a copy of `DoctestModule` from + https://github.com/pytest-dev/pytest/blob/main/src/_pytest/doctest.py. + The only change is that we use `SageDocTestParser` to extract the doctests + and `SageOutputChecker` to verify the output. + """ + + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. + https://github.com/pytest-dev/pytest/issues/3456 + https://bugs.python.org/issue25532 + """ + + def __init__(self) -> None: + super().__init__(parser=SageDocTestParser(set(["sage"]))) + + def _find_lineno(self, obj, source_lines): + """Doctest code does not take into account `@property`, this + is a hackish way to fix it. https://bugs.python.org/issue17446 + Wrapped Doctests will need to be unwrapped so the correct + line number is returned. This will be reported upstream. #8796 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + + if hasattr(obj, "__wrapped__"): + # Get the main obj in case of it being wrapped + obj = inspect.unwrap(obj) + + # Type ignored because this is a private function. + return super()._find_lineno( # type:ignore[misc] + obj, + source_lines, + ) + + def _find( + self, tests, obj, name, module, source_lines, globs, seen + ) -> None: + if _is_mocked(obj): + return + with _patch_unwrap_mock_aware(): + # Type ignored because this is a private function. + super()._find( # type:ignore[misc] + tests, obj, name, module, source_lines, globs, seen + ) + + if self.path.name == "conftest.py": + module = self.config.pluginmanager._importconftest( + self.path, + self.config.getoption("importmode"), + rootpath=self.config.rootpath, + consider_namespace_packages=True, + ) + else: + try: + module = import_path( + self.path, + mode=ImportMode.importlib, + root=self.config.rootpath, + consider_namespace_packages=True, + ) + except ImportError as exception: + if self.config.getvalue("doctest_ignore_import_errors"): + pytest.skip("unable to import module %r" % self.path) + else: + if isinstance(exception, ModuleNotFoundError): + # Ignore some missing features/modules for now + # TODO: Remove this once all optional things are using Features + if exception.name in ( + "valgrind", + "rpy2", + "sage.libs.coxeter3.coxeter", + ): + pytest.skip( + f"unable to import module { self.path } due to missing feature { exception.name }" + ) + raise + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self.config) + from sage.features import FeatureNotPresentError + + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=SageOutputChecker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + try: + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + except FeatureNotPresentError as exception: + pytest.skip( + f"unable to import module { self.path } due to missing feature { exception.feature.name }" + ) + except ModuleNotFoundError as exception: + # TODO: Remove this once all optional things are using Features + pytest.skip( + f"unable to import module { self.path } due to missing module { exception.name }" + ) + + +class IgnoreCollector(pytest.Collector): + """ + Ignore a file. + """ + + def __init__(self, parent: pytest.Collector) -> None: + super().__init__("ignore", parent) + + def collect(self) -> Iterable[pytest.Item | pytest.Collector]: + return [] + + +def pytest_collect_file( + file_path: Path, parent: pytest.Collector +) -> pytest.Collector | None: + """ + This hook is called when collecting test files, and can be used to + modify the file or test selection logic by returning a list of + ``pytest.Item`` objects which the ``pytest`` command will directly + add to the list of test items. + + See `pytest documentation `_. + """ + if ( + file_path.parent.name == "combinat" + or file_path.parent.parent.name == "combinat" + ): + # Crashes CI for some reason + return IgnoreCollector.from_parent(parent) + if file_path.suffix == ".pyx": + # We don't allow pytests to be defined in Cython files. + # Normally, Cython files are filtered out already by pytest and we only + # hit this here if someone explicitly runs `pytest some_file.pyx`. + return IgnoreCollector.from_parent(parent) + elif file_path.suffix == ".py": + if parent.config.option.doctest: + if file_path.name == "__main__.py" or file_path.name == "setup.py": + # We don't allow tests to be defined in __main__.py/setup.py files (because their import will fail). + return IgnoreCollector.from_parent(parent) + if ( + ( + file_path.name == "postprocess.py" + and file_path.parent.name == "nbconvert" + ) + or ( + file_path.name == "giacpy-mkkeywords.py" + and file_path.parent.name == "autogen" + ) + or ( + file_path.name == "flint_autogen.py" + and file_path.parent.name == "autogen" + ) + ): + # This is an executable file. + return IgnoreCollector.from_parent(parent) + + if file_path.name == "conftest_inputtest.py": + # This is an input file for testing the doctest machinery (and contains broken doctests). + return IgnoreCollector.from_parent(parent) + + if ( + ( + file_path.name == "finite_dimensional_lie_algebras_with_basis.py" + and file_path.parent.name == "categories" + ) + or ( + file_path.name == "__init__.py" + and file_path.parent.name == "crypto" + ) + or (file_path.name == "__init__.py" and file_path.parent.name == "mq") + ): + # TODO: Fix these (import fails with "RuntimeError: dictionary changed size during iteration") + return IgnoreCollector.from_parent(parent) + + if ( + file_path.name in ("forker.py", "reporting.py") + ) and file_path.parent.name == "doctest": + # Fails with many errors due to different testing framework + return IgnoreCollector.from_parent(parent) + + if ( + ( + file_path.name == "arithgroup_generic.py" + and file_path.parent.name == "arithgroup" + ) + or ( + file_path.name == "pari.py" + and file_path.parent.name == "lfunctions" + ) + or ( + file_path.name == "permgroup_named.py" + and file_path.parent.name == "perm_gps" + ) + or ( + file_path.name == "finitely_generated.py" + and file_path.parent.name == "matrix_gps" + ) + or ( + file_path.name == "libgap_mixin.py" + and file_path.parent.name == "groups" + ) + or ( + file_path.name == "finitely_presented.py" + and file_path.parent.name == "groups" + ) + or ( + file_path.name == "classical_geometries.py" + and file_path.parent.name == "generators" + ) + ): + # Fails with "Fatal Python error" + return IgnoreCollector.from_parent(parent) + + return SageDoctestModule.from_parent(parent, path=file_path) + + +def pytest_addoption(parser): + # Add a command line option to run doctests + # (we don't use the built-in --doctest-modules option because then doctests are collected twice) + group = parser.getgroup("collect") + group.addoption( + "--doctest", + action="store_true", + default=False, + help="Run doctests in all .py modules", + dest="doctest", + ) + + +# Monkey patch exception printing to replace the full qualified name of the exception by its short name +# TODO: Remove this hack once migration to pytest is complete +import traceback + +old_format_exception_only = traceback.format_exception_only + + +def format_exception_only(etype: type, value: BaseException) -> list[str]: + formatted_exception = old_format_exception_only(etype, value) + exception_name = etype.__name__ + if etype.__module__: + exception_full_name = etype.__module__ + "." + etype.__qualname__ + else: + exception_full_name = etype.__qualname__ + + for i, line in enumerate(formatted_exception): + if line.startswith(exception_full_name): + formatted_exception[i] = line.replace( + exception_full_name, exception_name, 1 + ) + return formatted_exception + + +# Initialize Sage-specific doctest stuff +init_sage() + +# Monkey patch doctest to use our custom printer etc +old_run = doctest.DocTestRunner.run + + +def doctest_run( + self: doctest.DocTestRunner, + test: doctest.DocTest, + compileflags: Optional[int] = None, + out: Any = None, + clear_globs: bool = True, +) -> doctest.TestResults: + from sage.repl.rich_output import get_display_manager + from sage.repl.user_globals import set_globals + + traceback.format_exception_only = format_exception_only + + # Display warnings in doctests + warnings.showwarning = showwarning_with_traceback + setattr(sys, "__displayhook__", get_display_manager().displayhook) + + # Ensure that injecting globals works as expected in doctests + set_globals(test.globs) + return old_run(self, test, compileflags, out, clear_globs) + + +doctest.DocTestRunner.run = doctest_run + + +@pytest.fixture(autouse=True, scope="session") +def add_imports(doctest_namespace: dict[str, Any]): + """ + Add global imports for doctests. + + See `pytest documentation `. + """ + # Inject sage.all into each doctest + import sage.repl.ipython_kernel.all_jupyter + + dict_all = sage.repl.ipython_kernel.all_jupyter.__dict__ + + # Remove '__package__' item from the globals since it is not + # always in the globals in an actual Sage session. + dict_all.pop("__package__", None) + + sage_namespace = dict(dict_all) + sage_namespace["__name__"] = "__main__" + + doctest_namespace.update(**sage_namespace) From 1d9d49050ccc353a9a3610914da6031ae01d218b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Feb 2025 19:07:24 +0100 Subject: [PATCH 306/507] moving random_element to category of rings --- src/sage/categories/rings.py | 25 +++++++++++++++++++++++++ src/sage/rings/ring.pyx | 31 ------------------------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index e0769336c6f..46fc677c840 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -16,6 +16,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport +from sage.misc.prandom import randint from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.rngs import Rngs from sage.structure.element import Element @@ -1646,6 +1647,30 @@ def _random_nonzero_element(self, *args, **kwds): if not x.is_zero(): return x + def random_element(self, bound=2): + """ + Return a random integer coerced into this ring. + + The integer is chosen uniformly + from the interval ``[-bound,bound]``. + + INPUT: + + - ``bound`` -- integer (default: 2) + + ALGORITHM: + + This uses Python's ``randint``. + + EXAMPLES:: + + sage: ZZ.random_element(8) # random + 1 + sage: QQ.random_element(8) # random + 2 + """ + return randint(-bound, bound) * self.one() + class ElementMethods: def is_unit(self) -> bool: r""" diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 6f41d0cbb7a..e54546587e4 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -114,7 +114,6 @@ from sage.misc.superseded import deprecation from sage.structure.coerce cimport coercion_model from sage.structure.parent cimport Parent from sage.structure.category_object cimport check_default_category -from sage.misc.prandom import randint from sage.categories.rings import Rings from sage.categories.algebras import Algebras from sage.categories.commutative_algebras import CommutativeAlgebras @@ -637,36 +636,6 @@ cdef class Ring(ParentWithGens): """ return self.zeta().multiplicative_order() - def random_element(self, bound=2): - """ - Return a random integer coerced into this ring, where the - integer is chosen uniformly from the interval ``[-bound,bound]``. - - INPUT: - - - ``bound`` -- integer (default: 2) - - ALGORITHM: - - Uses Python's randint. - - TESTS: - - The following example returns a :exc:`NotImplementedError` since the - generic ring class ``__call__`` function returns a - :exc:`NotImplementedError`. Note that - ``sage.rings.ring.Ring.random_element`` performs a call in the generic - ring class by a random integer:: - - sage: R = sage.rings.ring.Ring(ZZ); R - - sage: R.random_element() - Traceback (most recent call last): - ... - NotImplementedError: cannot construct elements of - """ - return self(randint(-bound,bound)) - @cached_method def epsilon(self): """ From 97f4d34c1413cfd17280569e15cc147b456c900d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 11 Feb 2025 19:24:04 +0100 Subject: [PATCH 307/507] add docstring for gens --- src/sage/rings/quotient_ring.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index ddac8d5b248..159674aec69 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -1248,6 +1248,16 @@ def gen(self, i=0): return self(self.__R.gen(i)) def gens(self) -> tuple: + r""" + Return a tuple containing generators of ``self``. + + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: S = R.quotient_ring(x^2 + y^2) + sage: S.gens() + (xbar, ybar) + """ return tuple(self(self.__R.gen(i)) for i in range(self.cover_ring().ngens())) From bc23f92c49ed799c45c58057686b5a4eab3e4309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Feb 2025 21:23:06 +0100 Subject: [PATCH 308/507] remove from meson build --- src/sage/groups/matrix_gps/meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/groups/matrix_gps/meson.build b/src/sage/groups/matrix_gps/meson.build index 77c70adf7fa..e9cd43cf946 100644 --- a/src/sage/groups/matrix_gps/meson.build +++ b/src/sage/groups/matrix_gps/meson.build @@ -9,13 +9,11 @@ py.install_sources( 'group_element.pxd', 'group_element_gap.pxd', 'heisenberg.py', - 'homset.py', 'isometries.py', 'linear.py', 'linear_gap.py', 'matrix_group.py', 'matrix_group_gap.py', - 'morphism.py', 'named_group.py', 'named_group_gap.py', 'orthogonal.py', From 1fa959b45b95f11465b73ffbc33b934628724587 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 11 Feb 2025 22:04:30 +0100 Subject: [PATCH 309/507] Update src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../drinfeld_modules/charzero_drinfeld_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 27a4373bb9a..e9de0b8b1f6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -162,7 +162,8 @@ def exponential(self, prec=Infinity, name='z'): - ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``); the precision at which the series is returned; if ``Infinity``, - a lazy power series in returned + a lazy power series in returned, else, a classical power series + is returned. - ``name`` -- string (default: ``'z'``); the name of the generator of the lazy power series ring From b27a7baf12914171682aa7f646af3b0758fb5f11 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Tue, 11 Feb 2025 16:29:41 -0500 Subject: [PATCH 310/507] Added the missing methods --- src/sage/geometry/fan.py | 52 +++++++++++++++++++ src/sage/geometry/polyhedron/base5.py | 20 ++++++- .../triangulation/point_configuration.py | 51 +++++++++++++++++- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 86cd4c83c65..7e6b482aa1e 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2439,6 +2439,58 @@ def Gale_transform(self): m = m.augment(matrix(ZZ, m.nrows(), 1, [1] * m.nrows())) return matrix(ZZ, m.integer_kernel().matrix()) + def is_regular(self): + r""" + Check if ``self`` is regular. + + A rational polyhedral fan is *regular* if it is the normal fan of a + polytope. + + OUTPUT: ``True`` if ``self`` is complete and ``False`` otherwise + + EXAMPLES: + + This is the mother of all examples, which is not regular (see Section + 7.1.1 in [DLRS2010]_):: + + sage: epsilon = 0 + sage: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)] + sage: S1 = [Cone([rays[i] for i in indices]) for indices in [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)]] + sage: mother = Fan(S1) + sage: mother.is_regular() + False + + Doing a slight perturbation makes the same subdivision regular:: + + sage: epsilon = 1/2 + sage: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)] + sage: S1 = [Cone([rays[i] for i in indices]) for indices in [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)]] + sage: mother = Fan(S1) + sage: mother.is_regular() + True + + .. SEEALSO:: + + :meth:`is_projective`. + """ + if not self.is_complete(): + raise ValueError('the fan is not complete') + from sage.geometry.triangulation.point_configuration import PointConfiguration + from sage.geometry.polyhedron.constructor import Polyhedron + pc = PointConfiguration(self.rays()) + v_pc = [vector(pc.point(i)) for i in range(pc.n_points())] + v_r = [vector(list(r)) for r in self.rays()] + cone_indices = [_.ambient_ray_indices() for _ in self.generating_cones()] + translator = [v_pc.index(v_r[i]) for i in range(pc.n_points())] + translated_cone_indices = [[translator[i] for i in ci] for ci in cone_indices] + dc_pc = pc.deformation_cone(translated_cone_indices) + lift = dc_pc.an_element() + ieqs = [[lift[i]] + list(v_pc[i]) for i in range(self.nrays())] + poly = Polyhedron(ieqs=ieqs) + return self.is_equivalent(poly.normal_fan()) + + is_projective = is_regular + def generating_cone(self, n): r""" Return the ``n``-th generating cone of ``self``. diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index d2f174a5975..a7a7ab4cfb4 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -669,7 +669,10 @@ def deformation_cone(self): that the resulting polytope has a normal fan which is a coarsening of the normal fan of ``self``. - EXAMPLES:: + EXAMPLES: + + Let's examine the deformation cone of the square with one truncated + vertex:: sage: tc = Polyhedron([(1, -1), (1/3, 1), (1, 1/3), (-1, 1), (-1, -1)]) sage: dc = tc.deformation_cone() @@ -685,6 +688,19 @@ def deformation_cone(self): A ray in the direction (0, 1, 1), A ray in the direction (1, 0, 2)) + Now, let's compute the deformation cone of the pyramid over a square + and verify that it is not full dimensional:: + + sage: py = Polyhedron([(0, -1, -1), (0, -1, 1), (0, 1, -1), (0, 1, 1), (1, 0, 0)]) + sage: dc_py = py.deformation_cone(); dc_py + A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 1 ray, 3 lines + sage: [_.b() for _ in py.Hrepresentation()] + [0, 1, 1, 1, 1] + sage: r = dc_py.rays()[0] + sage: l1,l2,l3 = dc_py.lines() + sage: r.vector()-l1.vector()/2-l2.vector()-l3.vector()/2 + (0, 1, 1, 1, 1) + .. SEEALSO:: :meth:`~sage.schemes.toric.variety.Kaehler_cone` @@ -699,7 +715,7 @@ def deformation_cone(self): A = A.transpose() A_ker = A.right_kernel_matrix(basis='computed') gale = tuple(A_ker.columns()) - collection = [_.ambient_H_indices() for _ in self.faces(0)] + collection = [f.ambient_H_indices() for f in self.faces(0)] n = len(gale) K = None for cone_indices in collection: diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 5b242bffd67..e6ac31dc97b 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -2121,7 +2121,7 @@ def Gale_transform(self, points=None, homogenize=True): except TypeError: pass if homogenize: - m = matrix([ (1,) + p.affine() for p in points]) + m = matrix([(1,) + p.affine() for p in points]) else: m = matrix([p.affine() for p in points]) return m.left_kernel().matrix() @@ -2167,6 +2167,55 @@ def deformation_cone(self, collection): A ray in the direction (0, 1, 0), A ray in the direction (1, 0, -1)) + Let's verify the mother of all examples explained in Section 7.1.1 of + [DLRS2010]_:: + + sage: epsilon = 0 + sage: mother = PointConfiguration([(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2)]) + sage: mother.points() + (P(4, 0, 0), P(0, 4, 0), P(0, 0, 4), P(2, 1, 1), P(1, 2, 1), P(1, 1, 2)) + sage: S1 = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5)] + sage: S2 = [(0,1,3),(1,3,4),(1,2,4),(2,4,5),(0,2,5),(0,3,5)] + + Both subdivisions `S1` and `S2` are not regular:: + + sage: mother_dc1 = mother.deformation_cone(S1) + sage: mother_dc1 + A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines + sage: mother_dc2 = mother.deformation_cone(S2) + sage: mother_dc2 + A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines + + Notice that they have a ray which provides a degenerate lifting which + only provides a coarsening of the subdivision from the lower hull (it + has 5 facets, and should have 8):: + + sage: result = Polyhedron([vector(list(mother.points()[_])+[mother_dc1.rays()[0][_]]) for _ in range(len(mother.points()))]) + sage: result.f_vector() + (1, 6, 9, 5, 1) + + But if we use epsilon to perturb the configuration, suddenly + `S1` becomes regular:: + + sage: epsilon = 1/2 + sage: mother = PointConfiguration([(4-epsilon,epsilon,0), + (0,4-epsilon,epsilon), + (epsilon,0,4-epsilon), + (2,1,1), + (1,2,1), + (1,1,2)]) + sage: mother.points() + (P(7/2, 1/2, 0), + P(0, 7/2, 1/2), + P(1/2, 0, 7/2), + P(2, 1, 1), + P(1, 2, 1), + P(1, 1, 2)) + sage: mother_dc1 = mother.deformation_cone(S1);mother_dc1 + A 6-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 3 rays, 3 lines + sage: mother_dc2 = mother.deformation_cone(S2);mother_dc2 + A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex and 3 lines + .. SEEALSO:: :meth:`~sage.schemes.toric.variety.Kaehler_cone` From aeb512f8f52529ccc31d0b4af326dd331488efe8 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 11 Feb 2025 22:47:57 +0100 Subject: [PATCH 311/507] Undo line gathering --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 8a935ad0c09..a865996556e 100755 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2400,9 +2400,9 @@ def _compute_gens(self, proof, sage: E = EllipticCurve([-127^2,0]) sage: P = E.lift_x(611429153205013185025/9492121848205441) - sage: (set(E.gens(use_database=False, algorithm='pari', pari_effort=4)) # long time - ....: <= set([P+T for T in E.torsion_points()] - ....: + [-P+T for T in E.torsion_points()])) + sage: ge = set(E.gens(use_database=False, algorithm='pari',pari_effort=4)) # long time + sage: ge <= set([P+T for T in E.torsion_points()] # long time + ....: + [-P+T for T in E.torsion_points()]) True """ # If the optional extended database is installed and an From ad8ac8660e16d60c7c41332fd67ce8ddba69cfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 11 Feb 2025 23:21:24 +0100 Subject: [PATCH 312/507] Python optimization of the code --- src/sage/geometry/fan.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 7e6b482aa1e..39fd694972b 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2444,7 +2444,7 @@ def is_regular(self): Check if ``self`` is regular. A rational polyhedral fan is *regular* if it is the normal fan of a - polytope. + polytope. OUTPUT: ``True`` if ``self`` is complete and ``False`` otherwise @@ -2478,14 +2478,15 @@ def is_regular(self): from sage.geometry.triangulation.point_configuration import PointConfiguration from sage.geometry.polyhedron.constructor import Polyhedron pc = PointConfiguration(self.rays()) - v_pc = [vector(pc.point(i)) for i in range(pc.n_points())] - v_r = [vector(list(r)) for r in self.rays()] - cone_indices = [_.ambient_ray_indices() for _ in self.generating_cones()] - translator = [v_pc.index(v_r[i]) for i in range(pc.n_points())] + v_pc = [tuple(p) for p in pc] + pc_to_indices = {tuple(p):i for (i,p) in enumerate(pc)} + indices_to_vr = [tuple(r) for r in self.rays()] + cone_indices = [cone.ambient_ray_indices() for cone in self.generating_cones()] + translator = [pc_to_indices[t] for t in indices_to_vr] translated_cone_indices = [[translator[i] for i in ci] for ci in cone_indices] dc_pc = pc.deformation_cone(translated_cone_indices) lift = dc_pc.an_element() - ieqs = [[lift[i]] + list(v_pc[i]) for i in range(self.nrays())] + ieqs = [(lift_i,) + v for (lift_i, v) in zip(lift, v_pc)] poly = Polyhedron(ieqs=ieqs) return self.is_equivalent(poly.normal_fan()) From 949c745c5f658b65f7684895e670e681e2cabc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 11 Feb 2025 23:32:24 +0100 Subject: [PATCH 313/507] creating a def mother(epsilon) in the example section --- src/sage/geometry/fan.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 39fd694972b..09a3d18bc77 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2450,23 +2450,25 @@ def is_regular(self): EXAMPLES: - This is the mother of all examples, which is not regular (see Section - 7.1.1 in [DLRS2010]_):: + This is the mother of all examples (see Section 7.1.1 in + [DLRS2010]_):: + + sage: def mother(epsilon=0): + ....: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)] + ....: L = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)] + ....: S1 = [Cone([rays[i] for i in indices]) for indices in L] + ....: return Fan(S1) + + When epsilon=0, it is not regular:: sage: epsilon = 0 - sage: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)] - sage: S1 = [Cone([rays[i] for i in indices]) for indices in [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)]] - sage: mother = Fan(S1) - sage: mother.is_regular() + sage: mother(epsilon).is_regular() False Doing a slight perturbation makes the same subdivision regular:: sage: epsilon = 1/2 - sage: rays = [(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2),(-1,-1,-1)] - sage: S1 = [Cone([rays[i] for i in indices]) for indices in [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5),(3,4,5),(6,0,1),(6,1,2),(6,2,0)]] - sage: mother = Fan(S1) - sage: mother.is_regular() + sage: mother(epsilon).is_regular() True .. SEEALSO:: From 4b2452404c3259367679129ed70730a3cfd40c1c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:42:39 +0700 Subject: [PATCH 314/507] Apply sort and filter of walk_packages consistently --- src/sage/misc/package_dir.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 6b6ca12803f..d84b896f6e5 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -370,9 +370,9 @@ def iter_modules(path=None, prefix=''): yielded[name] = 1 yield ModuleInfo(i, name, ispkg) - def iter_importer_modules(importer, prefix=''): + def _iter_importer_modules_helper(importer, prefix=''): r""" - Yield :class:`ModuleInfo` for all modules of ``importer``. + Helper function for :func:`iter_importer_modules`. """ from importlib.machinery import FileFinder @@ -391,11 +391,6 @@ def iter_importer_modules(importer, prefix=''): for fn in filenames: modname = inspect.getmodulename(fn) - if modname and (modname in ['__init__', 'all'] - or modname.startswith('all__') - or modname in yielded): - continue - path = os.path.join(importer.path, fn) ispkg = False @@ -414,6 +409,18 @@ def iter_importer_modules(importer, prefix=''): else: yield from importer.iter_modules(prefix) + def iter_importer_modules(importer, prefix=''): + r""" + Yield :class:`ModuleInfo` for all modules of ``importer``. + """ + for name, ispkg in sorted(list(_iter_importer_modules_helper(importer, prefix))): + # we sort again for consistency of output ordering if importer is not + # a FileFinder (needed in doctest of :func:`sage.misc.dev_tools/load_submodules`) + modname = name.rsplit('.', 1)[-1] + if modname in ['__init__', 'all'] or modname.startswith('all__'): + continue + yield name, ispkg + def seen(p, m={}): if p in m: return True From 3dc5ce3e8f52ce688c5f184ba85a3b462c193494 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:59:10 +0700 Subject: [PATCH 315/507] Fix more doctests in meson_editable install --- src/sage/doctest/external.py | 4 +++ src/sage/doctest/sources.py | 10 +++++++ src/sage/env.py | 4 +++ src/sage/features/meson_editable.py | 45 +++++++++++++++++++++++++++++ src/sage/misc/package_dir.py | 36 +++++++++++++++++++++-- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/sage/features/meson_editable.py diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 45b3987de05..2cc374f0763 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -366,6 +366,10 @@ def external_features(): r""" Generate the features that are only to be tested if ``--optional=external`` is used. + .. SEEALSO:: + + :func:`sage.features.all.all_features` + EXAMPLES:: sage: from sage.doctest.external import external_features diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 7589f62922b..01d24d67634 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -87,8 +87,18 @@ def get_basename(path): sage: import os sage: get_basename(sage.doctest.sources.__file__) 'sage.doctest.sources' + + :: + + sage: # optional - !meson_editable sage: get_basename(os.path.join(sage.structure.__path__[0], 'element.pxd')) 'sage.structure.element.pxd' + + TESTS:: + + sage: # optional - meson_editable + sage: get_basename(os.path.join(os.path.dirname(sage.structure.__file__), 'element.pxd')) + 'sage.structure.element.pxd' """ if path is None: return None diff --git a/src/sage/env.py b/src/sage/env.py index 060eb2209a9..826c0343ac8 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -319,6 +319,10 @@ def sage_include_directories(use_sources=False): sage: dirs = sage.env.sage_include_directories(use_sources=True) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True + + :: + + sage: # optional - !meson_editable (no need, see :issue:`39275`) sage: dirs = sage.env.sage_include_directories(use_sources=False) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True diff --git a/src/sage/features/meson_editable.py b/src/sage/features/meson_editable.py new file mode 100644 index 00000000000..a110648b873 --- /dev/null +++ b/src/sage/features/meson_editable.py @@ -0,0 +1,45 @@ +r""" +Feature for testing if Meson editable install is used. +""" + +import sys +from . import Feature, FeatureTestResult + + +class MesonEditable(Feature): + r""" + A :class:`~sage.features.Feature` describing if Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() + Feature('meson_editable') + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() is MesonEditable() + True + """ + Feature.__init__(self, 'meson_editable') + + def _is_present(self): + r""" + Test whether Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable()._is_present() # random + FeatureTestResult('meson_editable', True) + """ + import sage + result = type(sage.__loader__).__module__ == '_sagemath_editable_loader' + return FeatureTestResult(self, result) + + +def all_features(): + return [MesonEditable()] diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 6b6ca12803f..b9742a6d1c2 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -261,6 +261,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.cpython` is an ordinary package:: + sage: # optional - !meson_editable sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir sage: directory = sage.cpython.__path__[0]; directory '.../sage/cpython' @@ -270,24 +271,48 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.libs.mpfr` only has an ``__init__.pxd`` file, but we consider it a package directory for consistency with Cython:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True :mod:`sage` is designated to become an implicit namespace package:: + sage: # optional - !meson_editable sage: directory = sage.__path__[0]; directory '.../sage' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True Not a package:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.symbolic.__path__[0], 'ginac'); directory # needs sage.symbolic '.../sage/symbolic/ginac' sage: is_package_or_sage_namespace_package_dir(directory) # needs sage.symbolic False + + TESTS:: + + sage: # optional - meson_editable + sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir + sage: directory = os.path.dirname(sage.cpython.__file__); directory + '.../sage/cpython' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable + sage: directory = os.path.join(os.path.dirname(sage.libs.__file__), 'mpfr'); directory + '.../sage/libs/mpfr' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable, sage.symbolic + sage: directory = os.path.join(os.path.dirname(sage.symbolic.__file__), 'ginac'); directory + '.../sage/symbolic/ginac' + sage: is_package_or_sage_namespace_package_dir(directory) + False """ if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package return True @@ -345,8 +370,15 @@ def walk_packages(path=None, prefix='', onerror=None): EXAMPLES:: + sage: # optional - !meson_editable sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) # a namespace package [..., ModuleInfo(module_finder=FileFinder('.../sage/misc'), name='package_dir', ispkg=False), ...] + + TESTS:: + + sage: # optional - meson_editable + sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) + [..., ModuleInfo(module_finder=<...MesonpyPathFinder object...>, name='package_dir', ispkg=False), ...] """ # Adapted from https://github.com/python/cpython/blob/3.11/Lib/pkgutil.py From f77df2a9d93c4c69a15c2233fc96ad280e43400b Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:17:42 +0700 Subject: [PATCH 316/507] Improve sage_getfile by looking at __init__ --- src/sage/misc/sageinspect.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 4f7aed2820f..9066671f6f9 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -1328,6 +1328,16 @@ def sage_getfile(obj): if isinstance(obj, functools.partial): return sage_getfile(obj.func) return sage_getfile(obj.__class__) # inspect.getabsfile(obj.__class__) + else: + try: + objinit = obj.__init__ + except AttributeError: + pass + else: + pos = _extract_embedded_position(_sage_getdoc_unformatted(objinit)) + if pos is not None: + (_, filename, _) = pos + return filename # No go? fall back to inspect. try: @@ -1336,6 +1346,10 @@ def sage_getfile(obj): return '' for suffix in import_machinery.EXTENSION_SUFFIXES: if sourcefile.endswith(suffix): + # TODO: the following is incorrect in meson editable install + # but as long as either the class or its __init__ method has a + # docstring, _sage_getdoc_unformatted should return correct result + # see https://github.com/mesonbuild/meson-python/issues/723 return sourcefile.removesuffix(suffix)+os.path.extsep+'pyx' return sourcefile From f2dd36fe94e1782647aa77fb05b2ff89c05d19c8 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 11 Feb 2025 22:50:11 -0700 Subject: [PATCH 317/507] #34821 strings as matrix entries --- src/sage/matrix/args.pyx | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 332eaf82df8..19406c16121 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -1515,12 +1515,19 @@ cdef class MatrixArgs: [() 0 0] [ 0 () 0] [ 0 0 ()] + + Verify that :issue:`34821` is fixed:: + + sage: matrix(ZZ, 2, 2, "3") + [3 0] + [0 3] """ # Check basic Python types. This is very fast, so it doesn't # hurt to do these first. if self.entries is None: return MA_ENTRIES_ZERO - if isinstance(self.entries, (int, float, complex, Integer)): + if isinstance(self.entries, (int, float, complex, Integer, str)): + # Note that a string is not considered to be a sequence. if self.entries: return MA_ENTRIES_SCALAR return MA_ENTRIES_ZERO @@ -1565,9 +1572,6 @@ cdef class MatrixArgs: if isinstance(self.entries, MatrixArgs): # Prevent recursion return MA_ENTRIES_UNKNOWN - if isinstance(self.entries, str): - # Blacklist strings, we don't want them to be considered a sequence - return MA_ENTRIES_UNKNOWN try: self.entries = list(self.entries) except TypeError: @@ -1586,6 +1590,16 @@ cdef class MatrixArgs: is a sequence. If the entries are invalid, return ``MA_ENTRIES_UNKNOWN``. + + TESTS: + + Verify that :issue:`34821` is fixed:: + + sage: matrix(ZZ, 1,2, ["1", "2"]) + [1 2] + sage: matrix(ZZ, 2,1, ["1", "2"]) + [1] + [2] """ if not self.entries: return MA_ENTRIES_SEQ_FLAT @@ -1601,13 +1615,11 @@ cdef class MatrixArgs: return MA_ENTRIES_SEQ_SEQ else: return MA_ENTRIES_SEQ_FLAT - if isinstance(x, (int, float, complex)): + if isinstance(x, (int, float, complex, str)): + # Note that a string is not considered to be a sequence. return MA_ENTRIES_SEQ_FLAT if isinstance(x, Element) and element_is_scalar(x): return MA_ENTRIES_SEQ_FLAT - if isinstance(x, str): - # Blacklist strings, we don't want them to be considered a sequence - return MA_ENTRIES_UNKNOWN try: iter(x) except TypeError: From 0bdb8e299fcbfac76f8dce80ec9d34335a9cfb02 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 11 Feb 2025 23:02:26 -0700 Subject: [PATCH 318/507] fix doctest --- src/sage/matrix/args.pyx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 19406c16121..f7091d1efba 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -297,9 +297,12 @@ cdef class MatrixArgs: Test invalid input:: sage: MatrixArgs(ZZ, 2, 2, entries='abcd').finalized() + + sage: matrix(ZZ, 2, 2, entries='abcd') Traceback (most recent call last): ... - TypeError: unable to convert 'abcd' to a matrix + TypeError: unable to convert 'abcd' to an integer sage: MatrixArgs(ZZ, 2, 2, entries=MatrixArgs()).finalized() Traceback (most recent call last): ... From b6cb2f4bbe85113348fc2899d06cbbd029663967 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:11:10 +0700 Subject: [PATCH 319/507] Workaround for test-new failure --- src/sage/misc/package_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index b9742a6d1c2..e041167e945 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -274,7 +274,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) + sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) True :mod:`sage` is designated to become an implicit namespace package:: From 7002dbdcdd855a3c0e84f982f759d683b2799ed3 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:29:27 +0700 Subject: [PATCH 320/507] Fix links --- src/sage/rings/laurent_series_ring_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index cd33190e935..bdc2cdea53e 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -4,8 +4,8 @@ Laurent Series Laurent series in Sage are represented internally as a power of the variable times the power series part. If a Laurent series `f` is represented as `f = t^n \cdot u` where `t` is the variable and `u` has nonzero constant term, -`u` can be accessed through :meth:`valuation_zero_part` and `n` can be accessed -through :meth:`valuation`. +`u` can be accessed through :meth:`~LaurentSeries.valuation_zero_part` and `n` +can be accessed through :meth:`~LaurentSeries.valuation`. EXAMPLES:: From b20679750d123412e14138723530886719a20c46 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 10:04:35 +0100 Subject: [PATCH 321/507] comment code --- .../charzero_drinfeld_module.py | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index f34b2ebbe8d..32225468aa8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -572,18 +572,28 @@ def class_polynomial(self): ... ValueError: coefficients are not polynomials """ + # The algorithm is based on the following remark: + # writing phi_T = g_0 + g_1*tau + ... + g_r*tau^r, + # if s > deg(g_i/(q^i - 1)) - 1 for all i, then the + # class module is equal to + # H := E(Kinfty/A) / < T^(-s), T^(-s-1), ... > + # where E(Kinfty/A) is Kinfty/A equipped with the + # A-module structure coming from phi. + A = self.function_ring() Fq = A.base_ring() q = Fq.cardinality() r = self.rank() + # We compute the bound s gs = self.coefficients_in_function_ring(sparse=False) - s = max(gs[i].degree() // (q**i - 1) for i in range(1, r+1)) if s == 0: - # small case return A.one() + # We compute the matrix of phi_T acting on the quotient + # M := (Kinfty/A) / < T^(-s), T^(-s-1), ... > + # (for the standard structure of A-module!) M = matrix(Fq, s) qk = 1 for k in range(r+1): @@ -596,6 +606,12 @@ def class_polynomial(self): M[i, j] += gs[k][e] qk *= q + # We compute the subspace of E(Kinfty/A) (for the twisted + # structure of A-module!) + # V = < T^(-s), T^(-s+1), ... > + # It is also the phi_T-saturation of T^(-s+1) in M, i.e. + # the Fq-vector space generated by the phi_T^i(T^(-s+1)) + # for i varying in NN. v = vector(Fq, s) v[s-1] = 1 vs = [v] @@ -605,6 +621,8 @@ def class_polynomial(self): V = matrix(vs) V.echelonize() + # We compute the action of phi_T on H = M/V + # as an Fq-linear map (encoded in the matrix N) dim = V.rank() pivots = V.pivots() j = ip = 0 @@ -613,6 +631,10 @@ def class_polynomial(self): j += 1 ip += 1 V[i,j] = 1 - N = (V * M * ~V).submatrix(dim, dim) + + # The class module is now H where the action of T + # is given by the matrix N + # The class polynomial is then the characteristic + # polynomial of N return A(N.charpoly()) From ac3ad051562acea4c5a694480576d17f16b40f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 10:05:46 +0100 Subject: [PATCH 322/507] fix one of the failures --- src/sage/categories/sets_cat.py | 2 +- src/sage/rings/finite_rings/integer_mod_ring.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index ba13c6bc9a4..951bc7f2292 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2476,7 +2476,7 @@ def random_element(self, *args): True """ return self._cartesian_product_of_elements( - c.random_element(*args) for c in self.cartesian_factors()) + c.random_element(*args) for c in self.cartesian_factors()) @abstract_method def _sets_keys(self): diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 70ab41447b4..bd04ae7fae6 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -1541,14 +1541,15 @@ def random_element(self, bound=None): sage: while not all(found): ....: found[R.random_element()] = True - We test ``bound``-option:: + We test the ``bound`` option:: - sage: R.random_element(2) in [R(16), R(17), R(0), R(1), R(2)] + sage: R.random_element(2) in [R(-2), R(-1), R(0), R(1), R(2)] True """ if bound is not None: - return CommutativeRing.random_element(self, bound) - a = random.randint(0, self.order() - 1) + a = random.randint(-bound, bound) + else: + a = random.randint(0, self.order() - 1) return self(a) @staticmethod From 34ed89c6be7db97b2873bb2b5ac889aa09e4c103 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Wed, 12 Feb 2025 04:30:25 -0500 Subject: [PATCH 323/507] Linting the changes --- src/sage/geometry/fan.py | 23 ++++--- src/sage/geometry/polyhedron/base5.py | 16 ++--- .../triangulation/point_configuration.py | 62 +++++++++---------- 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 09a3d18bc77..66451a679b0 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2439,14 +2439,15 @@ def Gale_transform(self): m = m.augment(matrix(ZZ, m.nrows(), 1, [1] * m.nrows())) return matrix(ZZ, m.integer_kernel().matrix()) - def is_regular(self): + def is_polytopal(self): r""" - Check if ``self`` is regular. + Check if ``self`` is the normal fan of a polytope. - A rational polyhedral fan is *regular* if it is the normal fan of a - polytope. + A rational polyhedral fan is *polytopal* if it is the normal fan of a + polytope. This is also called *regular*, or provide a *coherent* + subdivision or leads to a *projective* toric variety. - OUTPUT: ``True`` if ``self`` is complete and ``False`` otherwise + OUTPUT: ``True`` if ``self`` is polytopal and ``False`` otherwise EXAMPLES: @@ -2459,16 +2460,16 @@ def is_regular(self): ....: S1 = [Cone([rays[i] for i in indices]) for indices in L] ....: return Fan(S1) - When epsilon=0, it is not regular:: + When epsilon=0, it is not polytopal:: sage: epsilon = 0 - sage: mother(epsilon).is_regular() + sage: mother(epsilon).is_polytopal() False - Doing a slight perturbation makes the same subdivision regular:: + Doing a slight perturbation makes the same subdivision polytopal:: sage: epsilon = 1/2 - sage: mother(epsilon).is_regular() + sage: mother(epsilon).is_polytopal() True .. SEEALSO:: @@ -2476,7 +2477,7 @@ def is_regular(self): :meth:`is_projective`. """ if not self.is_complete(): - raise ValueError('the fan is not complete') + raise ValueError('To be polytopal, the fan should be complete.') from sage.geometry.triangulation.point_configuration import PointConfiguration from sage.geometry.polyhedron.constructor import Polyhedron pc = PointConfiguration(self.rays()) @@ -2492,8 +2493,6 @@ def is_regular(self): poly = Polyhedron(ieqs=ieqs) return self.is_equivalent(poly.normal_fan()) - is_projective = is_regular - def generating_cone(self, n): r""" Return the ``n``-th generating cone of ``self``. diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index a7a7ab4cfb4..405f8cb8f24 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -711,19 +711,19 @@ def deformation_cone(self): 2.2 of [ACEP2020]. """ from .constructor import Polyhedron - A = matrix([_.A() for _ in self.Hrepresentation()]) - A = A.transpose() - A_ker = A.right_kernel_matrix(basis='computed') - gale = tuple(A_ker.columns()) + m = matrix([_.A() for _ in self.Hrepresentation()]) + m = m.transpose() + m_ker = m.right_kernel_matrix(basis='computed') + gale = tuple(m_ker.columns()) collection = [f.ambient_H_indices() for f in self.faces(0)] n = len(gale) - K = None + c = None for cone_indices in collection: dual_cone = Polyhedron(rays=[gale[i] for i in range(n) if i not in cone_indices]) - K = K.intersection(dual_cone) if K is not None else dual_cone - preimages = [A_ker.solve_right(r.vector()) for r in K.rays()] - return Polyhedron(lines=A.rows(), rays=preimages) + c = c.intersection(dual_cone) if c is not None else dual_cone + preimages = [A_ker.solve_right(r.vector()) for r in c.rays()] + return Polyhedron(lines=m.rows(), rays=preimages) ########################################################### # Binary operations. diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index e6ac31dc97b..e76e9bcc3c9 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -61,7 +61,7 @@ (2, 3, 4) sage: list(t) [(1, 3, 4), (2, 3, 4)] - sage: t.plot(axes=False) # needs sage.plot + sage: t.plot(axes=False) # needs sage.plot Graphics object consisting of 12 graphics primitives .. PLOT:: @@ -91,7 +91,7 @@ sage: p = [[0,-1,-1], [0,0,1], [0,1,0], [1,-1,-1], [1,0,1], [1,1,0]] sage: points = PointConfiguration(p) sage: triang = points.triangulate() - sage: triang.plot(axes=False) # needs sage.plot + sage: triang.plot(axes=False) # needs sage.plot Graphics3d Object .. PLOT:: @@ -116,7 +116,7 @@ 16 sage: len(nonregular) 2 - sage: nonregular[0].plot(aspect_ratio=1, axes=False) # needs sage.plot + sage: nonregular[0].plot(aspect_ratio=1, axes=False) # needs sage.plot Graphics object consisting of 25 graphics primitives sage: PointConfiguration.set_engine('internal') # to make doctests independent of TOPCOM @@ -1131,10 +1131,10 @@ def restricted_automorphism_group(self): sage: pyramid = PointConfiguration([[1,0,0], [0,1,1], [0,1,-1], ....: [0,-1,-1], [0,-1,1]]) - sage: G = pyramid.restricted_automorphism_group() # needs sage.graphs sage.groups - sage: G == PermutationGroup([[(3,5)], [(2,3),(4,5)], [(2,4)]]) # needs sage.graphs sage.groups + sage: G = pyramid.restricted_automorphism_group() # needs sage.graphs sage.groups + sage: G == PermutationGroup([[(3,5)], [(2,3),(4,5)], [(2,4)]]) # needs sage.graphs sage.groups True - sage: DihedralGroup(4).is_isomorphic(G) # needs sage.graphs sage.groups + sage: DihedralGroup(4).is_isomorphic(G) # needs sage.graphs sage.groups True The square with an off-center point in the middle. Note that @@ -1142,9 +1142,9 @@ def restricted_automorphism_group(self): `D_4` of the convex hull:: sage: square = PointConfiguration([(3/4,3/4), (1,1), (1,-1), (-1,-1), (-1,1)]) - sage: square.restricted_automorphism_group() # needs sage.graphs sage.groups + sage: square.restricted_automorphism_group() # needs sage.graphs sage.groups Permutation Group with generators [(3,5)] - sage: DihedralGroup(1).is_isomorphic(_) # needs sage.graphs sage.groups + sage: DihedralGroup(1).is_isomorphic(_) # needs sage.graphs sage.groups True """ v_list = [ vector(p.projective()) for p in self ] @@ -1532,9 +1532,9 @@ def bistellar_flips(self): sage: pc.bistellar_flips() (((<0,1,3>, <0,2,3>), (<0,1,2>, <1,2,3>)),) sage: Tpos, Tneg = pc.bistellar_flips()[0] - sage: Tpos.plot(axes=False) # needs sage.plot + sage: Tpos.plot(axes=False) # needs sage.plot Graphics object consisting of 11 graphics primitives - sage: Tneg.plot(axes=False) # needs sage.plot + sage: Tneg.plot(axes=False) # needs sage.plot Graphics object consisting of 11 graphics primitives The 3d analog:: @@ -1549,7 +1549,7 @@ def bistellar_flips(self): sage: pc.bistellar_flips() (((<0,1,3>, <0,2,3>), (<0,1,2>, <1,2,3>)),) sage: Tpos, Tneg = pc.bistellar_flips()[0] - sage: Tpos.plot(axes=False) # needs sage.plot + sage: Tpos.plot(axes=False) # needs sage.plot Graphics3d Object """ flips = [] @@ -2080,7 +2080,7 @@ def Gale_transform(self, points=None, homogenize=True): [ 1 1 1 0 -3] [ 0 2 2 -1 -3] - It might not affect the dimension of the result:: + It might not affect the dimension of the result:: sage: PC = PointConfiguration([[4,0,0],[0,4,0],[0,0,4],[2,1,1],[1,2,1],[1,1,2]]) sage: GT = PC.Gale_transform(homogenize=False);GT @@ -2136,9 +2136,9 @@ def deformation_cone(self, collection): - ``collection`` -- a collection of subconfigurations of ``self``. Subconfigurations are given as indices - OUTPUT: a polyhedron. It contains the liftings of the point configuration - making the collection a regular (or coherent, or projective) - subdivision. + OUTPUT: a polyhedron. It contains the liftings of the point configuration + making the collection a regular (or coherent, or projective, or + polytopal) subdivision. EXAMPLES:: @@ -2170,8 +2170,11 @@ def deformation_cone(self, collection): Let's verify the mother of all examples explained in Section 7.1.1 of [DLRS2010]_:: + sage: def mother(epsilon=0): + ....: return PointConfiguration([(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2)]) + sage: epsilon = 0 - sage: mother = PointConfiguration([(4-epsilon,epsilon,0),(0,4-epsilon,epsilon),(epsilon,0,4-epsilon),(2,1,1),(1,2,1),(1,1,2)]) + sage: m = mother(0) sage: mother.points() (P(4, 0, 0), P(0, 4, 0), P(0, 0, 4), P(2, 1, 1), P(1, 2, 1), P(1, 1, 2)) sage: S1 = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5)] @@ -2179,10 +2182,10 @@ def deformation_cone(self, collection): Both subdivisions `S1` and `S2` are not regular:: - sage: mother_dc1 = mother.deformation_cone(S1) + sage: mother_dc1 = m.deformation_cone(S1) sage: mother_dc1 A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines - sage: mother_dc2 = mother.deformation_cone(S2) + sage: mother_dc2 = m.deformation_cone(S2) sage: mother_dc2 A 4-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 1 ray, 3 lines @@ -2190,7 +2193,7 @@ def deformation_cone(self, collection): only provides a coarsening of the subdivision from the lower hull (it has 5 facets, and should have 8):: - sage: result = Polyhedron([vector(list(mother.points()[_])+[mother_dc1.rays()[0][_]]) for _ in range(len(mother.points()))]) + sage: result = Polyhedron([vector(list(m.points()[_])+[mother_dc1.rays()[0][_]]) for _ in range(len(m.points()))]) sage: result.f_vector() (1, 6, 9, 5, 1) @@ -2198,30 +2201,25 @@ def deformation_cone(self, collection): `S1` becomes regular:: sage: epsilon = 1/2 - sage: mother = PointConfiguration([(4-epsilon,epsilon,0), - (0,4-epsilon,epsilon), - (epsilon,0,4-epsilon), - (2,1,1), - (1,2,1), - (1,1,2)]) - sage: mother.points() + sage: mp = mother(epsilon) + sage: mp.points() (P(7/2, 1/2, 0), P(0, 7/2, 1/2), P(1/2, 0, 7/2), P(2, 1, 1), P(1, 2, 1), P(1, 1, 2)) - sage: mother_dc1 = mother.deformation_cone(S1);mother_dc1 + sage: mother_dc1 = mp.deformation_cone(S1);mother_dc1 A 6-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex, 3 rays, 3 lines - sage: mother_dc2 = mother.deformation_cone(S2);mother_dc2 + sage: mother_dc2 = mp.deformation_cone(S2);mother_dc2 A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex and 3 lines - .. SEEALSO:: - + .. SEEALSO:: + :meth:`~sage.schemes.toric.variety.Kaehler_cone` - + REFERENCES: - + For more information, see Section 5.4 of [DLRS2010]_ and Section 2.2 of [ACEP2020]. """ From 931ed170b8593beabba533391cabd5114557e8d4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Wed, 12 Feb 2025 04:46:47 -0500 Subject: [PATCH 324/507] More linting --- src/sage/geometry/fan.py | 2 +- src/sage/geometry/polyhedron/base5.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 66451a679b0..c9f83337aae 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2477,7 +2477,7 @@ def is_polytopal(self): :meth:`is_projective`. """ if not self.is_complete(): - raise ValueError('To be polytopal, the fan should be complete.') + raise ValueError('to be polytopal, the fan should be complete') from sage.geometry.triangulation.point_configuration import PointConfiguration from sage.geometry.polyhedron.constructor import Polyhedron pc = PointConfiguration(self.rays()) diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index 405f8cb8f24..0367bdebaad 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -694,7 +694,7 @@ def deformation_cone(self): sage: py = Polyhedron([(0, -1, -1), (0, -1, 1), (0, 1, -1), (0, 1, 1), (1, 0, 0)]) sage: dc_py = py.deformation_cone(); dc_py A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex, 1 ray, 3 lines - sage: [_.b() for _ in py.Hrepresentation()] + sage: [ineq.b() for ineq in py.Hrepresentation()] [0, 1, 1, 1, 1] sage: r = dc_py.rays()[0] sage: l1,l2,l3 = dc_py.lines() @@ -711,7 +711,7 @@ def deformation_cone(self): 2.2 of [ACEP2020]. """ from .constructor import Polyhedron - m = matrix([_.A() for _ in self.Hrepresentation()]) + m = matrix([ineq.A() for ineq in self.Hrepresentation()]) m = m.transpose() m_ker = m.right_kernel_matrix(basis='computed') gale = tuple(m_ker.columns()) @@ -722,7 +722,7 @@ def deformation_cone(self): dual_cone = Polyhedron(rays=[gale[i] for i in range(n) if i not in cone_indices]) c = c.intersection(dual_cone) if c is not None else dual_cone - preimages = [A_ker.solve_right(r.vector()) for r in c.rays()] + preimages = [m_ker.solve_right(r.vector()) for r in c.rays()] return Polyhedron(lines=m.rows(), rays=preimages) ########################################################### From bc9692dbc5c4876a5c196fe6621f74168d465c02 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 10:54:18 +0100 Subject: [PATCH 325/507] is_prime_field is provided by the category Rings now --- src/sage/combinat/sf/sfa.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index f746ff03b0a..92a913a71ec 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -1877,9 +1877,6 @@ def __init__(self, Sym, basis_name=None, prefix=None, graded=True): _print_style = 'lex' - def is_prime_field(self): - return False - # Todo: share this with ncsf and over algebras with basis indexed by word-like elements def __getitem__(self, c): r""" From 34d865fe04461a40abc0f033cb9d3e355a1fb7dc Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 10:55:17 +0100 Subject: [PATCH 326/507] reviewer's suggestions about docstring formatting and one list comprehension --- src/sage/data_structures/stream.py | 5 ++-- src/sage/rings/lazy_series_ring.py | 25 ++++++++----------- .../polynomial/multi_polynomial_sequence.py | 18 +++++++++++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index d777d22d12a..387f1e31fc2 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1402,6 +1402,7 @@ class CoefficientRing(UniqueRepresentation, FractionField_generic): """ def __init__(self, base_ring): """ + Initialize ``self``. EXAMPLES:: @@ -1919,8 +1920,8 @@ def _subs_in_caches(self, var, val): INPUT: - - ``var``, a variable in ``self._P`` - - ``val``, the value that should replace the variable + - ``var`` -- a variable in ``self._P`` + - ``val`` -- the value that should replace the variable EXAMPLES:: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 5f8635d9746..6919335ca75 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -667,14 +667,12 @@ def _terms_of_degree(self, n, R): Return the list of terms occurring in a coefficient of degree ``n`` such that coefficients are in the ring ``R``. - If ``self`` is a univariate Laurent, power, or Dirichlet - series, this is the list containing the one of the base ring. - - If ``self`` is a multivariate power series, this is the list - of monomials of total degree ``n``. - - If ``self`` is a lazy symmetric function, this is the list - of basis elements of total degree ``n``. + For example, if ``self`` is a univariate Laurent, power, or + Dirichlet series, this is the list containing the one of the + base ring. If ``self`` is a multivariate power series, this + is the list of monomials of total degree ``n``. If ``self`` + is a lazy symmetric function, this is the list of basis + elements of total degree ``n``. EXAMPLES:: @@ -3292,12 +3290,11 @@ def _terms_of_degree(self, n, R): B = B.change_ring(R) if self._arity == 1: return list(B.homogeneous_component_basis(n)) - l = [] - for c in IntegerVectors(n, self._arity): - for m in cartesian_product_iterator([F.homogeneous_component_basis(p) - for F, p in zip(B.tensor_factors(), c)]): - l.append(tensor(m)) - return l + + return [tensor(m) + for c in IntegerVectors(n, self._arity) + for m in cartesian_product_iterator([F.homogeneous_component_basis(p) + for F, p in zip(B.tensor_factors(), c)])] def _element_constructor_(self, x=None, valuation=None, degree=None, constant=None, check=True): r""" diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 0493b2f2e07..291d3802698 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -273,7 +273,7 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): TESTS: - A PolynomialSequence can exist with elements in an infinite field of + A ``PolynomialSequence`` can exist with elements in an infinite field of characteristic 2 (see :issue:`19452`):: sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence @@ -283,7 +283,7 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): sage: PolynomialSequence([0], R) [0] - A PolynomialSequence can be created from an iterator (see :issue:`25989`):: + A ``PolynomialSequence`` can be created from an iterator (see :issue:`25989`):: sage: R. = QQ[] sage: PolynomialSequence(iter(R.gens())) @@ -292,6 +292,20 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): [x, y, z] sage: PolynomialSequence(iter([(x,y), (z,)]), R) [x, y, z] + + A ``PolynomialSequence`` can be created from elements of an + ``InfinitePolynomialRing``:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: s = PolynomialSequence([a[i]-a[i+1] for i in range(3)]) + sage: s + [-a_1 + a_0, -a_2 + a_1, -a_3 + a_2] + sage: s.coefficients_monomials() + ( + [ 0 0 -1 1] + [ 0 -1 1 0] + [-1 1 0 0], (a_3, a_2, a_1, a_0) + ) """ from sage.structure.element import Matrix try: From 3edbf8bb6ab1dcc061f58850a7e9cc6aababd423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 13:33:27 +0100 Subject: [PATCH 327/507] remove duplicate methods --- src/sage/categories/commutative_rings.py | 43 ++++-------------------- src/sage/categories/rings.py | 17 ++-------- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index 1730bd6fa82..961ca17d629 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -140,12 +140,13 @@ def is_commutative(self) -> bool: def _ideal_class_(self, n=0): r""" - Return a callable object that can be used to create ideals in this - commutative ring. + Return a callable object that can be used to create ideals + in this commutative ring. - This class can depend on `n`, the number of generators of the ideal. - The default input of `n=0` indicates an unspecified number of generators, - in which case a class that works for any number of generators is returned. + This class can depend on `n`, the number of generators of + the ideal. The default input of `n=0` indicates an + unspecified number of generators, in which case a class + that works for any number of generators is returned. EXAMPLES:: @@ -340,38 +341,6 @@ def over(self, base=None, gen=None, gens=None, name=None, names=None): gens = (gen,) return RingExtension(self, base, gens, names) - def _ideal_class_(self, n=0): - r""" - Return a callable object that can be used to create ideals in this - ring. - - This class can depend on `n`, the number of generators of the ideal. - The default input of `n=0` indicates an unspecified number of generators, - in which case a class that works for any number of generators is returned. - - EXAMPLES:: - - sage: ZZ._ideal_class_() - - sage: RR._ideal_class_() - - sage: R. = GF(5)[] - sage: R._ideal_class_(1) - - sage: S = R.quo(x^3 - y^2) - sage: S._ideal_class_(1) - - sage: S._ideal_class_(2) - - sage: T. = S[] # needs sage.libs.singular - sage: T._ideal_class_(5) # needs sage.libs.singular - - sage: T._ideal_class_(1) # needs sage.libs.singular - - """ - from sage.rings.ideal import Ideal_generic, Ideal_principal - return Ideal_principal if n == 1 else Ideal_generic - def frobenius_endomorphism(self, n=1): """ Return the Frobenius endomorphism. diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index b1e229d1af2..0de9d22e407 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -5,7 +5,8 @@ # **************************************************************************** # Copyright (C) 2005 David Kohel # William Stein -# 2008 Teresa Gomez-Diaz (CNRS) +# 2008 Teresa Gomez-Diaz (CNRS) +# # 2008-2011 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) @@ -761,7 +762,7 @@ def unit_ideal(self): sage: Zp(7).unit_ideal() # needs sage.rings.padics Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 """ - return self.principal_ideal(self.one(), coerce=False) + return self._ideal_class_(1)(self, [self.one()]) def _ideal_class_(self, n=0): r""" @@ -815,18 +816,6 @@ def zero_ideal(self): """ return self._ideal_class_(1)(self, [self.zero()]) - @cached_method - def unit_ideal(self): - """ - Return the unit ideal of this ring. - - EXAMPLES:: - - sage: Zp(7).unit_ideal() # needs sage.rings.padics - Principal ideal (1 + O(7^20)) of 7-adic Ring with capped relative precision 20 - """ - return self._ideal_class_(1)(self, [self.one()]) - def principal_ideal(self, gen, coerce=True): """ Return the principal ideal generated by gen. From c84ed114be1a614a117e683eddb2daffcc90bf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 13:39:54 +0100 Subject: [PATCH 328/507] suggested detail --- src/sage/ext_data/magma/sage/basic.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 8689711341c..918e523e403 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -153,9 +153,9 @@ intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt {p-adic rings, either free precision model or exact model} prec := Precision(X); if Type(prec) eq Infty then - return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; + return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(prec), false; else - return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; + return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(prec)), false; end if; end intrinsic; @@ -163,9 +163,9 @@ intrinsic Sage(X::FldPad) -> MonStgElt, BoolElt {p-adic fields, either free precision model or exact model} prec := Precision(X); if Type(prec) eq Infty then - return Sprintf("Qp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(Precision(X))), false; + return Sprintf("Qp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(prec)), false; else - return Sprintf("Qp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(Precision(X))), false; + return Sprintf("Qp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(prec)), false; end if; end intrinsic; From a21e9f4e86b41e724b5462d5c00ee7e4d865b487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 13:41:46 +0100 Subject: [PATCH 329/507] remove import --- src/sage/rings/finite_rings/integer_mod_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index bd04ae7fae6..590f40ffb5a 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -67,7 +67,7 @@ from sage.arith.misc import factor from sage.arith.misc import primitive_root from sage.arith.misc import CRT_basis -from sage.rings.ring import Field, CommutativeRing +from sage.rings.ring import Field from sage.misc.mrange import cartesian_product_iterator import sage.rings.abc from sage.rings.finite_rings import integer_mod From bb64b5b8e8a553060b75e400e0a80cd6348da943 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 14:09:47 +0100 Subject: [PATCH 330/507] add TODO for possible future performance improvement --- src/sage/data_structures/stream.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 387f1e31fc2..8f93782b850 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1951,6 +1951,11 @@ def retract(c): def fix_cache(j, s, ao): if s._cache[ao]: + # TODO: perhaps, if not + # self._coefficient_ring.has_coerce_map_from(s._cache[ao].parent()) + # we can be certain that there is still an + # undetermined coefficient -- if so, we could replace + # the following line for a performance improvement if s._cache[ao] in self._coefficient_ring: s._true_order = True return False From 2215f72c6cb8d2aa59c3f342541de38abec29cd0 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 14:17:24 +0100 Subject: [PATCH 331/507] special case repr of generators for better performance --- src/sage/rings/polynomial/multi_polynomial_element.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 3b18664f505..09a3a025ca5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -467,6 +467,8 @@ def _repr_(self): sage: repr(-I*y - x^2) # indirect doctest '-x^2 + (-I)*y' """ + if self.is_gen(): + return self.parent().variable_names()[self.degrees().nonzero_positions()[0]] try: key = self.parent().term_order().sortkey except AttributeError: From 45404b521ea8b692bb6057f7e9250427292b8c85 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 14:28:40 +0100 Subject: [PATCH 332/507] slightly better formatting of docstring and polish --- .../rings/polynomial/infinite_polynomial_element.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index b7d10c05db1..91ff5346b9b 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -1269,17 +1269,18 @@ def gcd(self, x): EXAMPLES:: - sage: R.=InfinitePolynomialRing(QQ) - sage: p1=x[0] + x[1]**2 - sage: gcd(p1,p1+3) + sage: R. = InfinitePolynomialRing(QQ) + sage: p1 = x[0] + x[1]^2 + sage: gcd(p1, p1 + 3) 1 - sage: gcd(p1,p1)==p1 + sage: gcd(p1, p1) == p1 True """ P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return self.__class__.__base__(self.parent(), self._p.gcd(x._p)) + g = self._p.gcd(x._p) + return self.__class__.__base__(P, g) class InfinitePolynomial_sparse(InfinitePolynomial): From a69cabd2e51dc735269a5109b488d1e48cebfb60 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Wed, 12 Feb 2025 09:16:30 -0500 Subject: [PATCH 333/507] Fix the tests --- .../triangulation/point_configuration.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index e76e9bcc3c9..312da9db9a0 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -2073,20 +2073,20 @@ def Gale_transform(self, points=None, homogenize=True): sage: pc2 = PointConfiguration([[0,0],[3,0],[0,3],[3,3],[1,1]]) sage: pc2.Gale_transform(homogenize=False) - [-1 0 0 0 0] - [ 0 1 1 -1 0] + [ 1 0 0 0 0] [ 0 1 1 0 -3] + [ 0 0 0 1 -3] sage: pc2.Gale_transform(homogenize=True) [ 1 1 1 0 -3] [ 0 2 2 -1 -3] - It might not affect the dimension of the result:: + It might not affect the result (when acyclic):: sage: PC = PointConfiguration([[4,0,0],[0,4,0],[0,0,4],[2,1,1],[1,2,1],[1,1,2]]) sage: GT = PC.Gale_transform(homogenize=False);GT - [ 2 1 1 -4 0 0] - [ 1 2 1 0 -4 0] - [-2 -2 -1 3 3 -1] + [ 1 0 0 -3 1 1] + [ 0 1 0 1 -3 1] + [ 0 0 1 1 1 -3] sage: GT = PC.Gale_transform(homogenize=True);GT [ 1 0 0 -3 1 1] [ 0 1 0 1 -3 1] @@ -2103,10 +2103,10 @@ def Gale_transform(self, points=None, homogenize=True): [ 0 1 0 -1 1 -1 0] [ 0 0 1 -1 0 -1 1] sage: g_inhom = pc3.Gale_transform(homogenize=False);g_inhom - [-1 1 1 1 0 0 0] - [ 0 1 0 0 1 0 0] - [ 1 -1 -1 0 0 1 0] - [ 0 0 1 0 0 0 1] + [1 0 0 0 1 1 1] + [0 1 0 0 1 0 0] + [0 0 1 0 0 0 1] + [0 0 0 1 0 1 0] sage: Polyhedron(rays=g_hom.columns()) A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex and 3 lines sage: Polyhedron(rays=g_inhom.columns()) @@ -2175,7 +2175,7 @@ def deformation_cone(self, collection): sage: epsilon = 0 sage: m = mother(0) - sage: mother.points() + sage: m.points() (P(4, 0, 0), P(0, 4, 0), P(0, 0, 4), P(2, 1, 1), P(1, 2, 1), P(1, 1, 2)) sage: S1 = [(0,1,4),(0,3,4),(1,2,5),(1,4,5),(0,2,3),(2,3,5)] sage: S2 = [(0,1,3),(1,3,4),(1,2,4),(2,4,5),(0,2,5),(0,3,5)] From 3aab2e3fa96ab5ea463f87f9ed6c91e92f570182 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 17:05:52 +0100 Subject: [PATCH 334/507] adapt repr of TropicalMPolynomial --- src/sage/rings/semirings/tropical_mpolynomial.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index c1f1a744acb..225435ba488 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -662,7 +662,14 @@ def _repr_(self): """ if not self.monomial_coefficients(): return str(self.parent().base().zero()) - s = super()._repr_() + try: + key = self.parent().term_order().sortkey + except AttributeError: + key = None + atomic = self.parent().base_ring()._repr_option('element_is_atomic') + s = self.element().poly_repr(self.parent().variable_names(), + atomic_coefficients=atomic, + sortkey=key) if self.monomials()[-1].is_constant(): if self.monomial_coefficient(self.parent()(0)) < 0: s = s.replace(" - ", " + -") From ef5ca3a67ae798a1f8790012a7072a31f208aa4f Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 17:08:35 +0100 Subject: [PATCH 335/507] implement im_gens in the class fraction_field_FpT --- src/sage/rings/fraction_field_FpT.pyx | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 5317c1551fb..bf9d5d6ed37 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -572,6 +572,36 @@ cdef class FpTElement(FieldElement): normalize(x._numer, x._denom, self.p) return x + def _im_gens_(self, codomain, im_gens, base_map=None): + r""" + Return the image of this element in ``codomain`` under the + map that sends the images of the generators of the parent + to the tuple of elements of ``im_gens``. + + INPUT: + + - ``codomain`` -- a ring; where the image is computed + + - ``im_gens`` -- a list; the images of the generators + of the parent + + - ``base_map`` -- a morphism (default: ``None``); + the action on the underlying base ring + + EXAMPLES:: + + sage: Fq = GF(5) + sage: A. = GF(5)[] + sage: K. = Frac(A) + sage: f = K.hom([T^2]) + sage: f(1/T) + 1/T^2 + """ + nden = self.denom()._im_gens_(codomain, im_gens, base_map=base_map) + invden = nden.inverse_of_unit() + nnum = self.numer()._im_gens_(codomain, im_gens, base_map=base_map) + return nnum * invden + cpdef FpTElement next(self): """ Iterate through all polynomials, returning the "next" polynomial after this one. From aad8498d37668b397c1b880b7f90f6d7e577bf4d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 18:56:39 +0100 Subject: [PATCH 336/507] mark a doctest as random --- src/sage/rings/lazy_series_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 6919335ca75..d601b73d358 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -801,7 +801,7 @@ def define_implicitly(self, series, equations, max_lookahead=1): sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) - sage: F + sage: F # random From c8b88af9d0c1495c325c98a071d89edac81a6748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 19:04:03 +0100 Subject: [PATCH 337/507] fix the other doctest --- src/sage/categories/rings.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 46fc677c840..78919cda38b 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1647,16 +1647,17 @@ def _random_nonzero_element(self, *args, **kwds): if not x.is_zero(): return x - def random_element(self, bound=2): + def random_element(self, *args): """ Return a random integer coerced into this ring. - The integer is chosen uniformly - from the interval ``[-bound,bound]``. - INPUT: - - ``bound`` -- integer (default: 2) + - either no integer, one integer or two integers + + The integer is chosen uniformly from the closed interval + ``[-2,2]``, ``[-a,a]`` or ``[a,b]`` according to the + length of the input. ALGORITHM: @@ -1668,8 +1669,17 @@ def random_element(self, bound=2): 1 sage: QQ.random_element(8) # random 2 + sage: ZZ.random_element(4,12) # random + 7 """ - return randint(-bound, bound) * self.one() + if not args: + a, b = -2, 2 + elif len(args) == 1: + bound = args[0] + a, b = -bound, bound + else: + a, b = args[0], args[1] + return randint(a, b) * self.one() class ElementMethods: def is_unit(self) -> bool: From 7cdd4978a1e6dd0d28c69423912807e3b44d78a6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Feb 2025 19:35:34 +0100 Subject: [PATCH 338/507] remove more duplicate code --- src/sage/categories/rings.py | 67 ------------------------------------ src/sage/categories/rngs.py | 8 +++-- 2 files changed, 6 insertions(+), 69 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 0de9d22e407..7f55192fad5 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -764,73 +764,6 @@ def unit_ideal(self): """ return self._ideal_class_(1)(self, [self.one()]) - def _ideal_class_(self, n=0): - r""" - Return a callable object that can be used to create ideals in this - ring. - - EXAMPLES:: - - sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules - sage: MS._ideal_class_() # needs sage.modules - - - Since :issue:`7797`, non-commutative rings have ideals as well:: - - sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules - sage: A._ideal_class_() # needs sage.combinat sage.modules - - """ - from sage.rings.noncommutative_ideals import Ideal_nc - return Ideal_nc - - @cached_method - def zero_ideal(self): - """ - Return the zero ideal of this ring (cached). - - EXAMPLES:: - - sage: ZZ.zero_ideal() - Principal ideal (0) of Integer Ring - sage: QQ.zero_ideal() - Principal ideal (0) of Rational Field - sage: QQ['x'].zero_ideal() - Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field - - The result is cached:: - - sage: ZZ.zero_ideal() is ZZ.zero_ideal() - True - - TESTS: - - Make sure that :issue:`13644` is fixed:: - - sage: # needs sage.rings.padics - sage: K = Qp(3) - sage: R. = K[] - sage: L. = K.extension(a^2-3) - sage: L.ideal(a) - Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 - """ - return self._ideal_class_(1)(self, [self.zero()]) - - def principal_ideal(self, gen, coerce=True): - """ - Return the principal ideal generated by gen. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: R.principal_ideal(x+2*y) - Ideal (x + 2*y) of Multivariate Polynomial Ring in x, y over Integer Ring - """ - C = self._ideal_class_(1) - if coerce: - gen = self(gen) - return C(self, [gen]) - def characteristic(self): """ Return the characteristic of this ring. diff --git a/src/sage/categories/rngs.py b/src/sage/categories/rngs.py index e463bf56e2d..5c13497fee1 100644 --- a/src/sage/categories/rngs.py +++ b/src/sage/categories/rngs.py @@ -97,7 +97,11 @@ def _ideal_class_(self, n=0): The argument `n`, standing for the number of generators of the ideal, is ignored. - EXAMPLES: + EXAMPLES:: + + sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules + sage: MS._ideal_class_() # needs sage.modules + Since :issue:`7797`, non-commutative rings have ideals as well:: @@ -153,4 +157,4 @@ def zero_ideal(self): sage: L.ideal(a) Principal ideal (1 + O(a^40)) of 3-adic Eisenstein Extension Field in a defined by a^2 - 3 """ - return self.principal_ideal(self.zero(), coerce=False) + return self._ideal_class_(1)(self, [self.zero()]) From 152c47f98115349bcf1b1f205110c4f51102178b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 12 Feb 2025 21:10:14 +0100 Subject: [PATCH 339/507] fix --- src/sage/categories/sets_cat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 951bc7f2292..6fadc7f9ccf 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2472,7 +2472,7 @@ def random_element(self, *args): sage: c2 = C.random_element(4,7) sage: c2 # random (6, 5, 6, 4, 5, 6, 6, 4, 5, 5) - sage: all(4 <= i < 7 for i in c2) + sage: all(4 <= i <= 7 for i in c2) True """ return self._cartesian_product_of_elements( From 52655a9a1b0512992c268ed0e6751edec9faf095 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 22:21:36 +0100 Subject: [PATCH 340/507] address Antoine's comments --- .../drinfeld_modules/charzero_drinfeld_module.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 32225468aa8..41323879414 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -533,6 +533,9 @@ def class_polynomial(self): Return the class polynomial, that is the Fitting ideal of the class module, of this Drinfeld module. + We refer to [Tae2012]_ for the definition and basic + properties of the class module. + EXAMPLES: We check that the class module of the Carlitz module @@ -558,7 +561,7 @@ def class_polynomial(self): Here is an example with a nontrivial class module:: - sage: phi = DrinfeldModule(A, [T, -T^(2*q-1) + 2*T^(q-1)]) + sage: phi = DrinfeldModule(A, [T, 2*T^14 + 2*T^4]) sage: phi.class_polynomial() T + 3 @@ -629,7 +632,7 @@ def class_polynomial(self): for i in range(dim, s): while ip < dim and j == pivots[ip]: j += 1 - ip += 1 + ip += 1 V[i,j] = 1 N = (V * M * ~V).submatrix(dim, dim) From 67496edeaef29e9f0252a3f9540a2c44c46e5d15 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 22:24:08 +0100 Subject: [PATCH 341/507] remove "Taelman's unit" from the documentation --- .../function_field/drinfeld_modules/charzero_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 41323879414..ff7c2b60ab7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -11,7 +11,7 @@ AUTHORS: - David Ayotte (2023-09) -- Xavier Caruso (2024-12) - computation of class polynomials and Taelman's units +- Xavier Caruso (2024-12) - computation of class polynomials """ # ***************************************************************************** From 2876619203b19e5fed0d15359b484827cf2c57c4 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 22:45:28 +0100 Subject: [PATCH 342/507] singular --- src/sage/rings/fraction_field_FpT.pyx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index bf9d5d6ed37..cf2b12861da 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -575,22 +575,21 @@ cdef class FpTElement(FieldElement): def _im_gens_(self, codomain, im_gens, base_map=None): r""" Return the image of this element in ``codomain`` under the - map that sends the images of the generators of the parent - to the tuple of elements of ``im_gens``. + map that sends the image of the generator of the parent to + the element in ``im_gens``. INPUT: - ``codomain`` -- a ring; where the image is computed - - ``im_gens`` -- a list; the images of the generators - of the parent + - ``im_gens`` -- a list containing the image of the + generator of the parent as unique element - ``base_map`` -- a morphism (default: ``None``); the action on the underlying base ring EXAMPLES:: - sage: Fq = GF(5) sage: A. = GF(5)[] sage: K. = Frac(A) sage: f = K.hom([T^2]) From ce094d1820c31bcc6ecc361c5f0736579239373e Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 12 Feb 2025 23:14:05 +0100 Subject: [PATCH 343/507] add details in documentation --- src/sage/modules/free_module.py | 2 ++ src/sage/modules/free_module_pseudohomspace.py | 18 ++++++++++++------ src/sage/modules/free_module_pseudomorphism.py | 9 +++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 71efa38e777..7d6004ff7d7 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3163,6 +3163,8 @@ def pseudohom(self, f, twist, codomain=None, side="left"): pseudomorphism - ``twist`` -- the twisting morphism or the twisting derivation + (if a derivation is given, the corresponding morphism `\theta` + is automatically infered) - ``codomain`` -- (default: ``None``) the codomain of the pseudo morphisms; if ``None``, the codomain is the same than the domain diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 292d16f086d..b55abedb5fe 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -37,6 +37,10 @@ class FreeModulePseudoHomspace(UniqueRepresentation, HomsetWithBase): For free modules, the elements of a pseudomorphism correspond to matrices which define the mapping on elements of a basis. + This class is not supposed to be instantiated directly; the user should + use instead the method :meth:`sage.rings.module.free_module.FreeModule_generic.pseudoHom` + to create a space of pseudomorphisms. + TESTS:: sage: F = GF(125) @@ -57,11 +61,12 @@ def __classcall_private__(cls, domain, codomain, twist): INPUT: - - ``domain`` -- a free module, the domain of this pseudomorphism + - ``domain`` -- a free module, the domain of this pseudomorphism - - ``codomain`` -- a free module, the codomain of this pseudomorphism + - ``codomain`` -- a free module, the codomain of this pseudomorphism - - ``twist`` -- a twisting morphism/derivation or a Ore polynomial ring + - ``twist`` -- a twisting morphism/derivation or the corresponding + Ore polynomial ring TESTS:: @@ -94,11 +99,12 @@ def __init__(self, domain, codomain, ore): INPUT: - - ``domain`` -- a free module, the domain of this pseudomorphism + - ``domain`` -- a free module, the domain of this pseudomorphism - - ``codomain`` -- a free module, the codomain of this pseudomorphism + - ``codomain`` -- a free module, the codomain of this pseudomorphism - - ``ore`` -- the underlying Ore polynomial ring + - ``ore`` -- the underlying Ore polynomial ring (built from the + twisting morphism and derivation) TESTS:: diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 965152a893e..39eadae6764 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -45,6 +45,15 @@ class FreeModulePseudoMorphism(Morphism): The map `\theta` (resp. `\delta`) is referred to as the twisting endomorphism (resp. the twisting derivation) of `f`. + .. NOTE:: + + The implementation currently requires that `M` and `M'` + are free modules. + + This class is not supposed to be instantiated directly; the user should + use instead the method :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom` + to create a pseudomorphism. + TESTS:: sage: P. = ZZ[] From a3f602ed7d989d796da31e10d86d5744f82762a5 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 07:55:18 +0100 Subject: [PATCH 344/507] Update src/sage/modules/free_module_pseudohomspace.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- src/sage/modules/free_module_pseudohomspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index b55abedb5fe..b3831d4c5e4 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -227,7 +227,7 @@ def ore_ring(self, var='x'): def matrix_space(self): r""" Return the matrix space used for representing the - pseudomorphism in this space. + pseudomorphisms in this space. EXAMPLES:: From 90b7ca8269b53111007dad4d311c9c5985ce11d0 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 07:58:09 +0100 Subject: [PATCH 345/507] Update src/sage/modules/free_module_pseudomorphism.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../modules/free_module_pseudomorphism.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 39eadae6764..8628409b65d 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -49,6 +49,48 @@ class FreeModulePseudoMorphism(Morphism): The implementation currently requires that `M` and `M'` are free modules. + .. WARNING:: + + At the moment, it is not possible to specify both a twisting + endomorphism and a twisting derivation. Only one of those can be + used, preferably using the `twist` argument in the method + :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom`. + + We represent pseudo morphisms by matrices with coefficient in the + base ring `R`. The matrix `\mathcal M_f` representing a pseudo + morphism is such that its lines (resp. columns if ``side`` is + ``"right"``) are the coordinates of the images of the distinguished + basis of the domain (see also method :meth:`matrix`). More + concretely, let `n` (resp. `n'`) be the dimension of `M` (resp. + `M'`), let `(e_1, \dots, e_n)` be a basis of `M`. For any `x = + \sum_{i=1}^n x_i e_i \in M`, we have + + .. MATH:: + + f(x) = \begin{pmatrix} + \theta(x_1) & \cdots & \theta(x_n) + \end{pmatrix} + \mathcal M_f + + + \begin{pmatrix} + \delta(x_1) & \cdots & \theta(x_n) + \end{pmatrix} + . + + If ``side`` is ``"right"``, we have: + + .. MATH:: + + f(x) = \mathcal M_f + \begin{pmatrix} + \theta(x_1) \\ \vdots \\ \theta(x_n) + \end{pmatrix} + + + + \begin{pmatrix} + \delta(x_1) \\ \vdots \\ \theta(x_n) + \end{pmatrix} + . This class is not supposed to be instantiated directly; the user should use instead the method :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom` From 048009bcffc839d7a26360c4c113aa14f12cfc11 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 07:58:29 +0100 Subject: [PATCH 346/507] Update src/sage/modules/free_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- src/sage/modules/free_module.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 7d6004ff7d7..a93f42b8213 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3156,6 +3156,16 @@ def pseudohom(self, f, twist, codomain=None, side="left"): f(\lambda x) = \theta(\lambda) f(x) + \delta(\lambda) x When `\delta` is nonzero, this requires that `M` coerces into `M'`. + .. WARNING:: + + At the moment, it is not possible to specify both a twisting + endomorphism and a twisting derivation. Only one of those can be + used, preferably using the `twist` argument. + + We represent pseudo morphisms by matrices with coefficient in the base + ring `R`. See class + :class:`sage.modules.free_module_pseudomorphism.FreeModulePseudoMorphism` + for details. INPUT: From 941e26f157d0f06b0e3cffc4c2b20c3f963be52d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 07:58:43 +0100 Subject: [PATCH 347/507] Update src/sage/modules/free_module_pseudohomspace.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- src/sage/modules/free_module_pseudohomspace.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index b3831d4c5e4..1b7ea94dcfb 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -245,6 +245,9 @@ def basis(self, side="left"): r""" Return a basis for the underlying matrix space. + The result does not depend on the `side` of the homspace, i.e. + if matrices are acted upon on the left or on the right. + EXAMPLES:: sage: Fq = GF(7^3) From a342fbe2c6e61245abb3e1364b1ac7b8b8669d81 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 08:09:56 +0100 Subject: [PATCH 348/507] improve documentation --- src/sage/modules/free_module.py | 13 +++----- .../modules/free_module_pseudohomspace.py | 5 ++- .../modules/free_module_pseudomorphism.py | 32 ++++++++----------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index a93f42b8213..19b586a3102 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3156,16 +3156,13 @@ def pseudohom(self, f, twist, codomain=None, side="left"): f(\lambda x) = \theta(\lambda) f(x) + \delta(\lambda) x When `\delta` is nonzero, this requires that `M` coerces into `M'`. - .. WARNING:: - At the moment, it is not possible to specify both a twisting - endomorphism and a twisting derivation. Only one of those can be - used, preferably using the `twist` argument. + .. NOTE:: - We represent pseudo morphisms by matrices with coefficient in the base - ring `R`. See class - :class:`sage.modules.free_module_pseudomorphism.FreeModulePseudoMorphism` - for details. + Internally, pseudomorphisms are represented by matrices with + coefficient in the base ring `R`. See class + :class:`sage.modules.free_module_pseudomorphism.FreeModulePseudoMorphism` + for details. INPUT: diff --git a/src/sage/modules/free_module_pseudohomspace.py b/src/sage/modules/free_module_pseudohomspace.py index 1b7ea94dcfb..293fda25e40 100644 --- a/src/sage/modules/free_module_pseudohomspace.py +++ b/src/sage/modules/free_module_pseudohomspace.py @@ -202,7 +202,10 @@ def _repr_(self): def ore_ring(self, var='x'): r""" - Return the underlying Ore polynomial ring. + Return the underlying Ore polynomial ring, that is + the Ore polynomial ring over the base field twisted + by the twisting morphism and the twisting derivation + attached to this homspace. INPUT: diff --git a/src/sage/modules/free_module_pseudomorphism.py b/src/sage/modules/free_module_pseudomorphism.py index 8628409b65d..7ed8cf1ef49 100644 --- a/src/sage/modules/free_module_pseudomorphism.py +++ b/src/sage/modules/free_module_pseudomorphism.py @@ -49,21 +49,15 @@ class FreeModulePseudoMorphism(Morphism): The implementation currently requires that `M` and `M'` are free modules. - .. WARNING:: - - At the moment, it is not possible to specify both a twisting - endomorphism and a twisting derivation. Only one of those can be - used, preferably using the `twist` argument in the method - :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom`. - - We represent pseudo morphisms by matrices with coefficient in the - base ring `R`. The matrix `\mathcal M_f` representing a pseudo - morphism is such that its lines (resp. columns if ``side`` is - ``"right"``) are the coordinates of the images of the distinguished - basis of the domain (see also method :meth:`matrix`). More - concretely, let `n` (resp. `n'`) be the dimension of `M` (resp. - `M'`), let `(e_1, \dots, e_n)` be a basis of `M`. For any `x = - \sum_{i=1}^n x_i e_i \in M`, we have + + We represent pseudomorphisms by matrices with coefficient in the + base ring `R`. The matrix `\mathcal M_f` representing `f` is such + that its lines (resp. columns if ``side`` is ``"right"``) are the + coordinates of the images of the distinguished basis of the domain + (see also method :meth:`matrix`). + More concretely, let `n` (resp. `n'`) be the dimension of `M` + (resp. `M'`), let `(e_1, \dots, e_n)` be a basis of `M`. + For any `x = \sum_{i=1}^n x_i e_i \in M`, we have .. MATH:: @@ -77,7 +71,7 @@ class FreeModulePseudoMorphism(Morphism): \end{pmatrix} . - If ``side`` is ``"right"``, we have: + When ``side`` is ``"right"``, the formula is .. MATH:: @@ -85,15 +79,15 @@ class FreeModulePseudoMorphism(Morphism): \begin{pmatrix} \theta(x_1) \\ \vdots \\ \theta(x_n) \end{pmatrix} - + \begin{pmatrix} \delta(x_1) \\ \vdots \\ \theta(x_n) \end{pmatrix} . - This class is not supposed to be instantiated directly; the user should - use instead the method :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom` + This class is not supposed to be instantiated directly; the user + should use instead the method + :meth:`sage.rings.module.free_module.FreeModule_generic.pseudohom` to create a pseudomorphism. TESTS:: From 1db04c23b18cd048ed2a018522a56eddb8eb3308 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Feb 2025 08:11:26 +0100 Subject: [PATCH 349/507] grammar --- src/sage/modules/free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 19b586a3102..a077baf30da 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3122,7 +3122,7 @@ def pseudoHom(self, twist, codomain=None): - ``twist`` -- the twisting morphism or the twisting derivation - ``codomain`` -- (default: ``None``) the codomain of the pseudo - morphisms; if ``None``, the codomain is the same than the domain + morphisms; if ``None``, the codomain is the same as the domain EXAMPLES:: From 5e1477e5dffc99dcb1c6ece0db10e5eccb48e3b2 Mon Sep 17 00:00:00 2001 From: LudovicSchwob Date: Thu, 13 Feb 2025 10:06:51 +0100 Subject: [PATCH 350/507] New algorithm for cuts of a Poset --- src/sage/combinat/posets/posets.py | 48 ++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 173cbb872da..df33e9af532 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -8541,35 +8541,53 @@ def cuts(self): A cut is a subset `A` of ``self`` such that the set of lower bounds of the set of upper bounds of `A` is exactly `A`. - The cuts are computed here using the maximal independent sets in the - auxiliary graph defined as `P \times [0,1]` with an edge - from `(x, 0)` to `(y, 1)` if - and only if `x \not\geq_P y`. See the end of section 4 in [JRJ94]_. + The cuts are computed as the smallest family of subsets of P containing its + principal order filters, the whose set P and which is closed by intersection. EXAMPLES:: sage: P = posets.AntichainPoset(3) sage: Pc = P.cuts() sage: Pc # random - [frozenset({0}), + [frozenset({2}), + frozenset({1}), + frozenset({0}), frozenset(), - frozenset({0, 1, 2}), - frozenset({2}), - frozenset({1})] + frozenset({0, 1, 2})] sage: sorted(list(c) for c in Pc) [[], [0], [0, 1, 2], [1], [2]] + TESTS:: + + sage: P = Poset() + sage: P.cuts() + [frozenset()] + .. SEEALSO:: :meth:`completion_by_cuts` """ - from sage.graphs.graph import Graph - from sage.graphs.independent_sets import IndependentSets - auxg = Graph({(u, 0): [(v, 1) for v in self if not self.ge(u, v)] - for u in self}, format='dict_of_lists') - auxg.add_vertices([(v, 1) for v in self]) - return [frozenset([xa for xa, xb in c if xb == 0]) - for c in IndependentSets(auxg, maximal=True)] + C, C2 = [], [] + for x in P: + C.append(set(P.order_filter([x]))) + for i, c in enumerate(C): + for j in range(i + 1, len(C)): + I = c.intersection(C[j]) + if I not in C + C2: + C2.append(I) + while C2: + D = [] + for x in C: + for y in C2: + I = x.intersection(y) + if all(I not in X for X in [C, C2, D]): + D.append(I) + C.extend(C2) + C2 = D + S = set(P) + if S not in C: + C.append(S) + return [frozenset(x) for x in C] def completion_by_cuts(self): """ From 7ada96b58534168a537fbf817331f3b43a08a4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 13 Feb 2025 10:36:45 +0100 Subject: [PATCH 351/507] trying to enhance the correspondance --- src/sage/ext_data/magma/sage/basic.m | 2 +- src/sage/rings/padics/padic_base_leaves.py | 26 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 918e523e403..24ddb9bb2d9 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -155,7 +155,7 @@ intrinsic Sage(X::RngPad) -> MonStgElt, BoolElt if Type(prec) eq Infty then return Sprintf("Zp(%o, %o, 'relaxed')", Sage(Prime(X)), Sage(prec), false; else - return Sprintf("Zp(%o, %o, 'capped-rel')", Sage(Prime(X)), Sage(prec)), false; + return Sprintf("Zp(%o, %o, 'capped-abs')", Sage(Prime(X)), Sage(prec)), false; end if; end intrinsic; diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 878c54b748f..e0a7d4fceb3 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -304,19 +304,6 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) - def _magma_init_(self, magma): - """ - Conversion to magma. - - EXAMPLES:: - - sage: # optional - magma - sage: F = Qp(5,7,"capped-rel") - sage: magma(F) - 5-adic field mod 5^7 - """ - return f"pAdicRing({self.prime()},{self.precision_cap()})" - class pAdicRingCappedAbsolute(pAdicRingBaseGeneric, pAdicCappedAbsoluteRingGeneric): r""" @@ -417,6 +404,19 @@ def _convert_map_from_(self, R): from sage.rings.padics.padic_generic import ResidueLiftingMap return ResidueLiftingMap._create_(R, self) + def _magma_init_(self, magma): + """ + Conversion to magma. + + EXAMPLES:: + + sage: # optional - magma + sage: F = Qp(5,7,"capped-abs") + sage: magma(F) + 5-adic field mod 5^7 + """ + return f"pAdicRing({self.prime()},{self.precision_cap()})" + class pAdicRingFloatingPoint(pAdicRingBaseGeneric, pAdicFloatingPointRingGeneric): r""" From adbfc15205117a4173a999597f4893d6b2f10ecb Mon Sep 17 00:00:00 2001 From: LudovicSchwob Date: Thu, 13 Feb 2025 10:56:58 +0100 Subject: [PATCH 352/507] Fixing a mistake --- src/sage/combinat/posets/posets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index df33e9af532..31ab27c0799 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -8568,8 +8568,8 @@ def cuts(self): :meth:`completion_by_cuts` """ C, C2 = [], [] - for x in P: - C.append(set(P.order_filter([x]))) + for x in self: + C.append(set(self.order_filter([x]))) for i, c in enumerate(C): for j in range(i + 1, len(C)): I = c.intersection(C[j]) From 784dd6306542fee9e3d89775bdbd09054158d57a Mon Sep 17 00:00:00 2001 From: LudovicSchwob Date: Thu, 13 Feb 2025 11:24:01 +0100 Subject: [PATCH 353/507] Fixing an other mistake --- src/sage/combinat/posets/posets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 31ab27c0799..5fdfeed0057 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -8584,7 +8584,7 @@ def cuts(self): D.append(I) C.extend(C2) C2 = D - S = set(P) + S = set(self) if S not in C: C.append(S) return [frozenset(x) for x in C] From ba76a9019eda55891935dff72648a8a42af486b1 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:32:56 +0700 Subject: [PATCH 354/507] Fix some typo --- src/sage/rings/padics/factory.py | 4 ++-- src/sage/rings/polynomial/binary_form_reduce.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 9dce85e130f..c632b86600a 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1643,7 +1643,7 @@ class Zp_class(UniqueFactory): 1 + 2*5^2 + 5^3 The floating point case is similar to the fixed modulus type - in that elements do not trac their own precision. However, relative + in that elements do not track their own precision. However, relative precision is truncated with each operation rather than absolute precision. On the contrary, the lattice type tracks precision using lattices @@ -2196,7 +2196,7 @@ def Zq(q, prec=None, type='capped-rel', modulus=None, names=None, 2*3^2 + (2*a + 2)*3^3 The floating point case is similar to the fixed modulus type - in that elements do not trac their own precision. However, relative + in that elements do not track their own precision. However, relative precision is truncated with each operation rather than absolute precision. MODULUS: diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py index f56dfe459ac..56de0ec7199 100644 --- a/src/sage/rings/polynomial/binary_form_reduce.py +++ b/src/sage/rings/polynomial/binary_form_reduce.py @@ -192,7 +192,7 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): FM = f # for Julia's invariant else: # solve the minimization problem for 'true' covariant - CF = ComplexIntervalField(prec=prec) # keeps trac of our precision error + CF = ComplexIntervalField(prec=prec) # keeps track of our precision error z = CF(z) FM = F(list(mat * vector(R.gens()))).subs({R.gen(1): 1}).univariate_polynomial() from sage.rings.polynomial.complex_roots import complex_roots From 2393565d2d9c4aa14c84f74d87b50b23fb0d7666 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:36:57 +0700 Subject: [PATCH 355/507] Add a doctest for HomsetsCategory._make_named_class_key --- src/sage/categories/homsets.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/categories/homsets.py b/src/sage/categories/homsets.py index 73dc6be120c..a9b8ad9383e 100644 --- a/src/sage/categories/homsets.py +++ b/src/sage/categories/homsets.py @@ -163,6 +163,11 @@ def _make_named_class_key(self, name): - :meth:`CategoryWithParameters` - :meth:`CategoryWithParameters._make_named_class_key` + + TESTS:: + + sage: ModulesWithBasis(ZZ).Homsets()._make_named_class_key('parent_class') + """ return getattr(self.base_category(), name) From a052a1f0aa30285b91927b3d1019656ecb9932cf Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Thu, 13 Feb 2025 11:51:30 +0100 Subject: [PATCH 356/507] add of the function rank_support_of_vector --- src/sage/coding/linear_rank_metric.py | 104 +++++++++++++++++++++----- 1 file changed, 86 insertions(+), 18 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 97a37e96c9b..e45b1b1b0f7 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -91,6 +91,7 @@ AUTHORS: - Marketa Slukova (2019-08-16): initial version +- Camille Garnier and Rubén Muñoz--Bertrand (2024-02-13): added rank_support_of_vector, and corrected the documentation TESTS:: @@ -146,9 +147,9 @@ def to_matrix_representation(v, sub_field=None, basis=None): specified, it is the prime subfield `\GF{p}` of `\GF{q^m}` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator + of the multiplicative group of `\GF{q^m}`. + The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: @@ -198,9 +199,9 @@ def from_matrix_representation(w, base_field=None, basis=None): ``w``. - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - `\GF{q}`. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + `\GF{q}`. If not specified, given that `q = p^s`, let `\beta` be a generator + of the multiplicative group of `\GF{q^m}`. + The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: @@ -230,7 +231,7 @@ def rank_weight(c, sub_field=None, basis=None): Return the rank of ``c`` as a matrix over ``sub_field``. If ``c`` is a vector over some field `\GF{q^m}`, the function converts it - into a matrix over `\GF{q}`. + into a matrix over ``sub_field```. INPUT: @@ -241,8 +242,8 @@ def rank_weight(c, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + `1,\beta,\ldots,\beta^{sm-1}` be the basis that SageMath uses to + represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}` EXAMPLES:: @@ -278,9 +279,9 @@ def rank_distance(a, b, sub_field=None, basis=None): specified, it is the prime subfield `\GF{p}` of `\GF{q^m}` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator + of the multiplicative group of `\GF{q^m}`. + The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: @@ -379,9 +380,9 @@ def __init__(self, base_field, sub_field, length, default_encoder_name, - ``default_decoder_name`` -- the name of the default decoder of ``self`` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator + of the multiplicative group of `\GF{q^m}`. + The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES: @@ -587,6 +588,73 @@ def rank_weight_of_vector(self, word): 2 """ return rank_weight(word, self.sub_field()) + + def rank_support_of_vector(self, word, sub_field=None, basis=None): + r""" + Return the rank support of ``word`` over ``sub_field``, i.e. the vector space over + ``sub_field`` generated by its coefficients. + If ``word`` is a vector over some field `\GF{q^m}`, and ``sub_field`` is a subfield of + `\GF{q^m}`, the function converts it into a matrix over ``sub_field``, with + respect to the basis ``basis``. + + + INPUT: + + - ``word`` -- a vector over the ``base_field`` of ``self`` + + - ``sub_field`` -- (default: ``None``) a sub field of the ``base_field`` of ``self``; if not + specified, it is the prime subfield `\GF{p}` of the ``base_field`` of ``self`` + + - ``basis`` -- (default: ``None``) a basis of ``base_field`` of ``self`` as a vector space over + ``sub_field``. + + If not specified, given that `q = p^s`, let `\beta` be a generator of the multiplicative group + of `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: a = GF(64).gen() + sage: c = vector([a^4 + a^3 + 1, a^4 + a^3 + 1, a^4 + a^3 + a^2 + 1]) + sage: c in C + True + sage: C.rank_support_of_vector(c) + Vector space of degree 6 and dimension 2 over Finite Field of size 2 + Basis matrix: + [1 0 0 1 1 0] + [0 0 1 0 0 0] + + An example with a non canonical basis:: + + sage: K. = GF(2^3) + sage: G = Matrix(K, [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G) + sage: c = vector([a^2, a^2, 0]) + sage: basis = [a, a+1, a^2] + sage: C.rank_support_of_vector(c, basis=basis) + Vector space of degree 3 and dimension 1 over Finite Field of size 2 + Basis matrix: + [0 0 1] + + TESTS:: + + sage: C.rank_support_of_vector(a) + Traceback (most recent call last): + ... + TypeError: input must be a vector + + sage: C.rank_support_of_vector(c, GF(2^4)) + Traceback (most recent call last): + ... + TypeError: the input subfield Finite Field in z4 of size 2^4 is not a subfield of Finite Field in a of size 2^3 + """ + if not isinstance(word, Vector): + raise TypeError("input must be a vector") + if sub_field != None: + if self.base_field().degree() % sub_field.degree() != 0: + raise TypeError(f"the input subfield {sub_field} is not a subfield of {self.base_field()}") + return to_matrix_representation(word, sub_field, basis).column_module() def matrix_form_of_vector(self, word): r""" @@ -679,9 +747,9 @@ def __init__(self, generator, sub_field=None, basis=None): specified, it is the prime field of ``base_field`` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator + of the multiplicative group of `\GF{q^m}`. + The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: From d270b3f28b1ae03ac7d7371cc2b13a741faff1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Mu=C3=B1oz--Bertrand?= Date: Thu, 13 Feb 2025 14:25:08 +0100 Subject: [PATCH 357/507] Update documentation and condition --- src/sage/coding/linear_rank_metric.py | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index e45b1b1b0f7..62a1deb4b86 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -147,9 +147,9 @@ def to_matrix_representation(v, sub_field=None, basis=None): specified, it is the prime subfield `\GF{p}` of `\GF{q^m}` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator - of the multiplicative group of `\GF{q^m}`. - The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: @@ -199,9 +199,9 @@ def from_matrix_representation(w, base_field=None, basis=None): ``w``. - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - `\GF{q}`. If not specified, given that `q = p^s`, let `\beta` be a generator - of the multiplicative group of `\GF{q^m}`. - The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + `\GF{q}`. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: @@ -241,9 +241,9 @@ def rank_weight(c, sub_field=None, basis=None): specified, it is the prime subfield `\GF{p}` of `\GF{q^m}` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let - `1,\beta,\ldots,\beta^{sm-1}` be the basis that SageMath uses to - represent `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}` + ``sub_field``. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: @@ -279,9 +279,9 @@ def rank_distance(a, b, sub_field=None, basis=None): specified, it is the prime subfield `\GF{p}` of `\GF{q^m}` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator - of the multiplicative group of `\GF{q^m}`. - The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: @@ -380,9 +380,9 @@ def __init__(self, base_field, sub_field, length, default_encoder_name, - ``default_decoder_name`` -- the name of the default decoder of ``self`` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator - of the multiplicative group of `\GF{q^m}`. - The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES: @@ -588,29 +588,29 @@ def rank_weight_of_vector(self, word): 2 """ return rank_weight(word, self.sub_field()) - + def rank_support_of_vector(self, word, sub_field=None, basis=None): r""" - Return the rank support of ``word`` over ``sub_field``, i.e. the vector space over - ``sub_field`` generated by its coefficients. - If ``word`` is a vector over some field `\GF{q^m}`, and ``sub_field`` is a subfield of - `\GF{q^m}`, the function converts it into a matrix over ``sub_field``, with - respect to the basis ``basis``. + Return the rank support of ``word`` over ``sub_field``, i.e. the vector space over + ``sub_field`` generated by its coefficients. + If ``word`` is a vector over some field `\GF{q^m}`, and ``sub_field`` is a subfield of + `\GF{q^m}`, the function converts it into a matrix over ``sub_field``, with + respect to the basis ``basis``. INPUT: - - ``word`` -- a vector over the ``base_field`` of ``self`` + - ``word`` -- a vector over the ``base_field`` of ``self``. - - ``sub_field`` -- (default: ``None``) a sub field of the ``base_field`` of ``self``; if not - specified, it is the prime subfield `\GF{p}` of the ``base_field`` of ``self`` + - ``sub_field`` -- (default: ``None``) a sub field of the + ``base_field`` of ``self``; if not specified, it is the prime + subfield `\GF{p}` of the ``base_field`` of ``self``. + + - ``basis`` -- (default: ``None``) a basis of ``base_field`` of + ``self`` as a vector space over ``sub_field``. If not specified, + the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is + the generator of the multiplicative group of `\GF{q^m}` given by Sage. - - ``basis`` -- (default: ``None``) a basis of ``base_field`` of ``self`` as a vector space over - ``sub_field``. - - If not specified, given that `q = p^s`, let `\beta` be a generator of the multiplicative group - of `\GF{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. - EXAMPLES:: sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) @@ -636,9 +636,9 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): Vector space of degree 3 and dimension 1 over Finite Field of size 2 Basis matrix: [0 0 1] - + TESTS:: - + sage: C.rank_support_of_vector(a) Traceback (most recent call last): ... @@ -651,7 +651,7 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): """ if not isinstance(word, Vector): raise TypeError("input must be a vector") - if sub_field != None: + if sub_field is not None: if self.base_field().degree() % sub_field.degree() != 0: raise TypeError(f"the input subfield {sub_field} is not a subfield of {self.base_field()}") return to_matrix_representation(word, sub_field, basis).column_module() @@ -747,9 +747,9 @@ def __init__(self, generator, sub_field=None, basis=None): specified, it is the prime field of ``base_field`` - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over - ``sub_field``. If not specified, given that `q = p^s`, let `\beta` be a generator - of the multiplicative group of `\GF{q^m}`. - The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + ``sub_field``. If not specified, the default basis is + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the + multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: From 753a3a4e3fdb26d63faf3ccbf1dbc0a8da9563be Mon Sep 17 00:00:00 2001 From: LudovicSchwob Date: Thu, 13 Feb 2025 14:57:50 +0100 Subject: [PATCH 358/507] Adding one more doctest --- src/sage/combinat/posets/posets.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 5fdfeed0057..182d214ecc5 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -8542,7 +8542,7 @@ def cuts(self): bounds of the set of upper bounds of `A` is exactly `A`. The cuts are computed as the smallest family of subsets of P containing its - principal order filters, the whose set P and which is closed by intersection. + principal order filters, the whole set P and which is closed by intersection. EXAMPLES:: @@ -8562,6 +8562,22 @@ def cuts(self): sage: P = Poset() sage: P.cuts() [frozenset()] + sage: P = Poset({3: [4, 5, 7], 1: [2, 4, 6], 4: [], 0: [2, 5], 2: [7], 7: [], 5: [6], 6: []}) + sage: P.cuts() + [frozenset({3, 4, 5, 6, 7}), + frozenset({1, 2, 4, 6, 7}), + frozenset({4}), + frozenset({0, 2, 5, 6, 7}), + frozenset({2, 7}), + frozenset({7}), + frozenset({5, 6}), + frozenset({6}), + frozenset({4, 6, 7}), + frozenset({5, 6, 7}), + frozenset({2, 6, 7}), + frozenset(), + frozenset({6, 7}), + frozenset({0, 1, 2, 3, 4, 5, 6, 7})] .. SEEALSO:: From e2e9491f87def8a42f3c42ac74f5a6baef8369e2 Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Thu, 13 Feb 2025 15:05:42 +0100 Subject: [PATCH 359/507] correction of indentation --- src/sage/coding/linear_rank_metric.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 62a1deb4b86..46a71a2c64f 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -600,16 +600,17 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): INPUT: - - ``word`` -- a vector over the ``base_field`` of ``self``. + - ``word`` -- a vector over the ``base_field`` of ``self``. - - ``sub_field`` -- (default: ``None``) a sub field of the - ``base_field`` of ``self``; if not specified, it is the prime - subfield `\GF{p}` of the ``base_field`` of ``self``. + - ``sub_field`` -- (default: ``None``) a sub field of the + ``base_field`` of ``self``; if not specified, it is the prime + subfield `\GF{p}` of the ``base_field`` of ``self``. + + - ``basis`` -- (default: ``None``) a basis of ``base_field`` of + ``self`` as a vector space over ``sub_field``. If not specified, + the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is + the generator of the multiplicative group of `\GF{q^m}` given by Sage. - - ``basis`` -- (default: ``None``) a basis of ``base_field`` of - ``self`` as a vector space over ``sub_field``. If not specified, - the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is - the generator of the multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: From 763521d0971289c3f20a31af4063603d43ec884e Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 13 Feb 2025 21:19:18 +0700 Subject: [PATCH 360/507] Show test failures of ci-meson as annotations --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index ab073aae87c..ca7fcd60fc5 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -81,7 +81,7 @@ jobs: run: | # We don't install sage_setup, so don't try to test it rm -R ./src/sage_setup/ - ./sage -t --all -p4 + ./sage -t --all -p4 --format github - name: Upload log uses: actions/upload-artifact@v4.5.0 From d94fd8942587da666a730340bae75e00102d4d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Feb 2025 15:26:42 +0100 Subject: [PATCH 361/507] Do not recommend to set MAKE=make -jX Setting MAKE=make -jX can lead to an exponential growth of build processes (as noted during SD128.) It's better to rely on make's jobserver that is available on macOS and all relevant Linux distributions. We also recommend a sane default for MAKEFLAGS that people can just copy & paste. Unfortunately, a lot of non-developers of SageMath still have to build from source and they appreciate not having to think about these things and just being able to copy a good default value. --- README.md | 32 +++++++++++------------------ src/doc/en/installation/source.rst | 33 ++++++++++++++++++------------ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8a77ffe1b42..243e3c03868 100644 --- a/README.md +++ b/README.md @@ -334,26 +334,18 @@ in the Installation Guide. 11. Optional, but highly recommended: Set some environment variables to customize the build. - For example, the `MAKE` environment variable controls whether to - run several jobs in parallel. On a machine with 4 processors, say, - typing `export MAKE="make -j4"` will configure the build script to - perform a parallel compilation of Sage using 4 jobs. On some - powerful machines, you might even consider `-j16`, as building with - more jobs than CPU cores can speed things up further. - - Alternatively, the `MAKEFLAGS` environment variable can be used. - In this case, only provide the flag itself, for example - `export MAKEFLAGS="-j4"`. - - Note that the compilation may nonetheless uses a different number of - threads, because sometimes `ninja` is used. - Unfortunately, [there is no way to control number of jobs `ninja` uses - from environment variables](https://github.com/ninja-build/ninja/issues/1482). - See also https://github.com/sagemath/sage/issues/38950. - - If the [Meson build system](https://doc-release--sagemath.netlify.app/html/en/installation/meson) - is used, the number of jobs running in parallel passed to `meson compile` will be respected, - because everything are managed by `ninja`. + The `MAKEFLAGS` variable controls whether to run several jobs in parallel. + To saturate all the execution threads of your CPU, we recommend to run + `export MAKEFLAGS="-j$(nproc) -l$(nproc).5"` if you are on Linux, and + `export MAKEFLAGS="-j$(sysctl -n hw.ncpu) -l$(sysctl -n hw.ncpu).5"` if you + are on macOS. + + Note that the compilation may nonetheless use a different number of + processes, e.g., for parts that are built with `ninja` which automatically + decides on the amount of parallelity to use. In practice, you might + therefore see twice as many processes during the build process than your + CPU has execution threads. Unless your system is low on RAM, this should + not affect the time the compilation takes substantially. To reduce the terminal output during the build, type `export V=0`. (`V` stands for "verbosity".) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 12b1483dfa2..12ab938f0f3 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -669,8 +669,8 @@ Environment variables Sage uses several environment variables to control its build process. Most users won't need to set any of these: the build process just works on many platforms. -(Note though that setting :envvar:`MAKE`, as described below, can significantly -speed up the process.) +(Note though that setting :envvar:`MAKEFLAGS`, as described below, can +significantly speed up the process.) Building Sage involves building many packages, each of which has its own compilation instructions. @@ -680,19 +680,26 @@ Standard environment controlling the build process Here are some of the more commonly used variables affecting the build process: -.. envvar:: MAKE +.. envvar:: MAKEFLAGS - One useful setting for this variable when building Sage is - ``MAKE='make -jNUM'`` to tell the ``make`` program to run ``NUM`` jobs in - parallel when building. - Note that some Sage packages may not support this variable. + This variable can be set to tell the ``make`` program to build things in + parallel. Set it to ``-jNUM`` to run ``NUM`` jobs in parallel when building. + Add ``-lNUM`` to tell make not to spawn more processes when the load exceeds + ``NUM``. + + A good value for this variable is ``MAKEFLAGS="-j$(nproc) -l$(nproc).5"`` on + Linux and ``MAKEFLAGS="-j$(sysctl -n hw.ncpu) -l$(sysctl -n hw.ncpu).5"`` on + macOS. This instructs make to use all the execution threads of your CPU while + bounding the load if there are other processes generating load. If your + system does not have a lot of RAM, you might want to choose lower limits, if + you have lots of RAM, it can sometimes be beneficial to set these limits + slightly higher. + + Note that some parts of the SageMath build system do not respect this + variable, e.g., when ninja gets invoked, it figures out the number of + processes to use on its own so the number of processes and the system load + you see might exceed the number configured here. - Some people advise using more jobs than there are CPU cores, at least if the - system is not heavily loaded and has plenty of RAM; for example, a good - setting for ``NUM`` might be between 1 and 1.5 times the number of cores. - In addition, the ``-l`` option sets a load limit: ``MAKE='make -j4 -l5.5``, - for example, tells ``make`` to try to use four jobs, but to not start more - than one job if the system load average is above 5.5. See the manual page for GNU ``make``: `Command-line options `_ and `Parallel building From 2af2e49c2febb79559782003912d0cd7e535811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 13 Feb 2025 16:20:18 +0100 Subject: [PATCH 362/507] Remove recommendation of MAKE from the FAQ --- src/doc/en/faq/faq-usage.rst | 2 +- src/doc/it/faq/faq-usage.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 393a9c369e5..ca7cab3458b 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -70,7 +70,7 @@ Sage. The command .. CODE-BLOCK:: shell-session - $ export MAKE='make -j8' + $ export MAKEFLAGS='-j8' will enable 8 threads for parts of the build that support parallelism. Change the number 8 as appropriate to suit the number of diff --git a/src/doc/it/faq/faq-usage.rst b/src/doc/it/faq/faq-usage.rst index 677d1a24bc2..2d2b32352a0 100644 --- a/src/doc/it/faq/faq-usage.rst +++ b/src/doc/it/faq/faq-usage.rst @@ -70,7 +70,7 @@ questi prerequisiti come segue:: Se hai un sistema multiprocessore puoi scegliere una compilazione parallela di Sage. Il comando :: - export MAKE='make -j8' + export MAKEFLAGS='-j8' abiliterà 8 threads per quelle parti della compilazione che supportano il parallelismo. Al posto del numero 8 metti il numero di From 5f68d0baa289cfe4781ce18d892a5683adad0893 Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Thu, 13 Feb 2025 17:13:33 +0100 Subject: [PATCH 363/507] correction of documentation --- src/sage/coding/linear_rank_metric.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 46a71a2c64f..897275a17e3 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -603,14 +603,13 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): - ``word`` -- a vector over the ``base_field`` of ``self``. - ``sub_field`` -- (default: ``None``) a sub field of the - ``base_field`` of ``self``; if not specified, it is the prime - subfield `\GF{p}` of the ``base_field`` of ``self``. + ``base_field`` of ``self``; if not specified, it is the prime + subfield of `\GF{p}` the ``base_field`` of ``self``. - ``basis`` -- (default: ``None``) a basis of ``base_field`` of - ``self`` as a vector space over ``sub_field``. If not specified, - the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is - the generator of the multiplicative group of `\GF{q^m}` given by Sage. - + ``self`` as a vector space over ``sub_field``. If not specified, + the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is + the generator of the multiplicative group of `\GF{q^m}` given by Sage. EXAMPLES:: From 4c276c93847346fd95435f963037d359380bec8e Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Thu, 13 Feb 2025 17:15:21 +0100 Subject: [PATCH 364/507] incorporating minimal relation basis: documentation --- src/sage/matrix/matrix_polynomial_dense.pyx | 181 +++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 81cfe8163ce..4d9a26b5875 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -30,11 +30,13 @@ AUTHORS: - Vincent Neiger (2024-02-13): added basis_completion(), _is_basis_completion(), _basis_completion_via_reversed_approx(). + +- Vincent Neiger (2025-02-13): added minimal_relation_basis(). """ # **************************************************************************** # Copyright (C) 2016 Kwankyu Lee # Copyright (C) 2017 Johan Rosenkilde -# Copyright (C) 2018,2020,2021,2024 Vincent Neiger +# Copyright (C) 2018,2020,2021,2024,2025 Vincent Neiger # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -4087,6 +4089,183 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): column_indices.append(j) return P[:,column_indices] + def minimal_relation_basis(self, + mat, + shifts=None, + row_wise=True, + normal_form=False): + r""" + Return a relation basis in ``shifts``-ordered weak Popov form for this + polynomial matrix modulo ``mat``. + + If we work row-wise (which is the default), if `F` is an `m \times n` + polynomial matrix and `M` is a column reduced `n \times n` matrix, then + a relation basis `P` for `F` modulo `M` is a polynomial matrix whose + rows form a basis of the module of polynomial vectors `p` of size `m` + such that `p F = 0 \bmod M`, meaning that `p F` belongs to the module + generated by the rows of `M`. + + Such a basis `P` is an `m \times m` nonsingular matrix, which this + method computes in ``shifts``-ordered weak Popov form. If + ``normal_form`` is ``True``, then the output basis `P` is further + normalized: it is in the canonical ``shifts``-Popov form. + + It is guaranteed that the degree of the output basis is at most the + degree of determinant of ``mat``, independently of ``shifts``. + + Special cases include: + - minimal approximant bases, for which `M` is a diagonal of powers of + the variable (see :meth:`minimal_approximant_basis`, which may be + called directly for better performance), + - minimal interpolant bases, for which `M` is a diagonal of polynomials + that split into linear factors. + + If ``row_wise`` is ``False``, then, similarly, the columns of `P` form + a basis of the right-relations, i.e. of the module of column vectors + `p` such that `F p` is in the column space of `M`. + + An error is raised if the input dimensions are not sound: if working + row-wise (resp. column-wise), the matrix dimensions of ``mat`` must be + the number of columns (resp. rows) of ``self``, while the length of + ``shifts`` must be the number of rows (resp. columns) of ``self``. + There is no check about the reducedness of ``mat`` or about the + degree constraints concerning ``self`` and ``mat``. + + INPUT: + + - ``mat`` -- polynomial matrix + + - ``shifts`` -- (default: ``None``) list of integers; + ``None`` is interpreted as ``shifts=[0,...,0]`` + + - ``row_wise`` -- boolean (default: ``True``). If ``True`` then the + output basis is considered row-wise and operates on the left of + ``self``; ``mat`` must be column reduced and its column degrees must + strictly bound those of ``self`` entry-wise. Otherwise the output + basis is column-wise and operates on the right of ``self``; ``mat`` + must be row reduced and its row degrees must strictly bound those of + ``self`` entry-wise. + + - ``normal_form`` -- boolean (default: ``False``); if + ``True`` then the output basis is in ``shifts``-Popov form + + OUTPUT: a polynomial matrix + + EXAMPLES:: + + sage: pR. = GF(7)[] + + sage: order = [4, 3]; shifts = [-1, 2, 0] + sage: F = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], + ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], + ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) + sage: P = F.minimal_approximant_basis(order, shifts) + sage: P.is_minimal_approximant_basis(F, order, shifts) + True + + By default, the computed basis is not required to be in normal form + (and will not be except in rare special cases):: + + sage: P.is_minimal_approximant_basis(F, order, shifts, + ....: normal_form=True) + False + sage: P = F.minimal_approximant_basis(order, shifts, + ....: normal_form=True) + sage: P.is_minimal_approximant_basis(F, order, shifts, + ....: normal_form=True) + True + + If shifts are not specified, they are chosen as uniform `[0,\ldots,0]` + by default. Besides, if the orders are all the same, one can rather + give a single integer:: + + sage: (F.minimal_approximant_basis(3) == + ....: F.minimal_approximant_basis([3,3], shifts=None)) + True + + One can work column-wise by specifying ``row_wise=False``:: + + sage: P = F.minimal_approximant_basis([5,2,2], [0,1], + ....: row_wise=False) + sage: P.is_minimal_approximant_basis(F, [5,2,2], shifts=[0,1], + ....: row_wise=False) + True + sage: (F.minimal_approximant_basis(3, row_wise=True) == + ....: F.transpose().minimal_approximant_basis( + ....: 3, row_wise=False).transpose()) + True + + Errors are raised if the input dimensions are not sound:: + + sage: P = F.minimal_approximant_basis([4], shifts) + Traceback (most recent call last): + ... + ValueError: order length should be the column dimension + + sage: P = F.minimal_approximant_basis(order, [0,0,0,0]) + Traceback (most recent call last): + ... + ValueError: shifts length should be the row dimension + + An error is raised if order does not contain only positive integers:: + + sage: P = F.minimal_approximant_basis([1,0], shifts) + Traceback (most recent call last): + ... + ValueError: order should consist of positive integers + """ + m = self.nrows() + n = self.ncols() + + # set default shifts / check shifts dimension + if shifts is None: + shifts = [0] * m if row_wise else [0] * n + elif row_wise and len(shifts) != m: + raise ValueError('shifts length should be the row dimension') + elif (not row_wise) and len(shifts) != n: + raise ValueError('shifts length should be the column dimension') + + # check modulus dimension + if row_wise and mat.dimensions() != (n, n): + raise ValueError("order length should be the column dimension") + elif (not row_wise) and mat.dimensions() != (m, m): + raise ValueError("order length should be the row dimension") + + # compute approximant basis + # if required, normalize it into shifted Popov form + if row_wise: + P,rdeg = self._approximant_basis_iterative(order, shifts) + if normal_form: + # compute the list "- pivot degree" + # (since weak Popov, pivot degree is rdeg-shifts entrywise) + # Note: -deg(P[i,i]) = shifts[i] - rdeg[i] + degree_shifts = [shifts[i] - rdeg[i] for i in range(m)] + # compute approximant basis with that list as shifts + P,rdeg = self._approximant_basis_iterative(order, + degree_shifts) + # left-multiply by inverse of leading matrix + lmat = P.leading_matrix(shifts=degree_shifts) + P = lmat.inverse() * P + else: + P,rdeg = self.transpose()._approximant_basis_iterative(order, + shifts) + if normal_form: + # compute the list "- pivot degree" + # (since weak Popov, pivot degree is rdeg-shifts entrywise) + degree_shifts = [shifts[i] - rdeg[i] for i in range(n)] + # compute approximant basis with that list as shifts + P, rdeg = self.transpose()._approximant_basis_iterative( + order, degree_shifts) + P = P.transpose() + # right-multiply by inverse of leading matrix + lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False) + P = P * lmat.inverse() + else: + P = P.transpose() + + return P + + def _basis_completion_via_reversed_approx(self): r""" Return a Smith form-preserving nonsingular completion of a row basis of From 3f6a2614c280c61c37997ae2e376aad8b95b7d1d Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Thu, 13 Feb 2025 17:22:13 +0100 Subject: [PATCH 365/507] incorporating minimal relation basis: algo based on kernel basis --- src/sage/matrix/matrix_polynomial_dense.pyx | 51 ++++++++------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 4d9a26b5875..b6ad17c8d01 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4227,43 +4227,30 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # check modulus dimension if row_wise and mat.dimensions() != (n, n): - raise ValueError("order length should be the column dimension") + raise ValueError("modulus matrix dimensions must be the column dimension of self") elif (not row_wise) and mat.dimensions() != (m, m): - raise ValueError("order length should be the row dimension") + raise ValueError("modulus matrix dimensions must be the row dimension of self") - # compute approximant basis - # if required, normalize it into shifted Popov form + # compute extended shift for kernel basis computation + # -> for correctness, the constraint on the added part is that it + # must have maximum entry at most min(shifts) + min_shift = min(shifts) if row_wise: - P,rdeg = self._approximant_basis_iterative(order, shifts) - if normal_form: - # compute the list "- pivot degree" - # (since weak Popov, pivot degree is rdeg-shifts entrywise) - # Note: -deg(P[i,i]) = shifts[i] - rdeg[i] - degree_shifts = [shifts[i] - rdeg[i] for i in range(m)] - # compute approximant basis with that list as shifts - P,rdeg = self._approximant_basis_iterative(order, - degree_shifts) - # left-multiply by inverse of leading matrix - lmat = P.leading_matrix(shifts=degree_shifts) - P = lmat.inverse() * P + extended_shifts = [s - min_shift for s in shifts] + [0]*n + else + extended_shifts = [s - min_shift for s in shifts] + [0]*m + + # build matrix for kernel computation + if row_wise: + F = matrix.block([[self],[mat]]) else: - P,rdeg = self.transpose()._approximant_basis_iterative(order, - shifts) - if normal_form: - # compute the list "- pivot degree" - # (since weak Popov, pivot degree is rdeg-shifts entrywise) - degree_shifts = [shifts[i] - rdeg[i] for i in range(n)] - # compute approximant basis with that list as shifts - P, rdeg = self.transpose()._approximant_basis_iterative( - order, degree_shifts) - P = P.transpose() - # right-multiply by inverse of leading matrix - lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False) - P = P * lmat.inverse() - else: - P = P.transpose() + F = matrix.block([[self, mat]]) - return P + # compute shifted weak Popov kernel basis + K = A.minimal_kernel_basis(shifts=extended_shifts, normal_form=normal_form, row_wise=row_wise) + + # extract sought basis and return + return K[:m,:m] def _basis_completion_via_reversed_approx(self): From d29e58a28e1c155c8a6d3678c99abe44e4a0dd62 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Thu, 13 Feb 2025 18:03:51 +0100 Subject: [PATCH 366/507] incorporating minimal relation basis: add tests and fix documentation and code --- src/sage/matrix/matrix_polynomial_dense.pyx | 96 +++++++++------------ 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index b6ad17c8d01..d49495aed0f 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4106,14 +4106,14 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): generated by the rows of `M`. Such a basis `P` is an `m \times m` nonsingular matrix, which this - method computes in ``shifts``-ordered weak Popov form. If - ``normal_form`` is ``True``, then the output basis `P` is further - normalized: it is in the canonical ``shifts``-Popov form. + method computes in ``shifts``-ordered weak Popov form. It is + guaranteed that the degree of the output basis is at most the degree + of determinant of ``mat``, independently of ``shifts``. If + ``normal_form`` is ``True``, then this output basis `P` is further + normalized: it is the canonical ``shifts``-Popov basis. - It is guaranteed that the degree of the output basis is at most the - degree of determinant of ``mat``, independently of ``shifts``. + Here are two special cases. - Special cases include: - minimal approximant bases, for which `M` is a diagonal of powers of the variable (see :meth:`minimal_approximant_basis`, which may be called directly for better performance), @@ -4155,65 +4155,48 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: order = [4, 3]; shifts = [-1, 2, 0] - sage: F = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], + When M is a diagonal of powers of the variable, a relation basis is the + same as an approximant basis:: + + sage: mat = matrix.diagonal([x**4, x**3], sparse=False) + sage: shifts = [-1, 2, 0] + sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) - sage: P = F.minimal_approximant_basis(order, shifts) - sage: P.is_minimal_approximant_basis(F, order, shifts) - True - - By default, the computed basis is not required to be in normal form - (and will not be except in rare special cases):: - - sage: P.is_minimal_approximant_basis(F, order, shifts, - ....: normal_form=True) - False - sage: P = F.minimal_approximant_basis(order, shifts, - ....: normal_form=True) - sage: P.is_minimal_approximant_basis(F, order, shifts, - ....: normal_form=True) + sage: P_app = F.minimal_approximant_basis([4, 3], shifts, normal_form=True) + sage: P_rel = F.minimal_relation_basis(mat, shifts, normal_form=True) + sage: P_app == P_rel True - If shifts are not specified, they are chosen as uniform `[0,\ldots,0]` - by default. Besides, if the orders are all the same, one can rather - give a single integer:: + If ``self`` is the identity matrix, then relation bases are simply + matrices that are left-unimodularly equivalent to ``mat``:: - sage: (F.minimal_approximant_basis(3) == - ....: F.minimal_approximant_basis([3,3], shifts=None)) + sage: # mat is both row and column reduced + sage: mat = x**4 * 1 + matrix.random(pR, 3, 3, degree=3) + sage: F = matrix.identity(pR, 3) # cdeg(F) < cdeg(mat) + sage: P = F.minimal_relation_basis(mat, shifts=[0,1,2], normal_form=True) + sage: P == mat.popov_form(shifts=[0,1,2]) True - One can work column-wise by specifying ``row_wise=False``:: + One can consider column-wise relations; unspecified shift means taking + the uniform `[0,\ldots, 0]` shift:: - sage: P = F.minimal_approximant_basis([5,2,2], [0,1], - ....: row_wise=False) - sage: P.is_minimal_approximant_basis(F, [5,2,2], shifts=[0,1], - ....: row_wise=False) + sage: F = matrix.random(pR, 3, 5, degree=3) + sage: P = F.minimal_relation_basis(mat, row_wise=False) + sage: P.is_weak_popov(shifts=[0]*5, row_wise=False) True - sage: (F.minimal_approximant_basis(3, row_wise=True) == - ....: F.transpose().minimal_approximant_basis( - ....: 3, row_wise=False).transpose()) + sage: Q,R = (F*P).left_quo_rem(mat) # F*P = mat*Q + 0 + sage: R == 0 True - Errors are raised if the input dimensions are not sound:: - - sage: P = F.minimal_approximant_basis([4], shifts) - Traceback (most recent call last): - ... - ValueError: order length should be the column dimension - - sage: P = F.minimal_approximant_basis(order, [0,0,0,0]) - Traceback (most recent call last): - ... - ValueError: shifts length should be the row dimension - - An error is raised if order does not contain only positive integers:: + Unless requiring a normal form, the output basis will most often not be + the canonical one:: - sage: P = F.minimal_approximant_basis([1,0], shifts) - Traceback (most recent call last): - ... - ValueError: order should consist of positive integers + sage: P.is_popov(shifts=[0]*5, row_wise=False) + False """ + from sage.matrix.constructor import matrix # for matrix.block + m = self.nrows() n = self.ncols() @@ -4237,7 +4220,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): min_shift = min(shifts) if row_wise: extended_shifts = [s - min_shift for s in shifts] + [0]*n - else + else: extended_shifts = [s - min_shift for s in shifts] + [0]*m # build matrix for kernel computation @@ -4247,10 +4230,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): F = matrix.block([[self, mat]]) # compute shifted weak Popov kernel basis - K = A.minimal_kernel_basis(shifts=extended_shifts, normal_form=normal_form, row_wise=row_wise) + kbas = F.minimal_kernel_basis(shifts=extended_shifts, normal_form=normal_form, row_wise=row_wise) # extract sought basis and return - return K[:m,:m] + if row_wise: + return kbas[:m,:m] + else: + return kbas[:n,:n] def _basis_completion_via_reversed_approx(self): From 65dd442515295b8dfa09f6a2e3d302ddc321f9ee Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Thu, 13 Feb 2025 18:34:20 +0100 Subject: [PATCH 367/507] line_graph for multigraphs --- src/sage/graphs/line_graph.pyx | 107 +++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index e54caf52897..648d16531b0 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -263,21 +263,32 @@ def is_line_graph(g, certificate=False): return True -def line_graph(g, labels=True): +def line_graph(g, labels=True, origlabels=False): """ - Return the line graph of the (di)graph ``g``. + Return the line graph of the (di)graph ``g`` (multiedges and loops allowed). INPUT: - ``labels`` -- boolean (default: ``True``); whether edge labels should be taken in consideration. If ``labels=True``, the vertices of the line graph - will be triples ``(u,v,label)``, and pairs of vertices otherwise. - - The line graph of an undirected graph G is an undirected graph H such that - the vertices of H are the edges of G and two vertices e and f of H are + will be triples ``(u,v,label)``, and pairs of vertices otherwise. In case + of multiple edges, the vertices of the line graph will be triples + ``(u,v,an integer)``. + + - ``origlabels`` -- boolean (default: ``False``); wether edge labels should + be stored or not. If g has multiple edges, if ``origlabels=True``, the + method returns a list the first element of which is the line-graph of g + and the second element is a dictionary {vertex of the line-graph: label of + the corresponding original edge}. If ``origlabels=False``, the method + returns only the line-graph. + + The line graph of an undirected graph G is an undirected simple graph H such + that the vertices of H are the edges of G and two vertices e and f of H are adjacent if e and f share a common vertex in G. In other words, an edge in H represents a path of length 2 in G. + Loops are not adjacent to themselves. + The line graph of a directed graph G is a directed graph H such that the vertices of H are the edges of G and two vertices e and f of H are adjacent if e and f share a common vertex in G and the terminal vertex of e is the @@ -311,7 +322,7 @@ def line_graph(g, labels=True): (1, 2, None), (1, 3, None), (2, 3, None)] - sage: h.am() # needs sage.modules + sage: h.am() # needs sage.modules [0 1 1 1 1 0] [1 0 1 1 0 1] [1 1 0 0 1 1] @@ -321,7 +332,7 @@ def line_graph(g, labels=True): sage: h2 = g.line_graph(labels=False) sage: h2.vertices(sort=True) [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] - sage: h2.am() == h.am() # needs sage.modules + sage: h2.am() == h.am() # needs sage.modules True sage: g = DiGraph([[1..4], lambda i,j: i < j]) sage: h = g.line_graph() @@ -338,6 +349,40 @@ def line_graph(g, labels=True): ((1, 3, None), (3, 4, None), None), ((2, 3, None), (3, 4, None), None)] + Examples with multiple edges:: + + sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph() + sage: L.edges() + [((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None), + ((0, 1, 0), (1, 2, 2), None)] + sage: G = Graph([(0,1),(0,1,'a'),(0,1,'b'),(0,2),(1,2,'c')], + ....: multiedges=True) + sage: L = G.line_graph(False,True) + sage: L[0].edges() + [((0, 1, 1), (0, 1, 2), None), ((0, 1, 0), (0, 1, 2), None), + ((0, 1, 2), (0, 2, 3), None), ((0, 1, 2), (1, 2, 4), None), ((0, 1, 0), + (0, 1, 1), None), ((0, 1, 1), (0, 2, 3), None), ((0, 1, 1), + (1, 2, 4), None), ((0, 1, 0), (0, 2, 3), None), ((0, 1, 0), + (1, 2, 4), None), ((0, 2, 3), (1, 2, 4), None)] + sage: L[1] + {(0, 1, 0): None, + (0, 1, 1): 'a', + (0, 1, 2): 'b', + (0, 2, 3): None, + (1, 2, 4): 'c'} + sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True) + sage: g.line_graph().edges() + [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] + + An example with a loop:: + + sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True) + sage: L = g.line_graph() + sage: L.edges() + [((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None), + ((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None), + ((0, 2, None), (1, 2, None), None)] + TESTS: :issue:`13787`:: @@ -351,17 +396,36 @@ def line_graph(g, labels=True): """ cdef dict conflicts = {} cdef list elist = [] + cdef dict origlabels_dic = {} # stores original labels of edges in case of multiple edges - g._scream_if_not_simple() - if g._directed: + multiple = g.has_multiple_edges() + if multiple: + labels = True + + h = g.copy() + + # replace labels of edges of g with integers in range(len(g.edges())) in order to distinguish multiple edges. + if multiple: + for i, e in enumerate(h.edges()): + f = (e[0], e[1], i) + h.delete_edge(e) + h.add_edge(f) + if origlabels: + origlabels_dic[f] = e[2] + + if h._directed: from sage.graphs.digraph import DiGraph G = DiGraph() - G.add_vertices(g.edge_iterator(labels=labels)) - for v in g: + G.add_vertices(h.edge_iterator(labels=labels)) + for v in h: # Connect appropriate incident edges of the vertex v - G.add_edges((e, f) for e in g.incoming_edge_iterator(v, labels=labels) - for f in g.outgoing_edge_iterator(v, labels=labels)) - return G + G.add_edges((e, f) for e in h.incoming_edge_iterator(v, labels=labels) + for f in h.outgoing_edge_iterator(v, labels=labels)) + if origlabels and multiple: + return [G, origlabels_dic] + else: + return G + from sage.graphs.graph import Graph G = Graph() @@ -375,7 +439,7 @@ def line_graph(g, labels=True): # pair in the dictionary of conflicts # 1) List of vertices in the line graph - for e in g.edge_iterator(labels=labels): + for e in h.edge_iterator(labels=labels): if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -389,11 +453,11 @@ def line_graph(g, labels=True): G.add_vertices(elist) # 2) adjacencies in the line graph - for v in g: + for v in h: elist = [] # Add the edge to the list, according to hashes, as previously - for e in g.edge_iterator(v, labels=labels): + for e in h.edge_iterator(v, labels=labels): # iterates over the edges incident to v if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -402,12 +466,17 @@ def line_graph(g, labels=True): elist.append(conflicts[e]) # All pairs of elements in elist are edges of the line graph + # if g has multiple edges, some pairs appear more than once but as G is defined as simple, + # the corresponding edges are not added as multiedges (as it should be). while elist: x = elist.pop() for y in elist: G.add_edge(x, y) - return G + if origlabels and multiple: + return [G, origlabels_dic] + else: + return G def root_graph(g, verbose=False): From cf997285fc2d24036b1e486f444f6fc44dc54f0a Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Thu, 13 Feb 2025 18:51:37 +0100 Subject: [PATCH 368/507] blank line removed --- src/sage/graphs/line_graph.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index 648d16531b0..57547206f02 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -426,7 +426,6 @@ def line_graph(g, labels=True, origlabels=False): else: return G - from sage.graphs.graph import Graph G = Graph() From 3cb417405c44fa484be4031356ff8f64252a0891 Mon Sep 17 00:00:00 2001 From: JP Labbe Date: Thu, 13 Feb 2025 12:53:34 -0500 Subject: [PATCH 369/507] Update fan.py Corrected typo --- src/sage/geometry/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index c9f83337aae..edd28e9576b 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2444,7 +2444,7 @@ def is_polytopal(self): Check if ``self`` is the normal fan of a polytope. A rational polyhedral fan is *polytopal* if it is the normal fan of a - polytope. This is also called *regular*, or provide a *coherent* + polytope. This is also called *regular*, or provides a *coherent* subdivision or leads to a *projective* toric variety. OUTPUT: ``True`` if ``self`` is polytopal and ``False`` otherwise From e40fef78f8d6e04c109a387ea4be9ddf2a332611 Mon Sep 17 00:00:00 2001 From: Jean-Philippe/Thinkbook Date: Thu, 13 Feb 2025 16:03:40 -0500 Subject: [PATCH 370/507] More fixes from review --- src/sage/geometry/fan.py | 9 +++++++++ src/sage/geometry/polyhedron/base5.py | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index edd28e9576b..e5946f98fa6 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2472,6 +2472,15 @@ def is_polytopal(self): sage: mother(epsilon).is_polytopal() True + TESTS:: + + sage: cone = Cone([(1,1), (2,1)]) + sage: F = Fan([cone]) + sage: F.is_polytopal() + Traceback (most recent call last): + ... + ValueError: to be polytopal, the fan should be complete + .. SEEALSO:: :meth:`is_projective`. diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index 0367bdebaad..64862bf28e0 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -664,7 +664,7 @@ def deformation_cone(self): Return the deformation cone of ``self``. Let `P` be a `d`-polytope in `\RR^r` with `n` facets. The deformation - cone is a polyhedron in `\RR^n` who points are the right-hand side `b` + cone is a polyhedron in `\RR^n` whose points are the right-hand side `b` in `Ax\leq b` where `A` is the matrix of facet normals of ``self``, so that the resulting polytope has a normal fan which is a coarsening of the normal fan of ``self``. @@ -707,15 +707,15 @@ def deformation_cone(self): REFERENCES: - For more information, see Section 5.4 of [DLRS2010]_ and Section - 2.2 of [ACEP2020]. + For more information, see Section 5.4 of [DLRS2010]_ and Section + 2.2 of [ACEP2020]. """ from .constructor import Polyhedron m = matrix([ineq.A() for ineq in self.Hrepresentation()]) m = m.transpose() m_ker = m.right_kernel_matrix(basis='computed') gale = tuple(m_ker.columns()) - collection = [f.ambient_H_indices() for f in self.faces(0)] + collection = (f.ambient_H_indices() for f in self.faces(0)) n = len(gale) c = None for cone_indices in collection: From 8c21c70984743da21e995dd40780c7d3e6bdde3b Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Thu, 13 Feb 2025 22:27:19 +0100 Subject: [PATCH 371/507] small note in comment about correctness of the choice of shift --- src/sage/matrix/matrix_polynomial_dense.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index d49495aed0f..e45a0a5e8ed 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4217,6 +4217,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # compute extended shift for kernel basis computation # -> for correctness, the constraint on the added part is that it # must have maximum entry at most min(shifts) + # [see Lemma 4.2, Neiger-Vu, Computing Canonical Bases of Modules of + # Univariate Relations, Proc. ISSAC 2017] min_shift = min(shifts) if row_wise: extended_shifts = [s - min_shift for s in shifts] + [0]*n From 2a9328a4ccdf79f1186eefb0af30d04b62f2a659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 14 Feb 2025 09:27:20 +0100 Subject: [PATCH 372/507] some details in fan.py --- src/sage/geometry/fan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index e5946f98fa6..fc004dd88ac 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -2439,7 +2439,7 @@ def Gale_transform(self): m = m.augment(matrix(ZZ, m.nrows(), 1, [1] * m.nrows())) return matrix(ZZ, m.integer_kernel().matrix()) - def is_polytopal(self): + def is_polytopal(self) -> bool: r""" Check if ``self`` is the normal fan of a polytope. @@ -2491,9 +2491,9 @@ def is_polytopal(self): from sage.geometry.polyhedron.constructor import Polyhedron pc = PointConfiguration(self.rays()) v_pc = [tuple(p) for p in pc] - pc_to_indices = {tuple(p):i for (i,p) in enumerate(pc)} - indices_to_vr = [tuple(r) for r in self.rays()] - cone_indices = [cone.ambient_ray_indices() for cone in self.generating_cones()] + pc_to_indices = {tuple(p):i for i, p in enumerate(pc)} + indices_to_vr = (tuple(r) for r in self.rays()) + cone_indices = (cone.ambient_ray_indices() for cone in self.generating_cones()) translator = [pc_to_indices[t] for t in indices_to_vr] translated_cone_indices = [[translator[i] for i in ci] for ci in cone_indices] dc_pc = pc.deformation_cone(translated_cone_indices) From a99b6a365af29a5951ffcd1563f1403bc087e11d Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Fri, 14 Feb 2025 09:39:44 +0100 Subject: [PATCH 373/507] correction of documentation --- src/sage/coding/linear_rank_metric.py | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 897275a17e3..8ab3f611e7c 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -91,7 +91,7 @@ AUTHORS: - Marketa Slukova (2019-08-16): initial version -- Camille Garnier and Rubén Muñoz--Bertrand (2024-02-13): added rank_support_of_vector, and corrected the documentation +- Camille Garnier and Rubén Muñoz-\-Bertrand (2024-02-13): added rank_support_of_vector, and corrected the documentation TESTS:: @@ -148,8 +148,8 @@ def to_matrix_representation(v, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of `\GF{q^m}` + given by SageMath. EXAMPLES:: @@ -200,9 +200,9 @@ def from_matrix_representation(w, base_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over `\GF{q}`. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. - + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator + of `\GF{q^m}` given by SageMath. + EXAMPLES:: sage: from sage.coding.linear_rank_metric import from_matrix_representation @@ -242,8 +242,8 @@ def rank_weight(c, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator + of `\GF{q^m}` given by SageMath. EXAMPLES:: @@ -280,8 +280,8 @@ def rank_distance(a, b, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator + of `\GF{q^m}` given by SageMath. EXAMPLES:: @@ -381,8 +381,8 @@ def __init__(self, base_field, sub_field, length, default_encoder_name, - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator + of `\GF{q^m}` given by SageMath. EXAMPLES: @@ -609,7 +609,7 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of ``base_field`` of ``self`` as a vector space over ``sub_field``. If not specified, the default basis is `1,\beta,\ldots,\beta^{m-1}`, where `\beta` is - the generator of the multiplicative group of `\GF{q^m}` given by Sage. + the generator of `\GF{q^m}` given by SageMath. EXAMPLES:: @@ -748,8 +748,8 @@ def __init__(self, generator, sub_field=None, basis=None): - ``basis`` -- (default: ``None``) a basis of `\GF{q^m}` as a vector space over ``sub_field``. If not specified, the default basis is - `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of the - multiplicative group of `\GF{q^m}` given by Sage. + `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator `\GF{q^m}` + given by SageMath. EXAMPLES:: From 76b71cf9916b8d44b01ec83f9bf4bba60f6766b8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 14 Feb 2025 09:42:06 +0100 Subject: [PATCH 374/507] add new files in meson + add a small comment in documentation --- src/sage/modules/free_module.py | 3 ++- src/sage/modules/meson.build | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index a077baf30da..e9e3dfe5a0b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3171,7 +3171,8 @@ def pseudohom(self, f, twist, codomain=None, side="left"): - ``twist`` -- the twisting morphism or the twisting derivation (if a derivation is given, the corresponding morphism `\theta` - is automatically infered) + is automatically infered; + see also :class:`sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`) - ``codomain`` -- (default: ``None``) the codomain of the pseudo morphisms; if ``None``, the codomain is the same than the domain diff --git a/src/sage/modules/meson.build b/src/sage/modules/meson.build index a5c78e98633..2edb6b1f10a 100644 --- a/src/sage/modules/meson.build +++ b/src/sage/modules/meson.build @@ -10,6 +10,8 @@ py.install_sources( 'free_module_homspace.py', 'free_module_integer.py', 'free_module_morphism.py', + 'free_module_pseudohomspace.py', + 'free_module_pseudomorphism.py', 'free_quadratic_module.py', 'free_quadratic_module_integer_symmetric.py', 'matrix_morphism.py', From d44d4794142b1c7fb2dbf3d3a240f44021863480 Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Fri, 14 Feb 2025 09:46:13 +0100 Subject: [PATCH 375/507] correction of documentation --- src/sage/coding/linear_rank_metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 8ab3f611e7c..1f85664f5c0 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -202,7 +202,7 @@ def from_matrix_representation(w, base_field=None, basis=None): `\GF{q}`. If not specified, the default basis is `1,\beta,\ldots,\beta^{m-1}` where `\beta` is the generator of `\GF{q^m}` given by SageMath. - + EXAMPLES:: sage: from sage.coding.linear_rank_metric import from_matrix_representation From f66a0053e089cbd05f33f6c2b85778e3e0b7d780 Mon Sep 17 00:00:00 2001 From: camille-garnier Date: Fri, 14 Feb 2025 11:05:45 +0100 Subject: [PATCH 376/507] Update src/sage/coding/linear_rank_metric.py Co-authored-by: Xavier Caruso --- src/sage/coding/linear_rank_metric.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 1f85664f5c0..99865a3ef44 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -649,8 +649,7 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): ... TypeError: the input subfield Finite Field in z4 of size 2^4 is not a subfield of Finite Field in a of size 2^3 """ - if not isinstance(word, Vector): - raise TypeError("input must be a vector") + word = self.ambient_space()(word) if sub_field is not None: if self.base_field().degree() % sub_field.degree() != 0: raise TypeError(f"the input subfield {sub_field} is not a subfield of {self.base_field()}") From fc04aab0d294ec7dda33cca3d784c93e9763e6f6 Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Fri, 14 Feb 2025 11:26:11 +0100 Subject: [PATCH 377/507] correction of documentation --- src/sage/coding/linear_rank_metric.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 99865a3ef44..f7ce79c822c 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -638,12 +638,7 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): [0 0 1] TESTS:: - - sage: C.rank_support_of_vector(a) - Traceback (most recent call last): - ... - TypeError: input must be a vector - + sage: C.rank_support_of_vector(c, GF(2^4)) Traceback (most recent call last): ... From b8c384a1142376169ab19e1addf4f68abfdd302a Mon Sep 17 00:00:00 2001 From: Camille Garnier Date: Fri, 14 Feb 2025 11:35:49 +0100 Subject: [PATCH 378/507] correction of documentation --- src/sage/coding/linear_rank_metric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index f7ce79c822c..e25d326cc8b 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -638,7 +638,7 @@ def rank_support_of_vector(self, word, sub_field=None, basis=None): [0 0 1] TESTS:: - + sage: C.rank_support_of_vector(c, GF(2^4)) Traceback (most recent call last): ... From 92bc9948fedcd4d615f367b706a786e7cbe98c6a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 14 Feb 2025 20:44:20 +0700 Subject: [PATCH 379/507] Reformat meson.build file to satisfy update-meson --- src/sage/data_structures/meson.build | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/data_structures/meson.build b/src/sage/data_structures/meson.build index 8c100328378..3cc243fe62a 100644 --- a/src/sage/data_structures/meson.build +++ b/src/sage/data_structures/meson.build @@ -42,9 +42,7 @@ foreach name, pyx : extension_data ) endforeach -extension_data_cpp = { - 'pairing_heap' : files('pairing_heap.pyx'), -} +extension_data_cpp = {'pairing_heap' : files('pairing_heap.pyx')} foreach name, pyx : extension_data_cpp py.extension_module( From 6aee4983263425ab17a8050f199e3ae42499f7d4 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 14 Feb 2025 22:15:28 +0700 Subject: [PATCH 380/507] Apply suggestions --- src/sage/misc/sageinspect.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 9066671f6f9..6fc0e29551f 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -1329,12 +1329,8 @@ def sage_getfile(obj): return sage_getfile(obj.func) return sage_getfile(obj.__class__) # inspect.getabsfile(obj.__class__) else: - try: - objinit = obj.__init__ - except AttributeError: - pass - else: - pos = _extract_embedded_position(_sage_getdoc_unformatted(objinit)) + if hasattr(obj, '__init__'): + pos = _extract_embedded_position(_sage_getdoc_unformatted(obj.__init__)) if pos is not None: (_, filename, _) = pos return filename @@ -1347,6 +1343,7 @@ def sage_getfile(obj): for suffix in import_machinery.EXTENSION_SUFFIXES: if sourcefile.endswith(suffix): # TODO: the following is incorrect in meson editable install + # because the build is out-of-tree, # but as long as either the class or its __init__ method has a # docstring, _sage_getdoc_unformatted should return correct result # see https://github.com/mesonbuild/meson-python/issues/723 @@ -2370,12 +2367,8 @@ class Element: try: return inspect.getsourcelines(obj) except (OSError, TypeError) as err: - try: - objinit = obj.__init__ - except AttributeError: - pass - else: - d = _sage_getdoc_unformatted(objinit) + if hasattr(obj, '__init__'): + d = _sage_getdoc_unformatted(obj.__init__) pos = _extract_embedded_position(d) if pos is None: if inspect.isclass(obj): From 82a439a9ec694c19022808a67cf47732a4a5379a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 14 Feb 2025 20:51:49 +0700 Subject: [PATCH 381/507] Test on CI that update-meson is properly ran --- .github/workflows/ci-meson.yml | 25 +++++++++++++++++++++++++ .gitignore | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index ab073aae87c..13ae8d9dc92 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -72,6 +72,23 @@ jobs: # Use --no-deps and pip check below to verify that all necessary dependencies are installed via conda pip install --no-build-isolation --no-deps --config-settings=builddir=builddir . -v + - name: Check update-meson + # this step must be after build, because meson.build creates a number of __init__.py files + # that is needed to make tools/update-meson.py run correctly + shell: bash -l {0} + id: check_update_meson + run: | + python3 tools/update-meson.py + make test-git-no-uncommitted-changes + continue-on-error: true + + - name: Show files changed by update-meson + if: ${{ steps.check_update_meson.outcome == 'failure' }} + shell: bash -l {0} + run: | + git status + git diff + - name: Verify dependencies shell: bash -l {0} run: pip check @@ -83,6 +100,14 @@ jobs: rm -R ./src/sage_setup/ ./sage -t --all -p4 + - name: Report update-meson failure + if: ${{ steps.check_update_meson.outcome == 'failure' }} + shell: bash -l {0} + run: | + # Please see step 'Show files changed by update-meson' above and apply the changes, + # or run tools/update-meson.py locally + false + - name: Upload log uses: actions/upload-artifact@v4.5.0 if: failure() diff --git a/.gitignore b/.gitignore index b8bfc364a26..c0a86090cad 100644 --- a/.gitignore +++ b/.gitignore @@ -467,3 +467,8 @@ src/sage/libs/mpfr/__init__.py src/sage/libs/mpc/__init__.py src/sage/calculus/transforms/__init__.py src/sage/calculus/__init__.py + +# Temporary files generated by Meson CI (needed to make test pass because +# ci-meson.yml runs a `make test-git-no-uncommitted-changes` step) +/.ccache +/setup-miniconda-patched-environment-*.yml From 56f350c839d004646ee0a6ede9d671248b4326fb Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:37:54 +0700 Subject: [PATCH 382/507] Handle backslash in flint reader --- src/sage_setup/autogen/flint/reader.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage_setup/autogen/flint/reader.py b/src/sage_setup/autogen/flint/reader.py index 671fe5c491d..3673b330d96 100644 --- a/src/sage_setup/autogen/flint/reader.py +++ b/src/sage_setup/autogen/flint/reader.py @@ -176,11 +176,16 @@ def process_line(self): line = self.lines[self.i] if line.startswith('.. function::'): self.add_declaration() - if line[13] != ' ': + line_rest = line.removeprefix('.. function::') + if not line_rest.startswith(' '): print('Warning: no space {}'.format(line)) - self.signatures.append(line[13:].strip()) self.state = self.FUNCTION_DECLARATION self.i += 1 + signature = line_rest.strip() + while signature.endswith('\\'): + signature = signature.removesuffix('\\').strip() + ' ' + self.lines[self.i].strip() + self.i += 1 + self.signatures.append(signature) elif line.startswith('.. macro::'): self.add_declaration() if line[10] != ' ': From a787dec08f032abad8f476e0261e79ab0cc5992d Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:27:46 +0700 Subject: [PATCH 383/507] Update template files --- .../autogen/flint/templates/flint_sage.pyx.template | 2 +- .../autogen/flint/templates/types.pxd.template | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template index b77c1a764aa..a553d99d33c 100644 --- a/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template +++ b/src/sage_setup/autogen/flint/templates/flint_sage.pyx.template @@ -12,7 +12,7 @@ Import this module:: sage: import sage.libs.flint.flint_sage -We verify that :trac:`6919` is correctly fixed:: +We verify that :issue:`6919` is correctly fixed:: sage: R. = PolynomialRing(ZZ) sage: A = 2^(2^17+2^15) diff --git a/src/sage_setup/autogen/flint/templates/types.pxd.template b/src/sage_setup/autogen/flint/templates/types.pxd.template index ae6bd308f79..4b9d982c7fa 100644 --- a/src/sage_setup/autogen/flint/templates/types.pxd.template +++ b/src/sage_setup/autogen/flint/templates/types.pxd.template @@ -24,6 +24,9 @@ ctypedef mp_limb_t ulong ctypedef mp_limb_signed_t slong ctypedef mp_limb_t flint_bitcnt_t +# New in flint 3.2.0-rc1 +ctypedef mp_ptr nn_ptr +ctypedef mp_srcptr nn_srcptr cdef extern from "flint_wrap.h": # flint/fmpz.h @@ -269,6 +272,16 @@ cdef extern from "flint_wrap.h": ctypedef struct flint_rand_s: pass ctypedef flint_rand_s flint_rand_t[1] + ctypedef enum flint_err_t: + # flint_autogen.py does not support parsing .. enum:: yet + FLINT_ERROR + FLINT_OVERFLOW + FLINT_IMPINV + FLINT_DOMERR + FLINT_DIVZERO + FLINT_EXPOF + FLINT_INEXACT + FLINT_TEST_FAIL cdef long FLINT_BITS cdef long FLINT_D_BITS From 5ce77b66439ac47f7da0b7fcdf41f67e5549947f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 15 Feb 2025 08:29:47 +0100 Subject: [PATCH 384/507] explain that equations in the error messages may be printed in random order --- src/sage/rings/lazy_series_ring.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index d601b73d358..a3ef6705093 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -799,11 +799,21 @@ def define_implicitly(self, series, equations, max_lookahead=1): + Let us now try to only specify the degree 0 and degree 1 + components. We will see that this is still not enough to + remove the ambiguity, so an error is raised. However, we + will see that the dependence on ``series[1]`` disappears. + The equation which has no unique solution is now + ``6*series[3] + (-2*x - 2*y)*series[2] + (x*y*f1) == 0``.:: + sage: F = L.undefined() sage: L.define_implicitly([(F, [0, f1])], [F(2*z) - (1+exp(x*z)+exp(y*z))*F - exp((x+y)*z)*F(-z)]) - sage: F # random + sage: F + coefficient [3]: ... == 0> + + (Note that the order of summands of the equation in the error + message is not deterministic.) Laurent series examples:: From dc38269c5490a007aaba7d8122ec5dd8fe00a5a4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 15 Feb 2025 08:45:05 +0100 Subject: [PATCH 385/507] explain rationale behind convention how monomials are represented --- src/sage/rings/semirings/tropical_mpolynomial.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index 225435ba488..d5f5a9d2e43 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -653,12 +653,17 @@ def _repr_(self): r""" Return a string representation of ``self``. + Note that ``x`` equals ``0*x``, which is different from + ``1*x``. Therefore, we represent monomials always together + with their coefficients, to avoid confusion. + EXAMPLES:: sage: T = TropicalSemiring(QQ) sage: R. = PolynomialRing(T) sage: x + R(-1)*y + R(-3) 0*x + (-1)*y + (-3) + """ if not self.monomial_coefficients(): return str(self.parent().base().zero()) From f89cb8a7b525485eb573ca5ce295eee116ad902d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 15 Feb 2025 09:03:11 +0100 Subject: [PATCH 386/507] fix doctest in doc --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 2a039fb920b..31a39a94c8a 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -132,7 +132,6 @@ This base class provides a lot more methods than a general parent:: 'ngens', 'one', 'order', - 'random_element', 'zero', 'zeta', 'zeta_order'] From 7438eb96d30d0724118e2d0252829bc097d2a6d3 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 13 Nov 2024 14:46:51 +0000 Subject: [PATCH 387/507] #38960-eclib-upgrade-20241112 --- build/pkgs/eclib/SPKG.rst | 2 +- build/pkgs/eclib/checksums.ini | 4 ++-- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/SPKG.rst b/build/pkgs/eclib/SPKG.rst index 5627fdcb57c..ac8b27b3606 100644 --- a/build/pkgs/eclib/SPKG.rst +++ b/build/pkgs/eclib/SPKG.rst @@ -30,5 +30,5 @@ Upstream Contact - Author: John Cremona - Email: john.cremona@gmail.com - Website: - http://homepages.warwick.ac.uk/staff/J.E.Cremona/mwrank/index.html + https://johncremona.github.io/mwrank/index.html - Repository: https://github.com/JohnCremona/eclib diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index fde4faaee15..b046783341d 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ tarball=eclib-VERSION.tar.bz2 -sha1=3028ac95e1b76699f5f9e871ac706cda363ab842 -sha256=32d116a3e359b0de4f6486c2bb6188bb8b553c8b833f618cc2596484e8b6145a +sha1=749e4fda3660006a9459f129148d05ba482daa29 +sha256=30765c27ca1420141f83517897119d0185fea9b31132392170ddae40b060e46f upstream_url=https://github.com/JohnCremona/eclib/releases/download/vVERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 190b92f716b..353869c3cba 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20231212 +20241112 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index ba5c22fa090..23771dad1bd 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl use existing eclib only if the version reported by pkg-config is recent enough - m4_pushdef([SAGE_ECLIB_VER],["20231212"]) + m4_pushdef([SAGE_ECLIB_VER],["20241112"]) PKG_CHECK_MODULES([ECLIB], [eclib >= SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From eaef4ae0cfb87c7461b51dacc634e72a9db58e41 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Tue, 19 Nov 2024 13:51:49 +0000 Subject: [PATCH 388/507] #38960 fix eclib interface after upgrade --- src/sage/libs/eclib/__init__.pxd | 4 ++-- src/sage/libs/eclib/mat.pyx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index d44d4fba865..84d1fc92275 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -55,7 +55,7 @@ cdef extern from "eclib/matrix.h": cdef cppclass mat: mat() mat(mat m) - scalar* get_entries() + vector[scalar] get_entries() scalar sub(long, long) long nrows() long ncols() @@ -67,7 +67,7 @@ cdef extern from "eclib/smatrix.h": cdef cppclass smat: smat() smat(smat m) - scalar* get_entries() + vector[scalar] get_entries() scalar sub(long, long) long nrows() long ncols() diff --git a/src/sage/libs/eclib/mat.pyx b/src/sage/libs/eclib/mat.pyx index bfdeb6ae5c1..e7abe369b2b 100644 --- a/src/sage/libs/eclib/mat.pyx +++ b/src/sage/libs/eclib/mat.pyx @@ -10,7 +10,7 @@ from sage.rings.integer_ring import ZZ from sage.matrix.matrix_integer_sparse cimport Matrix_integer_sparse from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.rings.integer cimport Integer - +from libcpp.vector cimport vector cdef class Matrix: """ @@ -213,7 +213,7 @@ cdef class Matrix: """ cdef long n = self.nrows() cdef long i, j, k - cdef scalar* v = self.M.get_entries() # coercion needed to deal with const + cdef vector[scalar] v = self.M.get_entries() # coercion needed to deal with const cdef Matrix_integer_dense Td cdef Matrix_integer_sparse Ts From 34ffbd9460cfdf89bf21f6631e1989ca6247c45b Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 4 Dec 2024 08:35:58 +0000 Subject: [PATCH 389/507] #38960 simplify eclib interface --- src/sage/libs/eclib/__init__.pxd | 2 -- src/sage/libs/eclib/mat.pyx | 18 +++++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index 84d1fc92275..2673af0613f 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -55,7 +55,6 @@ cdef extern from "eclib/matrix.h": cdef cppclass mat: mat() mat(mat m) - vector[scalar] get_entries() scalar sub(long, long) long nrows() long ncols() @@ -67,7 +66,6 @@ cdef extern from "eclib/smatrix.h": cdef cppclass smat: smat() smat(smat m) - vector[scalar] get_entries() scalar sub(long, long) long nrows() long ncols() diff --git a/src/sage/libs/eclib/mat.pyx b/src/sage/libs/eclib/mat.pyx index e7abe369b2b..5dbb39faa0e 100644 --- a/src/sage/libs/eclib/mat.pyx +++ b/src/sage/libs/eclib/mat.pyx @@ -10,7 +10,6 @@ from sage.rings.integer_ring import ZZ from sage.matrix.matrix_integer_sparse cimport Matrix_integer_sparse from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.rings.integer cimport Integer -from libcpp.vector cimport vector cdef class Matrix: """ @@ -212,8 +211,7 @@ cdef class Matrix: """ cdef long n = self.nrows() - cdef long i, j, k - cdef vector[scalar] v = self.M.get_entries() # coercion needed to deal with const + cdef long i, j cdef Matrix_integer_dense Td cdef Matrix_integer_sparse Ts @@ -221,21 +219,19 @@ cdef class Matrix: # Ugly code... if sparse: Ts = MatrixSpace(ZZ, n, sparse=sparse).zero_matrix().__copy__() - k = 0 for i from 0 <= i < n: for j from 0 <= j < n: - if v[k]: - Ts.set_unsafe(i, j, Integer(v[k])) - k += 1 + Mij = Integer(self.M.sub(i+1,j+1)); + if Mij: + Ts.set_unsafe(i, j, Mij) return Ts else: Td = MatrixSpace(ZZ, n, sparse=sparse).zero_matrix().__copy__() - k = 0 for i from 0 <= i < n: for j from 0 <= j < n: - if v[k]: - Td.set_unsafe(i, j, Integer(v[k])) - k += 1 + Mij = Integer(self.M.sub(i+1,j+1)); + if Mij: + Td.set_unsafe(i, j, Mij) return Td From 37aa9f8eb373462eecf01909fbca114d188cf716 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 4 Dec 2024 09:20:07 +0000 Subject: [PATCH 390/507] #38960: fix lint issue --- src/sage/libs/eclib/mat.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/libs/eclib/mat.pyx b/src/sage/libs/eclib/mat.pyx index 5dbb39faa0e..989d9d1a70c 100644 --- a/src/sage/libs/eclib/mat.pyx +++ b/src/sage/libs/eclib/mat.pyx @@ -221,7 +221,7 @@ cdef class Matrix: Ts = MatrixSpace(ZZ, n, sparse=sparse).zero_matrix().__copy__() for i from 0 <= i < n: for j from 0 <= j < n: - Mij = Integer(self.M.sub(i+1,j+1)); + Mij = Integer(self.M.sub(i+1,j+1)) if Mij: Ts.set_unsafe(i, j, Mij) return Ts @@ -229,7 +229,7 @@ cdef class Matrix: Td = MatrixSpace(ZZ, n, sparse=sparse).zero_matrix().__copy__() for i from 0 <= i < n: for j from 0 <= j < n: - Mij = Integer(self.M.sub(i+1,j+1)); + Mij = Integer(self.M.sub(i+1,j+1)) if Mij: Td.set_unsafe(i, j, Mij) return Td From 5d94a65310d1b552cbdb2dfab1a0f7b4bccf7adb Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 15 Feb 2025 07:04:34 -0500 Subject: [PATCH 391/507] build/pkgs: update eclib to 20250122 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index b046783341d..ff0ea83451a 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ tarball=eclib-VERSION.tar.bz2 -sha1=749e4fda3660006a9459f129148d05ba482daa29 -sha256=30765c27ca1420141f83517897119d0185fea9b31132392170ddae40b060e46f -upstream_url=https://github.com/JohnCremona/eclib/releases/download/vVERSION/eclib-VERSION.tar.bz2 +sha1=ec8dd87df46ac5a54b548354681085d1da6e7e13 +sha256=9f8c2b32e24a4f20d7cc2d336ea30c8ea03b5b0953c2d32adda0c496e7616899 +upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 353869c3cba..b0aec664e8d 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20241112 +20250122 From 2da824dcb2389b5c2c66e06d9d5159bdd1779511 Mon Sep 17 00:00:00 2001 From: mklss <59539887+mklss@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:53:43 +0100 Subject: [PATCH 392/507] Appease linter. --- src/sage/parallel/use_fork.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index b3bca5ad5b6..60cb56eed37 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -22,6 +22,7 @@ from sage.misc.randstate import set_random_seed from sage.misc.prandom import getrandbits + class WorkerData: """ Simple class which stores data about a running ``p_iter_fork`` From d059ea5e08b00b64f03340ab59148c4136ce3373 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sat, 15 Feb 2025 21:21:50 +0100 Subject: [PATCH 393/507] improve documentation and add possibility of non-reduced input --- src/sage/matrix/matrix_polynomial_dense.pyx | 134 ++++++++++++-------- 1 file changed, 80 insertions(+), 54 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index e45a0a5e8ed..9aff26d5dce 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4090,65 +4090,69 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): return P[:,column_indices] def minimal_relation_basis(self, - mat, + mod, shifts=None, row_wise=True, - normal_form=False): + normal_form=False, + reduced_input=False): r""" Return a relation basis in ``shifts``-ordered weak Popov form for this - polynomial matrix modulo ``mat``. + polynomial matrix with respect to the module defined by ``mod``. - If we work row-wise (which is the default), if `F` is an `m \times n` - polynomial matrix and `M` is a column reduced `n \times n` matrix, then - a relation basis `P` for `F` modulo `M` is a polynomial matrix whose - rows form a basis of the module of polynomial vectors `p` of size `m` - such that `p F = 0 \bmod M`, meaning that `p F` belongs to the module - generated by the rows of `M`. + The description below uses notation `F` for ``self``, `M` for ``mod``, + and `s` for the shifts ``shifts``. `F` is an `m \times n` polynomial + matrix and `M` is a nonsingular `n \times n` matrix. + If we work row-wise (resp. column-wise), a relation basis for `F` + modulo `M` is a polynomial matrix `P` whose rows (resp. columns) form a + basis of the module of polynomial vectors `p` of size `m` such that `p + F` (resp. `F p`) belongs to the row space (resp. column space) of `M`. Such a basis `P` is an `m \times m` nonsingular matrix, which this - method computes in ``shifts``-ordered weak Popov form. It is - guaranteed that the degree of the output basis is at most the degree - of determinant of ``mat``, independently of ``shifts``. If - ``normal_form`` is ``True``, then this output basis `P` is further - normalized: it is the canonical ``shifts``-Popov basis. - - Here are two special cases. - - - minimal approximant bases, for which `M` is a diagonal of powers of - the variable (see :meth:`minimal_approximant_basis`, which may be - called directly for better performance), + method computes in `s`-ordered weak Popov form. + + If ``normal_form`` is ``True``, then the output `P` is the canonical + `s`-Popov basis. If ``reduced_input`` is ``True``, then the provided + `M` should be column reduced (resp. row reduced) and `F` should be + reduced modulo `M`, that is, should have column (resp. row) degrees + strictly bounded entry-wise by those of `M`. When those properties are + known to be true, setting ``reduced_input`` to the non-default ``True`` + may save some computation time. + + An error is raised if the dimensions of the input matrices or the + length of the input shift are not sound. When ``reduced_input`` is the + default ``False``, an error is raised if `M` is singular. + + Here are two special cases of relation bases. + + - minimal approximant bases, for which `M` is a diagonal of powers + `x^{d_0}, \ldots, x^{d_{n-1}}` (see + :meth:`minimal_approximant_basis`, which may be called directly for + better performance), - minimal interpolant bases, for which `M` is a diagonal of polynomials - that split into linear factors. - - If ``row_wise`` is ``False``, then, similarly, the columns of `P` form - a basis of the right-relations, i.e. of the module of column vectors - `p` such that `F p` is in the column space of `M`. - - An error is raised if the input dimensions are not sound: if working - row-wise (resp. column-wise), the matrix dimensions of ``mat`` must be - the number of columns (resp. rows) of ``self``, while the length of - ``shifts`` must be the number of rows (resp. columns) of ``self``. - There is no check about the reducedness of ``mat`` or about the - degree constraints concerning ``self`` and ``mat``. + that split into known linear factors (see + :meth:`minimal_interpolant_basis`, which may be called directly for + better performance). INPUT: - - ``mat`` -- polynomial matrix + - ``mod`` -- polynomial matrix, nonsingular - ``shifts`` -- (default: ``None``) list of integers; ``None`` is interpreted as ``shifts=[0,...,0]`` - - ``row_wise`` -- boolean (default: ``True``). If ``True`` then the - output basis is considered row-wise and operates on the left of - ``self``; ``mat`` must be column reduced and its column degrees must - strictly bound those of ``self`` entry-wise. Otherwise the output - basis is column-wise and operates on the right of ``self``; ``mat`` - must be row reduced and its row degrees must strictly bound those of - ``self`` entry-wise. + - ``row_wise`` -- boolean (default: ``True``). If ``True``, compute + left relations for ``self`` modulo the row space of ``mod``; + otherwise, compute right relations for ``self`` modulo the column + space of ``mod`` - ``normal_form`` -- boolean (default: ``False``); if ``True`` then the output basis is in ``shifts``-Popov form + - ``reduced_input`` -- boolean (default: ``False``). If ``True``, and + working row-wise (resp. column-wise), then ``mod`` must be column + (resp. row) reduced and its column (resp. row) degrees must be strict + upper bounds on those of ``self`` entry-wise + OUTPUT: a polynomial matrix EXAMPLES:: @@ -4158,34 +4162,34 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): When M is a diagonal of powers of the variable, a relation basis is the same as an approximant basis:: - sage: mat = matrix.diagonal([x**4, x**3], sparse=False) + sage: mod = matrix.diagonal([x**4, x**3], sparse=False) sage: shifts = [-1, 2, 0] sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) sage: P_app = F.minimal_approximant_basis([4, 3], shifts, normal_form=True) - sage: P_rel = F.minimal_relation_basis(mat, shifts, normal_form=True) + sage: P_rel = F.minimal_relation_basis(mod, shifts, normal_form=True) sage: P_app == P_rel True If ``self`` is the identity matrix, then relation bases are simply - matrices that are left-unimodularly equivalent to ``mat``:: + matrices that are left-unimodularly equivalent to ``mod``:: - sage: # mat is both row and column reduced - sage: mat = x**4 * 1 + matrix.random(pR, 3, 3, degree=3) - sage: F = matrix.identity(pR, 3) # cdeg(F) < cdeg(mat) - sage: P = F.minimal_relation_basis(mat, shifts=[0,1,2], normal_form=True) - sage: P == mat.popov_form(shifts=[0,1,2]) + sage: # mod is both row and column reduced + sage: mod = x**4 * 1 + matrix.random(pR, 3, 3, degree=3) + sage: F = matrix.identity(pR, 3) # cdeg(F) < cdeg(mod) + sage: P = F.minimal_relation_basis(mod, shifts=[0,1,2], normal_form=True) + sage: P == mod.popov_form(shifts=[0,1,2]) True One can consider column-wise relations; unspecified shift means taking the uniform `[0,\ldots, 0]` shift:: sage: F = matrix.random(pR, 3, 5, degree=3) - sage: P = F.minimal_relation_basis(mat, row_wise=False) + sage: P = F.minimal_relation_basis(mod, row_wise=False) sage: P.is_weak_popov(shifts=[0]*5, row_wise=False) True - sage: Q,R = (F*P).left_quo_rem(mat) # F*P = mat*Q + 0 + sage: Q,R = (F*P).left_quo_rem(mod) # F*P = mod*Q + 0 sage: R == 0 True @@ -4209,11 +4213,33 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): raise ValueError('shifts length should be the column dimension') # check modulus dimension - if row_wise and mat.dimensions() != (n, n): + if row_wise and mod.dimensions() != (n, n): raise ValueError("modulus matrix dimensions must be the column dimension of self") - elif (not row_wise) and mat.dimensions() != (m, m): + elif (not row_wise) and mod.dimensions() != (m, m): raise ValueError("modulus matrix dimensions must be the row dimension of self") + # make sure input is reduced, unless guaranteed by the user + if not reduced_input: + # Ensure reducedness of input mod + # Say we work row-wise: we want a column reduced matrix, + # left-unimodularly equivalent to mod; among the possibilities we + # have at least all shifted row-wise Popov forms of mod (including + # the Hermite form). Some choice of shifts might be smarter than + # others, but without more information, we will fix the choice to + # the uniform [0,...,0]. The informed user may want to do this step + # before calling this method, with their own choice of shift. + # -> check, in case, to avoid unnecessary computations + if not mod.is_reduced(row_wise=(not row_wise), include_zero_vectors=False): + mod = mod.popov_form(row_wise=row_wise) + if mod.nrows() < n: + raise ValueError("modulus matrix must be nonsingular") + + # Ensure self is reduced modulo mod + if row_wise: + self = self._right_quo_rem_reduced(mod)[1] + else: + self = self.T._right_quo_rem_reduced(mod.T)[1].T + # compute extended shift for kernel basis computation # -> for correctness, the constraint on the added part is that it # must have maximum entry at most min(shifts) @@ -4227,9 +4253,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # build matrix for kernel computation if row_wise: - F = matrix.block([[self],[mat]]) + F = matrix.block([[self],[mod]]) else: - F = matrix.block([[self, mat]]) + F = matrix.block([[self, mod]]) # compute shifted weak Popov kernel basis kbas = F.minimal_kernel_basis(shifts=extended_shifts, normal_form=normal_form, row_wise=row_wise) From 23d75a91222a05fd1fa36bbf6d54c284fea5924d Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sat, 15 Feb 2025 23:46:46 +0100 Subject: [PATCH 394/507] fix small bug and add tests --- src/sage/matrix/matrix_polynomial_dense.pyx | 66 ++++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 9aff26d5dce..f37ac59b809 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4162,42 +4162,78 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): When M is a diagonal of powers of the variable, a relation basis is the same as an approximant basis:: - sage: mod = matrix.diagonal([x**4, x**3], sparse=False) + sage: M = matrix.diagonal([x**4, x**3], sparse=False) sage: shifts = [-1, 2, 0] sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) sage: P_app = F.minimal_approximant_basis([4, 3], shifts, normal_form=True) - sage: P_rel = F.minimal_relation_basis(mod, shifts, normal_form=True) + sage: P_rel = F.minimal_relation_basis(M, shifts, normal_form=True) sage: P_app == P_rel True If ``self`` is the identity matrix, then relation bases are simply - matrices that are left-unimodularly equivalent to ``mod``:: + matrices that are left-unimodularly equivalent to `M`:: - sage: # mod is both row and column reduced - sage: mod = x**4 * 1 + matrix.random(pR, 3, 3, degree=3) - sage: F = matrix.identity(pR, 3) # cdeg(F) < cdeg(mod) - sage: P = F.minimal_relation_basis(mod, shifts=[0,1,2], normal_form=True) - sage: P == mod.popov_form(shifts=[0,1,2]) + sage: # M is both row and column reduced + sage: M = x**4 + matrix.random(pR, 3, 3, degree=3) + sage: F1 = matrix.identity(pR, 3) # cdeg(F1) < cdeg(M) + sage: P1 = F1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) + sage: P1 == M.popov_form(shifts=[0,1,2]) True One can consider column-wise relations; unspecified shift means taking the uniform `[0,\ldots, 0]` shift:: - sage: F = matrix.random(pR, 3, 5, degree=3) - sage: P = F.minimal_relation_basis(mod, row_wise=False) - sage: P.is_weak_popov(shifts=[0]*5, row_wise=False) + sage: F2 = matrix.random(pR, 3, 5, degree=3) + sage: P2 = F2.minimal_relation_basis(M, row_wise=False) + sage: P2.is_weak_popov(shifts=[0]*5, row_wise=False) True - sage: Q,R = (F*P).left_quo_rem(mod) # F*P = mod*Q + 0 + sage: Q,R = (F2*P2).left_quo_rem(M) # F2*P2 = M*Q + 0 sage: R == 0 True Unless requiring a normal form, the output basis will most often not be the canonical one:: - sage: P.is_popov(shifts=[0]*5, row_wise=False) + sage: P2.is_popov(shifts=[0]*5, row_wise=False) False + + By default, this supports input ``self`` that are not reduced modulo + ``M``, unless ``reduced_input`` is specified as ``True``: + + sage: G1 = F1 + M # G1 == F1 mod M; G1 not reduced mod M + sage: P1bis = G1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) + sage: P1bis == P1 + True + sage: P = G1.minimal_relation_basis(M, shifts=[0,1,2], reduced_input=True) + sage: P.is_weak_popov(shifts=[0,1,2]) + False + sage: G2 = F2 + M * matrix.random(pR, 3, 5) + sage: P2bis = G2.minimal_relation_basis(M, row_wise=False) + sage: P2bis == P2 + True + sage: P = G2.minimal_relation_basis(M, row_wise=False, reduced_input=True) + sage: P.is_weak_popov(row_wise=False) + False + + By default, this supports any nonsingular matrix ``M``, and nonsingularity + is checked (unless ``reduced_input`` is specified as ``True``): + + sage: M1 = matrix([[1,x**10,x**10],[0,1,0],[0,0,1]]) * M # unimodular transformation + sage: M1.is_reduced(row_wise=False) # M1 not column reduced + False + sage: P1bis = F1.minimal_relation_basis(M1, shifts=[0,1,2], normal_form=True) + sage: P1bis == P1 # True since M and M1 have same row space + True + sage: P = F1.minimal_relation_basis(M1, shifts=[0,1,2], reduced_input=True) + sage: P.is_weak_popov(shifts=[0,1,2]) + False + sage: M2 = M.with_row_set_to_multiple_of_row(0, 1, x) # M2 is singular + sage: F1.minimal_relation_basis(M2) + Traceback (most recent call last): + ... + ValueError: modulus matrix must be nonsingular """ from sage.matrix.constructor import matrix # for matrix.block @@ -4230,8 +4266,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # before calling this method, with their own choice of shift. # -> check, in case, to avoid unnecessary computations if not mod.is_reduced(row_wise=(not row_wise), include_zero_vectors=False): - mod = mod.popov_form(row_wise=row_wise) - if mod.nrows() < n: + mod = mod.popov_form(row_wise=row_wise, include_zero_vectors=False) + if not mod.is_square(): raise ValueError("modulus matrix must be nonsingular") # Ensure self is reduced modulo mod From fda5e106a908a3bba920e87d4e434c49027b5ca0 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 00:22:19 +0100 Subject: [PATCH 395/507] improve tests --- src/sage/matrix/matrix_polynomial_dense.pyx | 23 ++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index f37ac59b809..09e2dc58d3f 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4176,7 +4176,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): matrices that are left-unimodularly equivalent to `M`:: sage: # M is both row and column reduced - sage: M = x**4 + matrix.random(pR, 3, 3, degree=3) + sage: M = x**3 + matrix([[6*x**2, 2, x], [2, 2, 6*x], [x**2, 3, 6]]) sage: F1 = matrix.identity(pR, 3) # cdeg(F1) < cdeg(M) sage: P1 = F1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) sage: P1 == M.popov_form(shifts=[0,1,2]) @@ -4185,7 +4185,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): One can consider column-wise relations; unspecified shift means taking the uniform `[0,\ldots, 0]` shift:: - sage: F2 = matrix.random(pR, 3, 5, degree=3) + sage: F2 = matrix([[ 1, 6*x + 2, 5, 3, 5*x^2 + 3], + ....: [2*x^2 + 4*x + 4, 3*x + 1, 5*x, 6*x^2 + 5, 6], + ....: [ 5*x + 4, 3*x + 1, 2, 2*x + 2, 2*x + 1]]) sage: P2 = F2.minimal_relation_basis(M, row_wise=False) sage: P2.is_weak_popov(shifts=[0]*5, row_wise=False) True @@ -4200,29 +4202,22 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): False By default, this supports input ``self`` that are not reduced modulo - ``M``, unless ``reduced_input`` is specified as ``True``: + ``M``, unless ``reduced_input`` is specified as ``True``:: - sage: G1 = F1 + M # G1 == F1 mod M; G1 not reduced mod M + sage: G1 = F1 + x * M # G1 == F1 mod M; G1 not reduced mod M sage: P1bis = G1.minimal_relation_basis(M, shifts=[0,1,2], normal_form=True) sage: P1bis == P1 True sage: P = G1.minimal_relation_basis(M, shifts=[0,1,2], reduced_input=True) sage: P.is_weak_popov(shifts=[0,1,2]) False - sage: G2 = F2 + M * matrix.random(pR, 3, 5) - sage: P2bis = G2.minimal_relation_basis(M, row_wise=False) - sage: P2bis == P2 - True - sage: P = G2.minimal_relation_basis(M, row_wise=False, reduced_input=True) - sage: P.is_weak_popov(row_wise=False) - False By default, this supports any nonsingular matrix ``M``, and nonsingularity - is checked (unless ``reduced_input`` is specified as ``True``): + is checked (unless ``reduced_input`` is specified as ``True``):: - sage: M1 = matrix([[1,x**10,x**10],[0,1,0],[0,0,1]]) * M # unimodular transformation + sage: M1 = matrix([[1,x**10,x**10],[0,1,0],[0,0,1]]) * M sage: M1.is_reduced(row_wise=False) # M1 not column reduced - False + False sage: P1bis = F1.minimal_relation_basis(M1, shifts=[0,1,2], normal_form=True) sage: P1bis == P1 # True since M and M1 have same row space True From ccd88cda3cea7a2a68e5e5583a64faa063babc3b Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 09:04:53 +0100 Subject: [PATCH 396/507] fix lint (too many blank lines) --- src/sage/matrix/matrix_polynomial_dense.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 09e2dc58d3f..8a49aa3f8ca 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -48,7 +48,6 @@ from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense from sage.matrix.matrix2 cimport Matrix from sage.rings.integer_ring import ZZ - cdef class Matrix_polynomial_dense(Matrix_generic_dense): r""" Dense matrix over a univariate polynomial ring over a field. @@ -847,7 +846,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: - sage: pR. = GF(7)[] sage: A = Matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], @@ -4297,7 +4295,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): else: return kbas[:n,:n] - def _basis_completion_via_reversed_approx(self): r""" Return a Smith form-preserving nonsingular completion of a row basis of From 06530ce5fe3b236043c99180ae56fd91948ae410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Feb 2025 10:33:24 +0100 Subject: [PATCH 397/507] better doctest in src/sage/categories/rings.py Co-authored-by: Travis Scrimshaw --- src/sage/categories/rings.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 78919cda38b..49f08930071 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1665,12 +1665,12 @@ def random_element(self, *args): EXAMPLES:: - sage: ZZ.random_element(8) # random - 1 - sage: QQ.random_element(8) # random - 2 - sage: ZZ.random_element(4,12) # random - 7 + sage: -8 <= ZZ.random_element(8) <= 8 + True + sage: -8 <= QQ.random_element(8) <= 8 + True + sage: 4 <= ZZ.random_element(4,12) <= 12 + True """ if not args: a, b = -2, 2 From dc3c49b6cc702cceeea0f3cefb3301452ca396c5 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 15:49:04 +0100 Subject: [PATCH 398/507] slightly simplify existing approximant basis code --- src/sage/matrix/matrix_polynomial_dense.pyx | 29 +++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 8a49aa3f8ca..90d8670c65f 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -2175,8 +2175,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): one = R.one() if transformation: - from sage.matrix.constructor import identity_matrix - U = identity_matrix(R, m) + from sage.matrix.constructor import matrix + U = matrix.identity(R, m) # initialise to_row and conflicts list to_row = [[] for i in range(n)] @@ -3695,11 +3695,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) True """ - # Define parameters and perform some sanity checks - m = self.nrows() - n = self.ncols() - polynomial_ring = self.base_ring() - X = polynomial_ring.gen() + from sage.matrix.constructor import matrix + m, n = self.dimensions() # 'rest_order': the orders that remains to be dealt with # 'rest_index': indices of orders that remains to be dealt with @@ -3708,8 +3705,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # initialization of the residuals (= input self) # and of the approximant basis (= identity matrix) - from sage.matrix.constructor import identity_matrix - appbas = identity_matrix(polynomial_ring, m) + appbas = matrix.identity(self.base_ring(), m) residuals = self.__copy__() # throughout the algorithm, 'rdeg' will be the shifts-row degrees of @@ -3743,7 +3739,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # coefficient = the coefficient of degree d of the column j of the # residual matrix # --> this is very likely nonzero and we want to make it zero, so - # that this column becomes zero mod X^{d+1} + # that this column becomes zero mod x^{d+1} coefficient = [residuals[i, j][d] for i in range(m)] # Lambda: collect rows [i] with nonzero coefficient[i] @@ -3763,15 +3759,16 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): scalar = -coefficient[row]/coefficient[pi] appbas.add_multiple_of_row(row, pi, scalar) residuals.add_multiple_of_row(row, pi, scalar) - # update row pi + # update row pi: multiply by x rdeg[pi] += 1 - appbas.rescale_row(pi, X) - residuals.rescale_row(pi, X) + for jj in range(m): + appbas[pi, jj] = appbas[pi, jj].shift(1) + for jj in range(residuals.ncols()): + residuals[pi, jj] = residuals[pi, jj].shift(1) # Decrement rest_order[j], unless there is no more work to do in - # this column, i.e. if rest_order[j] was 1: - # in this case remove the column j of - # residual,rest_order,rest_index + # this column (i.e. if rest_order[j] was 1) in which case + # remove the column j of residual,rest_order,rest_index if rest_order[j] == 1: residuals = residuals.delete_columns([j]) rest_order.pop(j) From c3ddea3d06711083e389413708923625110ac4a0 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 16:10:06 +0100 Subject: [PATCH 399/507] starting interpolant basis --- src/sage/matrix/matrix_polynomial_dense.pyx | 144 ++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 90d8670c65f..866c7357611 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3777,6 +3777,150 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): rest_order[j] -= 1 return appbas, rdeg + def _interpolant_basis_iterative(self, points, shifts): + r""" + Return a ``shifts``-ordered weak Popov interpolant basis for this + polynomial matrix with respect to points ``points`` (see + :meth:`minimal_interpolant_basis` for definitions). + TODO HERE + + The output basis is considered row-wise, that is, its rows are + left-interpolants for the columns of ``self``. + + The input dimensions are supposed to be sound: the length of ``points`` + must be the number of columns of ``self``, while the length of + ``shifts`` must be the number of rows of ``self``. + + INPUT: + + - ``points`` -- list of lists of elements from the base ring (or + coercible into it) + + - ``shifts`` -- list of integers + + OUTPUT: + + - a polynomial matrix (the interpolant basis ``P``). + + - a list of integers (the shifts-row degrees of ``P``). + + ALGORITHM: + + This is inspired from the iterative algorithms described in [VBB1992]_ + and [Bec1992]_ . + + EXAMPLES:: + + sage: pR. = GF(7)[] + + This method supports any number of columns or rows, as well as + arbitrary shifts and orders:: + + sage: order = [4, 1, 2]; shifts = [-3, 4] + sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5, 4], + ....: [2*x^3 + 2*x^2 + 2*x + 3, 6, 6*x + 3]]) + sage: appbas, rdeg = pmat._approximant_basis_iterative(order, + ....: shifts) + sage: appbas.is_minimal_approximant_basis(pmat, order, shifts) + True + + The returned list is the shifted row degrees of ``appbas``:: + + sage: rdeg == appbas.row_degrees(shifts) + True + + Approximant bases for the zero matrix are all constant unimodular + matrices; in fact, this algorithm returns the identity:: + + sage: pmat = Matrix(pR, 3, 2) + sage: appbas,rdeg = pmat._approximant_basis_iterative([2,5], + ....: [5,0,-4]) + sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) + True + """ + from sage.matrix.constructor import matrix + m, n = self.dimensions() + + # 'rest_order': the orders that remains to be dealt with + # 'rest_index': indices of orders that remains to be dealt with + rest_order = list(order) + rest_index = list(range(n)) + + # initialization of the residuals (= input self) + # and of the approximant basis (= identity matrix) + appbas = matrix.identity(self.base_ring(), m) + residuals = self.__copy__() + + # throughout the algorithm, 'rdeg' will be the shifts-row degrees of + # 'appbas' + # --> initially, 'rdeg' is the shift-row degree of the identity matrix + rdeg = list(shifts) + + while rest_order: + # invariant: + # * appbas is a shifts-ordered weak Popov approximant basis for + # (self,doneorder) + # where doneorder = the already processed order, that is, the + # tuple order-rest_order (entrywise subtraction) + # * rdeg is the shifts-row degree of appbas + # * residuals is the submatrix of columns (appbas * self)[:,j] + # for all j such that rest_order[j] > 0 + + # choice for the next coefficient to be dealt with: first of the + # largest entries in order (--> process 'self' degree-wise, and + # left to right) + # Note: one may also consider the first one in order (--> process + # 'self' columnwise, from left column to right column, set j=0 + # instead of the below), but it seems to often be (barely) slower + max_rest_order = max(rest_order) + for ind, value in enumerate(rest_order): + if value == max_rest_order: + j = ind + break + d = order[rest_index[j]] - rest_order[j] + + # coefficient = the coefficient of degree d of the column j of the + # residual matrix + # --> this is very likely nonzero and we want to make it zero, so + # that this column becomes zero mod x^{d+1} + coefficient = [residuals[i, j][d] for i in range(m)] + + # Lambda: collect rows [i] with nonzero coefficient[i] + # pi: index of the first row with smallest shift, among those in + # Lambda + Lambda = [] + pi = -1 + for i in range(m): + if coefficient[i] != 0: + Lambda.append(i) + if pi < 0 or rdeg[i] < rdeg[pi]: + pi = i + if Lambda: # otherwise, nothing to do + # update all rows in Lambda--{pi} + Lambda.remove(pi) + for row in Lambda: + scalar = -coefficient[row]/coefficient[pi] + appbas.add_multiple_of_row(row, pi, scalar) + residuals.add_multiple_of_row(row, pi, scalar) + # update row pi: multiply by x + rdeg[pi] += 1 + for jj in range(m): + appbas[pi, jj] = appbas[pi, jj].shift(1) + for jj in range(residuals.ncols()): + residuals[pi, jj] = residuals[pi, jj].shift(1) + + # Decrement rest_order[j], unless there is no more work to do in + # this column (i.e. if rest_order[j] was 1) in which case + # remove the column j of residual,rest_order,rest_index + if rest_order[j] == 1: + residuals = residuals.delete_columns([j]) + rest_order.pop(j) + rest_index.pop(j) + else: + rest_order[j] -= 1 + return appbas, rdeg + + def is_minimal_kernel_basis(self, pmat, shifts=None, From 0b201ce4f7886d69bac4c06da245ceddd1959a5f Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 16:11:33 +0100 Subject: [PATCH 400/507] just choice of words: rem_order sounds more suitable than rest_order --- src/sage/matrix/matrix_polynomial_dense.pyx | 76 ++++++++++----------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 866c7357611..0ab669f6501 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3698,10 +3698,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): from sage.matrix.constructor import matrix m, n = self.dimensions() - # 'rest_order': the orders that remains to be dealt with - # 'rest_index': indices of orders that remains to be dealt with - rest_order = list(order) - rest_index = list(range(n)) + # 'rem_order': the orders that remains to be dealt with + # 'rem_index': indices of orders that remains to be dealt with + rem_order = list(order) + rem_index = list(range(n)) # initialization of the residuals (= input self) # and of the approximant basis (= identity matrix) @@ -3713,15 +3713,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # --> initially, 'rdeg' is the shift-row degree of the identity matrix rdeg = list(shifts) - while rest_order: + while rem_order: # invariant: # * appbas is a shifts-ordered weak Popov approximant basis for # (self,doneorder) # where doneorder = the already processed order, that is, the - # tuple order-rest_order (entrywise subtraction) + # tuple order-rem_order (entrywise subtraction) # * rdeg is the shifts-row degree of appbas # * residuals is the submatrix of columns (appbas * self)[:,j] - # for all j such that rest_order[j] > 0 + # for all j such that rem_order[j] > 0 # choice for the next coefficient to be dealt with: first of the # largest entries in order (--> process 'self' degree-wise, and @@ -3729,12 +3729,12 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # Note: one may also consider the first one in order (--> process # 'self' columnwise, from left column to right column, set j=0 # instead of the below), but it seems to often be (barely) slower - max_rest_order = max(rest_order) - for ind, value in enumerate(rest_order): - if value == max_rest_order: + max_rem_order = max(rem_order) + for ind, value in enumerate(rem_order): + if value == max_rem_order: j = ind break - d = order[rest_index[j]] - rest_order[j] + d = order[rem_index[j]] - rem_order[j] # coefficient = the coefficient of degree d of the column j of the # residual matrix @@ -3766,15 +3766,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): for jj in range(residuals.ncols()): residuals[pi, jj] = residuals[pi, jj].shift(1) - # Decrement rest_order[j], unless there is no more work to do in - # this column (i.e. if rest_order[j] was 1) in which case - # remove the column j of residual,rest_order,rest_index - if rest_order[j] == 1: + # Decrement rem_order[j], unless there is no more work to do in + # this column (i.e. if rem_order[j] was 1) in which case + # remove the column j of residual,rem_order,rem_index + if rem_order[j] == 1: residuals = residuals.delete_columns([j]) - rest_order.pop(j) - rest_index.pop(j) + rem_order.pop(j) + rem_index.pop(j) else: - rest_order[j] -= 1 + rem_order[j] -= 1 return appbas, rdeg def _interpolant_basis_iterative(self, points, shifts): @@ -3806,8 +3806,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ALGORITHM: - This is inspired from the iterative algorithms described in [VBB1992]_ - and [Bec1992]_ . + This is inspired from the iterative algorithms described in [Bec1992]_ + and [VBB1992]_ . EXAMPLES:: @@ -3841,10 +3841,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): from sage.matrix.constructor import matrix m, n = self.dimensions() - # 'rest_order': the orders that remains to be dealt with - # 'rest_index': indices of orders that remains to be dealt with - rest_order = list(order) - rest_index = list(range(n)) + # 'rem_order': the orders that remains to be dealt with + # 'rem_index': indices of orders that remains to be dealt with + rem_order = list(order) + rem_index = list(range(n)) # initialization of the residuals (= input self) # and of the approximant basis (= identity matrix) @@ -3856,15 +3856,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # --> initially, 'rdeg' is the shift-row degree of the identity matrix rdeg = list(shifts) - while rest_order: + while rem_order: # invariant: # * appbas is a shifts-ordered weak Popov approximant basis for # (self,doneorder) # where doneorder = the already processed order, that is, the - # tuple order-rest_order (entrywise subtraction) + # tuple order-rem_order (entrywise subtraction) # * rdeg is the shifts-row degree of appbas # * residuals is the submatrix of columns (appbas * self)[:,j] - # for all j such that rest_order[j] > 0 + # for all j such that rem_order[j] > 0 # choice for the next coefficient to be dealt with: first of the # largest entries in order (--> process 'self' degree-wise, and @@ -3872,12 +3872,12 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # Note: one may also consider the first one in order (--> process # 'self' columnwise, from left column to right column, set j=0 # instead of the below), but it seems to often be (barely) slower - max_rest_order = max(rest_order) - for ind, value in enumerate(rest_order): - if value == max_rest_order: + max_rem_order = max(rem_order) + for ind, value in enumerate(rem_order): + if value == max_rem_order: j = ind break - d = order[rest_index[j]] - rest_order[j] + d = order[rem_index[j]] - rem_order[j] # coefficient = the coefficient of degree d of the column j of the # residual matrix @@ -3909,15 +3909,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): for jj in range(residuals.ncols()): residuals[pi, jj] = residuals[pi, jj].shift(1) - # Decrement rest_order[j], unless there is no more work to do in - # this column (i.e. if rest_order[j] was 1) in which case - # remove the column j of residual,rest_order,rest_index - if rest_order[j] == 1: + # Decrement rem_order[j], unless there is no more work to do in + # this column (i.e. if rem_order[j] was 1) in which case + # remove the column j of residual,rem_order,rem_index + if rem_order[j] == 1: residuals = residuals.delete_columns([j]) - rest_order.pop(j) - rest_index.pop(j) + rem_order.pop(j) + rem_index.pop(j) else: - rest_order[j] -= 1 + rem_order[j] -= 1 return appbas, rdeg From dd9ed344b0de996b62b1deefe9f938c671984292 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 17:00:06 +0100 Subject: [PATCH 401/507] use copy and deepcopy when needed --- src/sage/matrix/matrix_polynomial_dense.pyx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 0ab669f6501..34739dbb0d5 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3695,12 +3695,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) True """ - from sage.matrix.constructor import matrix + from sage.matrix.constructor import matrix # for identity + from copy import copy m, n = self.dimensions() # 'rem_order': the orders that remains to be dealt with # 'rem_index': indices of orders that remains to be dealt with - rem_order = list(order) + rem_order = copy(order) rem_index = list(range(n)) # initialization of the residuals (= input self) @@ -3728,7 +3729,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # left to right) # Note: one may also consider the first one in order (--> process # 'self' columnwise, from left column to right column, set j=0 - # instead of the below), but it seems to often be (barely) slower + # instead of the below), but it seems to often be (a bit) slower max_rem_order = max(rem_order) for ind, value in enumerate(rem_order): if value == max_rem_order: @@ -3838,12 +3839,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) True """ - from sage.matrix.constructor import matrix + from sage.matrix.constructor import matrix # for identity + from copy import deepcopy m, n = self.dimensions() # 'rem_order': the orders that remains to be dealt with # 'rem_index': indices of orders that remains to be dealt with - rem_order = list(order) + rem_order = deepcopy(points) rem_index = list(range(n)) # initialization of the residuals (= input self) @@ -3877,7 +3879,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): if value == max_rem_order: j = ind break - d = order[rem_index[j]] - rem_order[j] + d = points[rem_index[j]] - rem_order[j] # coefficient = the coefficient of degree d of the column j of the # residual matrix From dd4040f6aae8fa3f46ae6cfc74056e67ea189a78 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 17:01:04 +0100 Subject: [PATCH 402/507] use copy and deepcopy when needed --- src/sage/matrix/matrix_polynomial_dense.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 34739dbb0d5..2e15a1da1b6 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3696,12 +3696,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): True """ from sage.matrix.constructor import matrix # for identity - from copy import copy m, n = self.dimensions() # 'rem_order': the orders that remains to be dealt with # 'rem_index': indices of orders that remains to be dealt with - rem_order = copy(order) + rem_order = [o for o in order] rem_index = list(range(n)) # initialization of the residuals (= input self) From fa09d515004e87373732346fab50316404cb735f Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 18:49:18 +0100 Subject: [PATCH 403/507] iterative interpolant basis, main procedure --- src/sage/matrix/matrix_polynomial_dense.pyx | 173 +++++++++++--------- 1 file changed, 94 insertions(+), 79 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 2e15a1da1b6..77c7f80214e 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3708,10 +3708,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): appbas = matrix.identity(self.base_ring(), m) residuals = self.__copy__() - # throughout the algorithm, 'rdeg' will be the shifts-row degrees of - # 'appbas' + # throughout the algorithm, 'rdeg' is the shifts-row degrees of 'appbas' # --> initially, 'rdeg' is the shift-row degree of the identity matrix - rdeg = list(shifts) + rdeg = [s for s in shifts] while rem_order: # invariant: @@ -3724,8 +3723,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # for all j such that rem_order[j] > 0 # choice for the next coefficient to be dealt with: first of the - # largest entries in order (--> process 'self' degree-wise, and - # left to right) + # largest entries in order # Note: one may also consider the first one in order (--> process # 'self' columnwise, from left column to right column, set j=0 # instead of the below), but it seems to often be (a bit) slower @@ -3743,8 +3741,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): coefficient = [residuals[i, j][d] for i in range(m)] # Lambda: collect rows [i] with nonzero coefficient[i] - # pi: index of the first row with smallest shift, among those in - # Lambda + # pi: index of the first row with smallest shift, among those in Lambda Lambda = [] pi = -1 for i in range(m): @@ -3780,9 +3777,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): def _interpolant_basis_iterative(self, points, shifts): r""" Return a ``shifts``-ordered weak Popov interpolant basis for this - polynomial matrix with respect to points ``points`` (see + polynomial matrix with respect to points specified in ``points`` (see :meth:`minimal_interpolant_basis` for definitions). - TODO HERE The output basis is considered row-wise, that is, its rows are left-interpolants for the columns of ``self``. @@ -3811,88 +3807,107 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: - sage: pR. = GF(7)[] + sage: ff = GF(7) + sage: xring. = ff[] This method supports any number of columns or rows, as well as - arbitrary shifts and orders:: - - sage: order = [4, 1, 2]; shifts = [-3, 4] - sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5, 4], - ....: [2*x^3 + 2*x^2 + 2*x + 3, 6, 6*x + 3]]) - sage: appbas, rdeg = pmat._approximant_basis_iterative(order, - ....: shifts) - sage: appbas.is_minimal_approximant_basis(pmat, order, shifts) + arbitrary shifts and lists of points. The returned list is the shifted + row degrees of the interpolation basis:: + + sage: F = Matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5], + ....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4], + ....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]]) + sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]] + sage: mod1 = (x - 3) * x * (x - 6) * (x - 3) + sage: mod2 = (x - 1)**3 + + sage: P, rdeg = F._interpolant_basis_iterative(points, [0,0,0]) + sage: rdeg == P.row_degrees() + True + sage: P.is_weak_popov(ordered=True) + True + sage: G = P*F + sage: G[:,0] % mod1 == 0 and G[:,1] % mod2 == 0 True - The returned list is the shifted row degrees of ``appbas``:: + For "sufficiently generic" input, the fact that the returned matrix + generates the module of interpolants is equivalent to the fact that its + determinant is the product of linear factors defined by the + interpolation points:: + + sage: P.det() == mod1 * mod2 + True - sage: rdeg == appbas.row_degrees(shifts) + sage: points[1] = [] + sage: shifts = [-1, 2, 0] + sage: P, rdeg = F._interpolant_basis_iterative(points, shifts) + sage: rdeg == P.row_degrees(shifts=shifts) + True + sage: P.is_weak_popov(shifts=shifts, ordered=True) + True + sage: G = P*F + sage: G[:,0] % mod1 == 0 and P.det() == mod1 True - Approximant bases for the zero matrix are all constant unimodular + Interpolant bases for the zero matrix are all constant unimodular matrices; in fact, this algorithm returns the identity:: - sage: pmat = Matrix(pR, 3, 2) - sage: appbas,rdeg = pmat._approximant_basis_iterative([2,5], - ....: [5,0,-4]) - sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) + sage: F = matrix(xring, 4, 2) + sage: P,rdeg = F._interpolant_basis_iterative(points, shifts) + sage: rdeg == shifts and P == 1 True """ from sage.matrix.constructor import matrix # for identity - from copy import deepcopy + from copy import copy m, n = self.dimensions() - # 'rem_order': the orders that remains to be dealt with - # 'rem_index': indices of orders that remains to be dealt with - rem_order = deepcopy(points) - rem_index = list(range(n)) + # 'rem_points': the points that remain to be dealt with + # 'rem_index': indices of lists of points that remain to be dealt with + rem_points = [copy(pts) for pts in points if len(pts) > 0] + rem_index = [j for j in range(n) if len(points[j]) > 0] - # initialization of the residuals (= input self) - # and of the approximant basis (= identity matrix) - appbas = matrix.identity(self.base_ring(), m) - residuals = self.__copy__() + # initialization of the residuals (= input self, without columns associated to no point) + # and of the interpolant basis (= identity matrix) + intbas = matrix.identity(self.base_ring(), m) + residuals = self.matrix_from_columns(rem_index) - # throughout the algorithm, 'rdeg' will be the shifts-row degrees of - # 'appbas' + # throughout the algorithm, 'rdeg' is the shifts-row degrees of 'intbas' # --> initially, 'rdeg' is the shift-row degree of the identity matrix - rdeg = list(shifts) + rdeg = [s for s in shifts] - while rem_order: + while rem_points: # invariant: - # * appbas is a shifts-ordered weak Popov approximant basis for - # (self,doneorder) - # where doneorder = the already processed order, that is, the - # tuple order-rem_order (entrywise subtraction) - # * rdeg is the shifts-row degree of appbas - # * residuals is the submatrix of columns (appbas * self)[:,j] - # for all j such that rem_order[j] > 0 - - # choice for the next coefficient to be dealt with: first of the - # largest entries in order (--> process 'self' degree-wise, and - # left to right) - # Note: one may also consider the first one in order (--> process - # 'self' columnwise, from left column to right column, set j=0 - # instead of the below), but it seems to often be (barely) slower - max_rem_order = max(rem_order) - for ind, value in enumerate(rem_order): - if value == max_rem_order: + # * intbas is a shifts-ordered weak Popov interpolant basis for + # self and the already processed points + # * rdeg is the shifts-row degree of intbas + # * residuals is the submatrix of columns + # (intbas * self)[:,j] / prod_i(x - points[j][i]) + # for all j in rem_index and where i goes through the already + # processed points from points[j] + + # choice for the next point to be dealt with: last point of the + # list points[j] that has largest length + # Note: one may also consider a point of points[j] for the smallest + # possible j (--> roughly, set j=0 instead of the below), but it + # seems to often be (a bit) slower + max_rem_points = max(len(pts) for pts in rem_points) + for ind, pts in enumerate(rem_points): + if len(pts) == max_rem_points: j = ind break - d = points[rem_index[j]] - rem_order[j] + pt = rem_points[j].pop() - # coefficient = the coefficient of degree d of the column j of the - # residual matrix + # evals = the evaluations at pt of the column j of the residual matrix # --> this is very likely nonzero and we want to make it zero, so - # that this column becomes zero mod x^{d+1} - coefficient = [residuals[i, j][d] for i in range(m)] + # that this column becomes zero mod (x - pt) + evals = [residuals[i, j](pt) for i in range(m)] - # Lambda: collect rows [i] with nonzero coefficient[i] - # pi: index of the first row with smallest shift, among those in - # Lambda + # Lambda: collect rows [i] with nonzero evals[i] + # pi: index of the first row with smallest shift, among those in Lambda Lambda = [] pi = -1 for i in range(m): - if coefficient[i] != 0: + if evals[i] != 0: Lambda.append(i) if pi < 0 or rdeg[i] < rdeg[pi]: pi = i @@ -3900,26 +3915,26 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # update all rows in Lambda--{pi} Lambda.remove(pi) for row in Lambda: - scalar = -coefficient[row]/coefficient[pi] - appbas.add_multiple_of_row(row, pi, scalar) + scalar = -evals[row]/evals[pi] + intbas.add_multiple_of_row(row, pi, scalar) residuals.add_multiple_of_row(row, pi, scalar) - # update row pi: multiply by x + # update row pi: multiply by x - pt rdeg[pi] += 1 - for jj in range(m): - appbas[pi, jj] = appbas[pi, jj].shift(1) - for jj in range(residuals.ncols()): - residuals[pi, jj] = residuals[pi, jj].shift(1) + x = self.base_ring().gen() + intbas.rescale_row(pi, x - pt) + residuals.rescale_row(pi, x - pt) + # divide residual column by x - pt + for i in range(m): + residuals[i, j] = residuals[i, j] // (x - pt) - # Decrement rem_order[j], unless there is no more work to do in - # this column (i.e. if rem_order[j] was 1) in which case - # remove the column j of residual,rem_order,rem_index - if rem_order[j] == 1: + # If rem_points[j] is now empty, there is no more work to do in + # this column: remove column j of residual,rem_points,rem_index + if len(rem_points[j]) == 0: residuals = residuals.delete_columns([j]) - rem_order.pop(j) + rem_points.pop(j) rem_index.pop(j) - else: - rem_order[j] -= 1 - return appbas, rdeg + + return intbas, rdeg def is_minimal_kernel_basis(self, From d3c948e60c45effd61382f6990a3b9b52c66eebd Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 18:51:19 +0100 Subject: [PATCH 404/507] clean examples for approximants --- src/sage/matrix/matrix_polynomial_dense.pyx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 77c7f80214e..eb2e362c79f 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3676,23 +3676,21 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: order = [4, 1, 2]; shifts = [-3, 4] sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5, 4], ....: [2*x^3 + 2*x^2 + 2*x + 3, 6, 6*x + 3]]) - sage: appbas, rdeg = pmat._approximant_basis_iterative(order, - ....: shifts) - sage: appbas.is_minimal_approximant_basis(pmat, order, shifts) + sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts) + sage: P.is_minimal_approximant_basis(pmat, order, shifts) True - The returned list is the shifted row degrees of ``appbas``:: + The returned list is the shifted row degrees of the output basis:: - sage: rdeg == appbas.row_degrees(shifts) + sage: rdeg == P.row_degrees(shifts) True Approximant bases for the zero matrix are all constant unimodular matrices; in fact, this algorithm returns the identity:: sage: pmat = Matrix(pR, 3, 2) - sage: appbas,rdeg = pmat._approximant_basis_iterative([2,5], - ....: [5,0,-4]) - sage: rdeg == [5,0,-4] and appbas == Matrix.identity(pR, 3) + sage: P,rdeg = pmat._approximant_basis_iterative([2,5], [5,0,-4]) + sage: rdeg == [5,0,-4] and P == matrix.identity(pR, 3) True """ from sage.matrix.constructor import matrix # for identity From cd24e22f12e9acc5c3bdd5bfbea82746ec7d29e6 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 18:54:40 +0100 Subject: [PATCH 405/507] clean examples for approximants: add test for zero orders --- src/sage/matrix/matrix_polynomial_dense.pyx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index eb2e362c79f..3ff9401bfab 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3671,24 +3671,30 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] This method supports any number of columns or rows, as well as - arbitrary shifts and orders:: + arbitrary shifts and orders, and the returned list is the shifted row + degrees of the output basis:: sage: order = [4, 1, 2]; shifts = [-3, 4] - sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5, 4], - ....: [2*x^3 + 2*x^2 + 2*x + 3, 6, 6*x + 3]]) + sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2, 3*x^2 + 4], + ....: [2*x^3 + 2*x^2 + 2*x + 3, x^3 + 6, 6*x + 3]]) sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts) sage: P.is_minimal_approximant_basis(pmat, order, shifts) True + sage: rdeg == P.row_degrees(shifts) + True - The returned list is the shifted row degrees of the output basis:: - + Zero orders are supported: + sage: order = [4, 0, 2]; shifts = [3, -1] + sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts) + sage: P.is_minimal_approximant_basis(pmat, order, shifts) + True sage: rdeg == P.row_degrees(shifts) True Approximant bases for the zero matrix are all constant unimodular matrices; in fact, this algorithm returns the identity:: - sage: pmat = Matrix(pR, 3, 2) + sage: pmat = matrix(pR, 3, 2) sage: P,rdeg = pmat._approximant_basis_iterative([2,5], [5,0,-4]) sage: rdeg == [5,0,-4] and P == matrix.identity(pR, 3) True From 0a9bc73234ad610b102769590a2ae27249449b6a Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 18:59:24 +0100 Subject: [PATCH 406/507] consolidate code for zero orders --- src/sage/matrix/matrix_polynomial_dense.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 3ff9401bfab..7d2b224c052 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3704,13 +3704,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # 'rem_order': the orders that remains to be dealt with # 'rem_index': indices of orders that remains to be dealt with - rem_order = [o for o in order] - rem_index = list(range(n)) + rem_order = [d for d in order if d > 0] + rem_index = [j for j in range(n) if order[j] > 0] - # initialization of the residuals (= input self) + # initialization of the residuals (= input self, without columns with zero order) # and of the approximant basis (= identity matrix) appbas = matrix.identity(self.base_ring(), m) - residuals = self.__copy__() + residuals = self.matrix_from_columns(rem_index) # throughout the algorithm, 'rdeg' is the shifts-row degrees of 'appbas' # --> initially, 'rdeg' is the shift-row degree of the identity matrix From 1dd7b70be6c04baa2b5d892f36b4b0a005eafb99 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 19:07:19 +0100 Subject: [PATCH 407/507] consolidate code for zero/negative orders --- src/sage/matrix/matrix_polynomial_dense.pyx | 46 +++++++++------------ 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 7d2b224c052..2e3447b9cb8 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3467,12 +3467,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): this polynomial matrix at order ``order``. Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix - and `(d_0,\ldots,d_{n-1})` are positive integers, then an approximant - basis for `F` at order `(d_0,\ldots,d_{n-1})` is a polynomial matrix - whose rows form a basis of the module of approximants for `F` at order + and `(d_0,\ldots,d_{n-1})` are integers, then an approximant basis for + `F` at order `(d_0,\ldots,d_{n-1})` is a polynomial matrix whose rows + form a basis of the module of approximants for `F` at order `(d_0,\ldots,d_{n-1})`. The latter approximants are the polynomial vectors `p` of size `m` such that the column `j` of `p F` has valuation - at least `d_j`, for all `0 \le j \le n-1`. + at least `d_j`, for all `0 \le j \le n-1` (for `j` such that `d_j \le + 0`, this constraint is void.) If ``normal_form`` is ``True``, then the output basis `P` is furthermore in ``shifts``-Popov form. By default, `P` is considered @@ -3492,7 +3493,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): INPUT: - - ``order`` -- list of positive integers, or a positive integer + - ``order`` -- list of integers, or an integer - ``shifts`` -- (default: ``None``) list of integers; ``None`` is interpreted as ``shifts=[0,...,0]`` @@ -3568,13 +3569,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Traceback (most recent call last): ... ValueError: shifts length should be the row dimension - - An error is raised if order does not contain only positive integers:: - - sage: P = F.minimal_approximant_basis([1,0], shifts) - Traceback (most recent call last): - ... - ValueError: order should consist of positive integers """ m = self.nrows() n = self.ncols() @@ -3596,10 +3590,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): elif (not row_wise) and len(order) != m: raise ValueError("order length should be the row dimension") - for o in order: - if o < 1: - raise ValueError("order should consist of positive integers") - # compute approximant basis # if required, normalize it into shifted Popov form if row_wise: @@ -3610,21 +3600,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # Note: -deg(P[i,i]) = shifts[i] - rdeg[i] degree_shifts = [shifts[i] - rdeg[i] for i in range(m)] # compute approximant basis with that list as shifts - P,rdeg = self._approximant_basis_iterative(order, - degree_shifts) + P,rdeg = self._approximant_basis_iterative(order, degree_shifts) # left-multiply by inverse of leading matrix lmat = P.leading_matrix(shifts=degree_shifts) P = lmat.inverse() * P else: - P,rdeg = self.transpose()._approximant_basis_iterative(order, - shifts) + P,rdeg = self.transpose()._approximant_basis_iterative(order, shifts) if normal_form: # compute the list "- pivot degree" # (since weak Popov, pivot degree is rdeg-shifts entrywise) degree_shifts = [shifts[i] - rdeg[i] for i in range(n)] # compute approximant basis with that list as shifts - P, rdeg = self.transpose()._approximant_basis_iterative( - order, degree_shifts) + P, rdeg = self.T._approximant_basis_iterative(order, degree_shifts) P = P.transpose() # right-multiply by inverse of leading matrix lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False) @@ -3637,8 +3624,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): def _approximant_basis_iterative(self, order, shifts): r""" Return a ``shifts``-ordered weak Popov approximant basis for this - polynomial matrix at order ``order`` - (see :meth:`minimal_approximant_basis` for definitions). + polynomial matrix at order ``order`` (see + :meth:`minimal_approximant_basis` for definitions). The output basis is considered row-wise, that is, its rows are left-approximants for the columns of ``self``. It is guaranteed that @@ -3651,7 +3638,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): INPUT: - - ``order`` -- list of positive integers + - ``order`` -- list of integers - ``shifts`` -- list of integers @@ -3683,13 +3670,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: rdeg == P.row_degrees(shifts) True - Zero orders are supported: + Zero or negative orders are supported:: + sage: order = [4, 0, 2]; shifts = [3, -1] sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts) sage: P.is_minimal_approximant_basis(pmat, order, shifts) True sage: rdeg == P.row_degrees(shifts) True + sage: order = [4, -3, 2]; shifts = [3, -1] + sage: P2, rdeg = pmat._approximant_basis_iterative(order, shifts) + sage: P == P2 + True Approximant bases for the zero matrix are all constant unimodular matrices; in fact, this algorithm returns the identity:: @@ -3838,7 +3830,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): generates the module of interpolants is equivalent to the fact that its determinant is the product of linear factors defined by the interpolation points:: - + sage: P.det() == mod1 * mod2 True From 3fe8dac275a5bfca3a6dd93eb00a90d54c51b074 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 19:13:54 +0100 Subject: [PATCH 408/507] consolidate code for zero/negative orders: improve doc --- src/sage/matrix/matrix_polynomial_dense.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 2e3447b9cb8..acb6e4cfac9 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3558,6 +3558,15 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ....: 3, row_wise=False).transpose()) True + Zero or negative order entries are supported, and amount to forgetting + the corresponding column of ``self`` (or row, if column-wise):: + + sage: P = F.minimal_approximant_basis([4, 0, 3], row_wise=False) + sage: P == F.minimal_approximant_basis([4, -2, 3], row_wise=False) + True + sage: P == F[[0,2],:].minimal_approximant_basis([4,3], row_wise=False) + True + Errors are raised if the input dimensions are not sound:: sage: P = F.minimal_approximant_basis([4], shifts) From 5f616300cb4ddaf69a875043d8fb90f32e9e9d19 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 19:17:22 +0100 Subject: [PATCH 409/507] minor doc improvement --- src/sage/matrix/matrix_polynomial_dense.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index acb6e4cfac9..b01173c5477 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3559,7 +3559,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): True Zero or negative order entries are supported, and amount to forgetting - the corresponding column of ``self`` (or row, if column-wise):: + the corresponding column of ``self`` (or corresponding row, if column-wise):: sage: P = F.minimal_approximant_basis([4, 0, 3], row_wise=False) sage: P == F.minimal_approximant_basis([4, -2, 3], row_wise=False) @@ -4152,7 +4152,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): TESTS: - We check that PR #37208 is fixed:: + We check that the issue in PR #37208 is fixed:: sage: Matrix(pR, 2, 0).minimal_kernel_basis().is_sparse() False From e0aad8d1f96f875d40a7f380be6209920dc4b9d1 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 19:19:52 +0100 Subject: [PATCH 410/507] simplify (consequence of supporting zero orders) --- src/sage/matrix/matrix_polynomial_dense.pyx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index b01173c5477..0a11a680ea7 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4154,7 +4154,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): We check that the issue in PR #37208 is fixed:: - sage: Matrix(pR, 2, 0).minimal_kernel_basis().is_sparse() + sage: matrix(pR, 2, 0).minimal_kernel_basis().is_sparse() False """ from sage.matrix.constructor import matrix @@ -4190,17 +4190,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # orders for approximation orders = self.column_degrees(degree_bounds) for i in range(n): orders[i] = orders[i]+1 - - # note: minimal_approximant_basis requires orders[i] > 0 + # note: # -> if d>0, then degree_bounds > 0 entry-wise and this tuple - # `orders` already has all entries strictly positive - # -> if d==0, then `orders[i]` is zero exactly when the column i - # of self is zero; we may as well take orders[i] == 1 for such - # columns which do not influence the left kernel - if d == 0: - for i in range(n): - if orders[i] == 0: - orders[i] = 1 + # `orders` has all entries strictly positive + # -> if d==0, then `orders[i]` is zero exactly when the column i of + # self is zero; such columns which do not influence the left kernel # compute approximant basis and retrieve kernel rows P = self.minimal_approximant_basis(orders,shifts,True,normal_form) From 066da7d589fbeb3ef02cc3417e8730bd11577630 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 19:21:33 +0100 Subject: [PATCH 411/507] simplify (consequence of supporting zero orders) --- src/sage/matrix/matrix_polynomial_dense.pyx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 0a11a680ea7..fe138629106 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4194,7 +4194,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # -> if d>0, then degree_bounds > 0 entry-wise and this tuple # `orders` has all entries strictly positive # -> if d==0, then `orders[i]` is zero exactly when the column i of - # self is zero; such columns which do not influence the left kernel + # self is zero; such columns do not influence the left kernel # compute approximant basis and retrieve kernel rows P = self.minimal_approximant_basis(orders,shifts,True,normal_form) @@ -4227,13 +4227,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): # note: minimal_approximant_basis requires orders[i] > 0 # -> if d>0, then degree_bounds > 0 entry-wise and this tuple # `orders` already has all entries strictly positive - # -> if d==0, then `orders[i]` is zero exactly when the row i - # of self is zero; we may as well take orders[i] == 1 for such - # rows which do not influence the right kernel - if d == 0: - for i in range(m): - if orders[i] == 0: - orders[i] = 1 + # -> if d==0, then `orders[i]` is zero exactly when the row i of + # self is zero; such rows do not influence the right kernel # compute approximant basis and retrieve kernel columns P = self.minimal_approximant_basis(orders,shifts,False,normal_form) From 6276920dac384dc4cfc630258e535f34f965b9b0 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 20:30:35 +0100 Subject: [PATCH 412/507] minimal interpolation basis, main interface, and tests, and documentation --- src/sage/matrix/matrix_polynomial_dense.pyx | 211 +++++++++++++++++++- 1 file changed, 203 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index fe138629106..2277c4a8e3d 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -31,7 +31,8 @@ AUTHORS: - Vincent Neiger (2024-02-13): added basis_completion(), _is_basis_completion(), _basis_completion_via_reversed_approx(). -- Vincent Neiger (2025-02-13): added minimal_relation_basis(). +- Vincent Neiger (2025-02-16): added minimal_relation_basis(), + minimal_interpolation_basis(). """ # **************************************************************************** # Copyright (C) 2016 Kwankyu Lee @@ -3464,7 +3465,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): normal_form=False): r""" Return an approximant basis in ``shifts``-ordered weak Popov form for - this polynomial matrix at order ``order``. + this polynomial matrix at order ``order``. This is a direct extension + of the so-called Hermite-Padé approximation, which corresponds to the + case where ``self`` is a single vector. Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix and `(d_0,\ldots,d_{n-1})` are integers, then an approximant basis for @@ -3473,7 +3476,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): `(d_0,\ldots,d_{n-1})`. The latter approximants are the polynomial vectors `p` of size `m` such that the column `j` of `p F` has valuation at least `d_j`, for all `0 \le j \le n-1` (for `j` such that `d_j \le - 0`, this constraint is void.) + 0`, this constraint is void). If ``normal_form`` is ``True``, then the output basis `P` is furthermore in ``shifts``-Popov form. By default, `P` is considered @@ -3543,7 +3546,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): give a single integer:: sage: (F.minimal_approximant_basis(3) == - ....: F.minimal_approximant_basis([3,3], shifts=None)) + ....: F.minimal_approximant_basis([3,3], shifts=[0,0,0])) True One can work column-wise by specifying ``row_wise=False``:: @@ -3558,7 +3561,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ....: 3, row_wise=False).transpose()) True - Zero or negative order entries are supported, and amount to forgetting + Zero or negative order entries are supported, and amount to ignoring the corresponding column of ``self`` (or corresponding row, if column-wise):: sage: P = F.minimal_approximant_basis([4, 0, 3], row_wise=False) @@ -3779,6 +3782,198 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): rem_order[j] -= 1 return appbas, rdeg + def minimal_interpolant_basis(self, + points, + shifts=None, + row_wise=True, + normal_form=False): + r""" + Return an interpolant basis in ``shifts``-ordered weak Popov form for + this polynomial matrix with respect to the points in ``points``. This + is a general form of interpolation problems usually called M-Padé + approximation or vector rational interpolation. + + Assuming we work row-wise, if `F` is an `m \times n` polynomial matrix + and `a_{i,j}, 0 \le i < d_j` are elements (called points) of the base + field for some integers `d_0,\ldots,d_{n-1}`, then an interpolant basis + for `F` with respect to these points is a polynomial matrix whose rows + form a basis of the module of interpolants for `F` with respect to the + points. The latter interpolants are the polynomial vectors `p` of size + `m` such that the column `j` of `p F` vanishes modulo `\mu_j = \prod_{0 + \le i < d_j} (x - a_{i,j})` (that is, it vanishes at all points + `a_{i,j}`'s, with multiplicity in case of repeated points), for all `0 + \le j \le n-1`. For `j` such that `d_j \le 0`, i.e. the `j`th list of + points is empty, this constraint on the column `j` is void. + + If ``normal_form`` is ``True``, then the output basis `P` is + furthermore in ``shifts``-Popov form. By default, `P` is considered + row-wise, that is, its rows are left-interpolants for ``self``; if + ``row_wise`` is ``False`` then its columns are right-interpolants for + ``self``. It is guaranteed that the degree of the output basis is at + most `\deg(\lcm(\mu_j, 0 \le j < n))`, independently of ``shifts``. + + An error is raised if the input dimensions are not sound: if working + row-wise (resp. column-wise), the length of ``points`` must be the + number of columns (resp. rows) of ``self``, while the length of + ``shifts`` must be the number of rows (resp. columns) of ``self``. + + If a single list is provided for ``points``, then it is converted into + a list containing this list repeated the suitable number of times. + + INPUT: + + - ``points`` -- list of elements from the base field (or coercible into + it), of list of such lists + + - ``shifts`` -- (default: ``None``) list of integers; + ``None`` is interpreted as ``shifts=[0,...,0]`` + + - ``row_wise`` -- boolean (default: ``True``); if ``True`` then the + output basis is considered row-wise and operates on the left of + ``self``. Otherwise it is column-wise and operates on the right of + ``self``. + + - ``normal_form`` -- boolean (default: ``False``); if ``True`` then the + output basis is in ``shifts``-Popov form + + OUTPUT: a polynomial matrix + + ALGORITHM: + + The implementation is inspired from the iterative algorithms described + in [Bec1992]_ and [VBB1992]_ ; for obtaining the normal form, it relies + directly on Lemmas 3.3 and 4.1 in [JNSV2016]_ . + + EXAMPLES:: + + sage: ff = GF(7) + sage: xring. = ff[] + + This method supports any number of columns or rows, as well as + arbitrary shifts and lists of points:: + + sage: F = matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5], + ....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4], + ....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]]) + sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]] + sage: mod1 = (x - 3) * x * (x - 6) * (x - 3) + sage: mod2 = (x - 1)**3 + + sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0]) + sage: P.is_weak_popov(ordered=True) + True + sage: G = P*F + sage: G[:,0] % mod1 == 0 and G[:,1] % mod2 == 0 + True + sage: P.det() == mod1 * mod2 + True + + The last test highlights that for "sufficiently generic" input, the + fact that the returned matrix generates the module of interpolants is + equivalent to the fact that its determinant is the product of the + linear factors defined by the interpolation points. + + If shifts are not specified, they are chosen as uniform `[0,\ldots,0]` + by default. Besides, if the lists of points are all identical, one can + rather give a single list:: + + sage: P = F.minimal_interpolant_basis([points[0], points[0]]) + sage: P == F.minimal_interpolant_basis(points[0], shifts=[0,0,0]) + True + + One can work column-wise by specifying ``row_wise=False``. Empty lists + of points are supported, and amount to ignoring the corresponding + column of ``self`` (or corresponding row, if column-wise):: + + sage: points[1] = [] + sage: shifts = [-1, 2, 0] + sage: Ft = F.transpose() + sage: P = Ft.minimal_interpolant_basis(points, shifts=shifts, row_wise=False) + sage: P == Ft[0,:].minimal_interpolant_basis([points[0]], shifts=shifts, row_wise=False) + True + sage: P.is_weak_popov(shifts=shifts, ordered=True, row_wise=False) + True + sage: G = Ft * P + sage: G[0,:] % mod1 == 0 and P.det() == mod1 + True + + Errors are raised if the input dimensions are not sound:: + + sage: P = F.minimal_interpolant_basis([points[0]]) + Traceback (most recent call last): + ... + ValueError: points length should be the column dimension + + sage: P = F.minimal_interpolant_basis(points, shifts=[0,0,0,0]) + Traceback (most recent call last): + ... + ValueError: shifts length should be the row dimension + """ + from sage.matrix.constructor import matrix # for identity + from copy import copy + + m = self.nrows() + n = self.ncols() + + # set default shifts / check shifts dimension + if shifts is None: + shifts = [0] * m if row_wise else [0] * n + elif row_wise and len(shifts) != m: + raise ValueError('shifts length should be the row dimension') + elif (not row_wise) and len(shifts) != n: + raise ValueError('shifts length should be the column dimension') + + # deal with corner case where there is no equation to solve + if row_wise and (n == 0 or len(points) == 0): + return matrix.identity(self.base_ring(), m) + elif (not row_wise) and (m == 0 or len(points) == 0): + return matrix.identity(self.base_ring(), n) + + # thanks to the above corner case, from here on, points is a nonempty list + # if its entries are field elements, build full list of lists of points + if not isinstance(points[0], list): + if row_wise: + points = [copy(points) for j in range(n)] + else: + points = [copy(points) for i in range(m)] + + # check length of points + if row_wise and len(points) != n: + raise ValueError("points length should be the column dimension") + elif (not row_wise) and len(points) != m: + raise ValueError("points length should be the row dimension") + + # compute interpolant basis + # if required, normalize it into shifted Popov form + if row_wise: + P, rdeg = self._interpolant_basis_iterative(points, shifts) + if normal_form: + # compute the list "- pivot degree" + # (since weak Popov, pivot degree is rdeg-shifts entrywise) + # Note: -deg(P[i,i]) = shifts[i] - rdeg[i] + degree_shifts = [shifts[i] - rdeg[i] for i in range(m)] + # compute interpolant basis with that list as shifts + P, rdeg = self._interpolant_basis_iterative(points, degree_shifts) + # left-multiply by inverse of leading matrix + lmat = P.leading_matrix(shifts=degree_shifts) + P = lmat.inverse() * P + else: + P, rdeg = self.transpose()._interpolant_basis_iterative(points, shifts) + if normal_form: + # compute the list "- pivot degree" + # (since weak Popov, pivot degree is rdeg-shifts entrywise) + degree_shifts = [shifts[i] - rdeg[i] for i in range(n)] + # compute interpolant basis with that list as shifts + P, rdeg = self.T._interpolant_basis_iterative(points, degree_shifts) + P = P.transpose() + # right-multiply by inverse of leading matrix + lmat = P.leading_matrix(shifts=degree_shifts, row_wise=False) + P = P * lmat.inverse() + else: + P = P.transpose() + + return P + def _interpolant_basis_iterative(self, points, shifts): r""" Return a ``shifts``-ordered weak Popov interpolant basis for this @@ -3794,7 +3989,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): INPUT: - - ``points`` -- list of lists of elements from the base ring (or + - ``points`` -- list of lists of elements from the base field (or coercible into it) - ``shifts`` -- list of integers @@ -3819,7 +4014,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): arbitrary shifts and lists of points. The returned list is the shifted row degrees of the interpolation basis:: - sage: F = Matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5], + sage: F = matrix([[5*x^3 + 4*x^2 + 4*x + 6, 5*x^3 + 5*x^2 + 5], ....: [2*x^3 + 2*x^2 + 2*x + 3, 3*x^3 + 6*x + 4], ....: [ x^2 + 6*x + 3, 5*x^3 + 2*x^2 + 3*x]]) sage: points = [[ff(3), ff(0), ff(6), ff(3)], [ff(1), ff(1), ff(1)]] @@ -3837,7 +4032,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): For "sufficiently generic" input, the fact that the returned matrix generates the module of interpolants is equivalent to the fact that its - determinant is the product of linear factors defined by the + determinant is the product of the linear factors defined by the interpolation points:: sage: P.det() == mod1 * mod2 From a632edb6f786abf7326ded7c3c4885c43c55a366 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 20:36:11 +0100 Subject: [PATCH 413/507] fixes in doc --- src/sage/matrix/matrix_polynomial_dense.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 2277c4a8e3d..bde6ac7da48 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3802,7 +3802,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): `m` such that the column `j` of `p F` vanishes modulo `\mu_j = \prod_{0 \le i < d_j} (x - a_{i,j})` (that is, it vanishes at all points `a_{i,j}`'s, with multiplicity in case of repeated points), for all `0 - \le j \le n-1`. For `j` such that `d_j \le 0`, i.e. the `j`th list of + \le j \le n-1`. For `j` such that `d_j \le 0`, i.e. the `j` th list of points is empty, this constraint on the column `j` is void. If ``normal_form`` is ``True``, then the output basis `P` is @@ -3818,7 +3818,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ``shifts`` must be the number of rows (resp. columns) of ``self``. If a single list is provided for ``points``, then it is converted into - a list containing this list repeated the suitable number of times. + a list containing the provided list repeated the suitable number of + times. INPUT: From 512bc549ba6569f8b72fde4d346f1669d73154a6 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 20:39:26 +0100 Subject: [PATCH 414/507] doc: unify matrix/Matrix -> matrix --- src/sage/matrix/matrix_polynomial_dense.pyx | 178 ++++++++++---------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index bde6ac7da48..06fdc71a15f 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -121,7 +121,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M._check_shift_dimension(shifts=[1,3,2]) sage: M._check_shift_dimension(shifts=[1,3,2], row_wise=False) @@ -147,19 +147,19 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.degree() 3 The zero matrix has degree ``-1``:: - sage: M = Matrix(pR, 2, 3) + sage: M = matrix(pR, 2, 3) sage: M.degree() -1 For an empty matrix, the degree is not defined:: - sage: M = Matrix(pR, 3, 0) + sage: M = matrix(pR, 3, 0) sage: M.degree() Traceback (most recent call last): ... @@ -201,7 +201,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.degree_matrix() [ 1 -1 0] [ 3 -1 -1] @@ -250,7 +250,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -274,16 +274,16 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] ....: ]) sage: M.is_constant() False - sage: M = Matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant() + sage: M = matrix(pR, [[1,5,2], [3,1,5]]); M.is_constant() True - sage: M = Matrix.zero(pR, 3, 5); M.is_constant() + sage: M = matrix.zero(pR, 3, 5); M.is_constant() True .. SEEALSO:: @@ -321,7 +321,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -417,7 +417,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -508,7 +508,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -616,7 +616,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -751,7 +751,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 3, 3, + sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) @@ -848,7 +848,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 3, 3, + sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) @@ -860,7 +860,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: B == X*A % x**4 True - sage: B = Matrix(pR, 2, 3, + sage: B = matrix(pR, 2, 3, ....: [[3*x, x^2 + x + 2, x^2 + 2*x + 3], ....: [ 0, 6*x^2 + 1, 1]]) sage: A.solve_left_series_trunc(B, 3) @@ -895,7 +895,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): [5*x^2 + 2*x + 5 5*x + 5 2*x + 4] [5*x^3 + 2*x + 1 2*x^2 + 2*x + 5 4*x^2] - sage: V = Matrix([[3*x^2 + 4*x + 1, 4*x]]) + sage: V = matrix([[3*x^2 + 4*x + 1, 4*x]]) sage: A[:2,:].solve_left_series_trunc(V*A[:2,:], 4) == V True @@ -995,7 +995,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 3, 3, + sage: A = matrix(pR, 3, 3, ....: [[4*x+5, 5*x^2 + x + 1, 4*x^2 + 4], ....: [6*x^2 + 6*x + 6, 4*x^2 + 5*x, 4*x^2 + x + 3], ....: [3*x^2 + 2, 4*x + 1, x^2 + 3*x]]) @@ -1006,7 +1006,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): (2*x^3 + x^2, 5*x^3 + x^2 + 5*x + 6, 4*x^3 + 6*x^2 + 4*x) sage: B == A*X % x**4 True - sage: B = Matrix(pR, 3, 2, + sage: B = matrix(pR, 3, 2, ....: [[5*x^2 + 6*x + 3, 4*x^2 + 6*x + 4], ....: [ x^2 + 4*x + 2, 5*x + 2], ....: [ 5*x + 3, 0]]) @@ -1044,7 +1044,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): [ x^2 + 3*x + 5 3*x^2 + 4*x + 4] [ 5*x + 3 3*x + 2] - sage: V = Matrix([[2*x^2 + 5*x + 1], [3*x^2+4]]) + sage: V = matrix([[2*x^2 + 5*x + 1], [3*x^2+4]]) sage: A[:,:2].solve_right_series_trunc(A[:,:2]*V, 4) == V True @@ -1103,7 +1103,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.row_degrees() [1, 3] @@ -1113,7 +1113,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): A zero row in a polynomial matrix can be identified in the (shifted) row degrees as the entries equal to ``min(shifts)-1``:: - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 0, 0]]) sage: M.row_degrees() [1, 3, -1] @@ -1123,13 +1123,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): The row degrees of an empty matrix (`0\times n` or `m\times 0`) is not defined:: - sage: M = Matrix(pR, 0, 3) + sage: M = matrix(pR, 0, 3) sage: M.row_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have row degrees - sage: M = Matrix(pR, 3, 0) + sage: M = matrix(pR, 3, 0) sage: M.row_degrees() Traceback (most recent call last): ... @@ -1170,7 +1170,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.column_degrees() [3, -1, 0] @@ -1186,13 +1186,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): The column degrees of an empty matrix (`0\times n` or `m\times 0`) is not defined:: - sage: M = Matrix(pR, 0, 3) + sage: M = matrix(pR, 0, 3) sage: M.column_degrees() Traceback (most recent call last): ... ValueError: empty matrix does not have column degrees - sage: M = Matrix(pR, 3, 0) + sage: M = matrix(pR, 3, 0) sage: M.column_degrees() Traceback (most recent call last): ... @@ -1251,7 +1251,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.leading_matrix() [3 0 0] [1 0 0] @@ -1326,13 +1326,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, 0, 0) + sage: M = matrix(pR, 0, 0) sage: M._is_empty_popov() True sage: M._is_empty_popov(include_zero_vectors=False) True - sage: M = Matrix(pR, 0, 3) + sage: M = matrix(pR, 0, 3) sage: M._is_empty_popov(include_zero_vectors=False) True sage: M._is_empty_popov(row_wise=False) @@ -1397,7 +1397,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.is_reduced() False @@ -1411,7 +1411,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ....: include_zero_vectors=False) False - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 1, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0], [0, 1, 0]]) sage: M.is_reduced(shifts=[2,0,0], row_wise=False) True @@ -1477,7 +1477,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) + sage: M = matrix(pR, [[3*x+1, 0, 1], [x^3+3, 0, 0]]) sage: M.leading_positions() [0, 0] @@ -1508,7 +1508,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): The leading positions and pivot degrees of an empty matrix (`0\times n` or `m\times 0`) is not defined:: - sage: M = Matrix(pR, 0, 3) + sage: M = matrix(pR, 0, 3) sage: M.leading_positions() Traceback (most recent call last): ... @@ -1519,7 +1519,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ... ValueError: empty matrix does not have leading positions - sage: M = Matrix(pR, 3, 0) + sage: M = matrix(pR, 3, 0) sage: M.leading_positions(row_wise=False) Traceback (most recent call last): ... @@ -1609,7 +1609,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix([ [x^3+3*x^2+6*x+6, 3*x^2+3*x+6, 4*x^2+x+3], + sage: M = matrix([ [x^3+3*x^2+6*x+6, 3*x^2+3*x+6, 4*x^2+x+3], ....: [5, 1, 0 ], ....: [2*x^2+2, 2*x+5, x^2+4*x+6] ]) sage: M.is_weak_popov() @@ -1638,7 +1638,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Rectangular matrices are supported:: - sage: M = Matrix([ + sage: M = matrix([ ....: [ x^3+5*x^2+5*x+1, 5, 6*x+4, 0], ....: [ 6*x^2+3*x+1, 1, 2, 0], ....: [2*x^3+4*x^2+6*x+4, 5*x + 1, 2*x^2+5*x+5, x^2+5*x+6] @@ -1651,7 +1651,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Zero rows (resp. columns) can be forbidden:: - sage: M = Matrix([ + sage: M = matrix([ ....: [ 6*x+4, 0, 5*x+1, 0], ....: [ 2, 5*x + 1, 6*x^2+3*x+1, 0], ....: [2*x^2+5*x+5, 1, 2*x^3+4*x^2+6*x+4, 0] @@ -1749,7 +1749,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], + sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], ....: [x^2+6*x+6, x^2+5*x+5, 2 ], ....: [3*x, 6*x+5, x+5]]) sage: M.is_popov() @@ -1764,7 +1764,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: M[:2,:].is_popov(shifts=[0,1,2]) True - sage: M = Matrix(pR, [[x^4+3*x^3+x^2+2*x+6, x^3+5*x^2+5*x+1], + sage: M = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, x^3+5*x^2+5*x+1], ....: [6*x+1, x^2+4*x+1 ], ....: [6, 6 ]]) sage: M.is_popov(row_wise=False) @@ -1775,7 +1775,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): One can forbid zero rows (or columns if not working row-wise):: - sage: N = Matrix(pR, [[x^4+3*x^3+x^2+2*x+6, 6*x+1 ], + sage: N = matrix(pR, [[x^4+3*x^3+x^2+2*x+6, 6*x+1 ], ....: [5*x^2+5*x+1, x^2+4*x+1 ], ....: [0, 0 ]]) @@ -1887,7 +1887,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], + sage: M = matrix(pR, [[x^4+6*x^3+4*x+4, 3*x+6, 3 ], ....: [0, x^2+5*x+5, 2 ], ....: [0, 0, x+5]]) @@ -1898,7 +1898,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: M.is_hermite(row_wise=False, lower_echelon=True) False - sage: N = Matrix(pR, [[x+5, 0, 0 ], + sage: N = matrix(pR, [[x+5, 0, 0 ], ....: [2, x^4+6*x^3+4*x+4, 0 ], ....: [3, 3*x^3+6, x^2+5*x+5]]) sage: N.is_hermite() @@ -2002,7 +2002,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [ + sage: M = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) sage: P, U = M.weak_popov_form(transformation=True) @@ -2287,7 +2287,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: M = Matrix(pR, [ + sage: M = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) @@ -2653,11 +2653,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 3, 2, + sage: A = matrix(pR, 3, 2, ....: [[ 3*x^3 + 3*x, 2*x^3 + 4], ....: [ 3*x^3 + 6*x + 5, 6*x^3 + 5*x^2 + 1], ....: [ 2*x^3 + 2*x + 6, 3*x^2 + 2*x + 2]]) - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[ 3, x + 3, 6], ....: [3*x^3 + 3*x + 1, 4*x^2 + 3*x, 6*x^3 + x + 4], ....: [ 4*x^2 + x + 4, 3*x^2 + 4*x, 3*x^2 + 3*x + 2]]) @@ -2748,10 +2748,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Case where `B` is a square, column reduced matrix:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 2, 3, + sage: A = matrix(pR, 2, 3, ....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6], ....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]]) - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[4*x^2 + 3*x + 3, 3*x^2 + 3*x + 1, 4*x^2 + x + 4], ....: [6*x^2 + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [5*x^2 + 3*x + 6, 6*x^2 + x + 4, 3*x^2 + 3*x + 2]]) @@ -2770,7 +2770,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ValueError: column dimension of self should be the column dimension of the input matrix - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4], ....: [x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]]) @@ -2791,7 +2791,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): With a nonsingular but also non-reduced matrix, there exists a solution, but it might not be unique:: - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[ 5, 0, 2*x + 6], ....: [ 4*x, 3*x^2 + 4*x + 5, x + 1], ....: [3*x^2 + 5*x + 2, 6*x^3 + 4*x + 6, 3]]) @@ -2809,10 +2809,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(3)) True - sage: Q2 = Matrix(pR, 2, 3, + sage: Q2 = matrix(pR, 2, 3, ....: [[6*x^2 + 3*x + 1, 4*x^2 + 3*x + 6, 5*x + 1], ....: [ x^2 + 5*x + 3, 5*x^2 + 3*x + 2, x + 2]]) - sage: R2 = Matrix(pR, 2, 3, + sage: R2 = matrix(pR, 2, 3, ....: [[ 5*x, 3*x + 4, 5], ....: [4*x + 6, 5*x, 4]]) sage: A == Q2*B + R2 @@ -2850,11 +2850,11 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): remainder, in which case this method will find it via normal form computation:: - sage: B = Matrix(pR, 1, 2, [[x, x]]) - sage: A = Matrix(pR, 1, 2, [[x, x+2]]) + sage: B = matrix(pR, 1, 2, [[x, x]]) + sage: A = matrix(pR, 1, 2, [[x, x+2]]) sage: A.right_quo_rem(B) ([1], [0 2]) - sage: A == 1*B + Matrix([[0,2]]) + sage: A == 1*B + matrix([[0,2]]) True .. SEEALSO:: @@ -2903,10 +2903,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 2, 3, + sage: A = matrix(pR, 2, 3, ....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6], ....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]]) - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[4*x^2 + 3*x + 3, 3*x^2 + 3*x + 1, 4*x^2 + x + 4], ....: [6*x^2 + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [5*x^2 + 3*x + 6, 6*x^2 + x + 4, 3*x^2 + 3*x + 2]]) @@ -2920,7 +2920,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: A == Q*B+R and R.degree() < 2 True - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[4*x + 3*x + 3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4], ....: [6*x + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]]) @@ -2981,10 +2981,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: A = Matrix(pR, 2, 3, + sage: A = matrix(pR, 2, 3, ....: [[3*x^3 + 3*x, 3*x^3 + 6*x + 5, 2*x^3 + 2*x + 6], ....: [2*x^3 + 4, 6*x^3 + 5*x^2 + 1, 3*x^2 + 2*x + 2]]) - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[4*x + 3*x + 3, 3*x^3 + 3*x + 1, 4*x^2 + x + 4], ....: [6*x + 2*x + 3, 4*x^2 + 3*x, 3*x^2 + 4*x], ....: [6, 6*x^3 + x + 4, 3*x^2 + 3*x + 2]]) @@ -3005,7 +3005,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): With a nonsingular but also non-reduced matrix, there exists a solution and one is found by this method, but it might not be unique:: - sage: B = Matrix(pR, 3, 3, + sage: B = matrix(pR, 3, 3, ....: [[ 5, 0, 2*x + 6], ....: [ 4*x, 3*x^2 + 4*x + 5, x + 1], ....: [3*x^2 + 5*x + 2, 6*x^3 + 4*x + 6, 3]]) @@ -3023,10 +3023,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: A == Q*B+R and all(cdegR[i] < cdegB[i] for i in range(3)) True - sage: Q2 = Matrix(pR, 2, 3, + sage: Q2 = matrix(pR, 2, 3, ....: [[6*x^2 + 3*x + 1, 4*x^2 + 3*x + 6, 5*x + 1], ....: [ x^2 + 5*x + 3, 5*x^2 + 3*x + 2, x + 2]]) - sage: R2 = Matrix(pR, 2, 3, + sage: R2 = matrix(pR, 2, 3, ....: [[ 5*x, 3*x + 4, 5], ....: [4*x + 6, 5*x, 4]]) sage: A == Q2*B + R2 @@ -3061,9 +3061,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): columns), even when there is a solution, this method might not find it:: - sage: B = Matrix(pR, 1, 2, [[x, x]]) - sage: A = Matrix(pR, 1, 2, [[x, x+2]]) - sage: A == 1*B + Matrix([[0,2]]) # a valid quo_rem + sage: B = matrix(pR, 1, 2, [[x, x]]) + sage: A = matrix(pR, 1, 2, [[x, x+2]]) + sage: A == 1*B + matrix([[0,2]]) # a valid quo_rem True sage: A._right_quo_rem_solve(B) Traceback (most recent call last): @@ -3147,10 +3147,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: B = Matrix(pR, [ + sage: B = matrix(pR, [ ....: [ 6*x+4, 5*x^3+5*x, 6*x^2+2*x+2], ....: [4*x^2+5*x+2, x^4+5*x^2+2*x+4, 4*x^3+6*x^2+6*x+5]]) - sage: A = Matrix(pR, 1, 3, [ + sage: A = matrix(pR, 1, 3, [ ....: [3*x^4+3*x^3+4*x^2+5*x+1, x^4+x^3+5*x^2+4*x+4, 4*x^4+2*x^3+x]]) sage: Q, R = A.reduce(B,return_quotient=True); R @@ -3192,7 +3192,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): several columns, and a matrix `B` which does not have full column rank (its column-wise Popov form has a zero column):: - sage: A = Matrix(pR, 2, 2, + sage: A = matrix(pR, 2, 2, ....: [[5*x^3 + 2*x^2 + 4*x + 1, x^3 + 4*x + 4], ....: [2*x^3 + 5*x^2 + 2*x + 4, 2*x^3 + 3*x + 2]]) sage: (Q,R) = A.reduce(B,row_wise=False, return_quotient=True); R @@ -3311,13 +3311,13 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): computing minimal approximant bases, 2006]:: sage: order = 8; shifts = [1,1,0,0,0] - sage: pmat = Matrix(pR, 5, 1, [ + sage: pmat = matrix(pR, 5, 1, [ ....: pR([35, 0, 41, 87, 3, 42, 22, 90]), ....: pR([80, 15, 62, 87, 14, 93, 24, 0]), ....: pR([42, 57, 90, 87, 22, 80, 71, 53]), ....: pR([37, 72, 74, 6, 5, 75, 23, 47]), ....: pR([36, 10, 74, 1, 29, 44, 87, 74])]) - sage: appbas = Matrix(pR, [ + sage: appbas = matrix(pR, [ ....: [x+47, 57, 58*x+44, 9*x+23, 93*x+76], ....: [ 15, x+18, 52*x+23, 15*x+58, 93*x+88], ....: [ 17, 86, x^2+77*x+16, 76*x+29, 90*x+78], @@ -3332,7 +3332,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): not an approximant basis since its rows generate a module strictly contained in the set of approximants for ``pmat`` at order 8:: - sage: M = x^8 * Matrix.identity(pR, 5) + sage: M = x^8 * matrix.identity(pR, 5) sage: M.is_minimal_approximant_basis(pmat, 8) # needs sage.libs.pari False @@ -3340,7 +3340,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): its column-wise approximant bases at order 8 are all `1\times 1` matrices `[c x^8]` for some nonzero field element `c`:: - sage: M = Matrix(pR, [x^8]) + sage: M = matrix(pR, [x^8]) sage: M.is_minimal_approximant_basis( ....: pmat, 8, row_wise=False, normal_form=True) True @@ -3360,7 +3360,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): ValueError: shifts length should be the column dimension of the input matrix - sage: Matrix(pR, [x^8]).is_minimal_approximant_basis(pmat, 8) + sage: matrix(pR, [x^8]).is_minimal_approximant_basis(pmat, 8) Traceback (most recent call last): ... ValueError: column dimension should be the row dimension of the @@ -3522,7 +3522,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: pR. = GF(7)[] sage: order = [4, 3]; shifts = [-1, 2, 0] - sage: F = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], + sage: F = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2 + 4*x + 1], ....: [ 2*x^2 + 2*x + 3, 6*x^2 + 6*x + 3], ....: [4*x^3 + x + 1, 4*x^2 + 2*x + 3]]) sage: P = F.minimal_approximant_basis(order, shifts) @@ -3674,7 +3674,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): degrees of the output basis:: sage: order = [4, 1, 2]; shifts = [-3, 4] - sage: pmat = Matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2, 3*x^2 + 4], + sage: pmat = matrix(pR, [[5*x^3 + 4*x^2 + 4*x + 6, 5*x^2, 3*x^2 + 4], ....: [2*x^3 + 2*x^2 + 2*x + 3, x^3 + 6, 6*x + 3]]) sage: P, rdeg = pmat._approximant_basis_iterative(order, shifts) sage: P.is_minimal_approximant_basis(pmat, order, shifts) @@ -4180,22 +4180,22 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(97)[] - sage: pmat = Matrix(pR, [[1], [x], [x**2]]) + sage: pmat = matrix(pR, [[1], [x], [x**2]]) - sage: kerbas = Matrix(pR, [[x,-1,0], [0,x,-1]]) + sage: kerbas = matrix(pR, [[x,-1,0], [0,x,-1]]) sage: kerbas.is_minimal_kernel_basis(pmat) True A matrix in Popov form which has the right rank, all rows in the kernel, but does not generate the kernel:: - sage: kerbas = Matrix(pR, [[x**2,0,-1], [0,x,-1]]) + sage: kerbas = matrix(pR, [[x**2,0,-1], [0,x,-1]]) sage: kerbas.is_minimal_kernel_basis(pmat) False Shifts and right kernel bases are supported (with ``row_wise``), and one can test whether the kernel basis is normalized in shifted-Popov form (with ``normal_form``):: - sage: kerbas = Matrix(pR, [[-x,-x**2], [1,0], [0,1]]) + sage: kerbas = matrix(pR, [[-x,-x**2], [1,0], [0,1]]) sage: kerbas.is_minimal_kernel_basis( ....: pmat.transpose(), row_wise=False, ....: normal_form=True, shifts=[0,1,2]) @@ -4302,18 +4302,18 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): EXAMPLES:: sage: pR. = GF(7)[] - sage: pmat = Matrix([[(x+1)*(x+3)], [(x+1)*(x+3)+1]]) + sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+3)+1]]) sage: pmat.minimal_kernel_basis() [6*x^2 + 3*x + 3 x^2 + 4*x + 3] - sage: pmat = Matrix([[(x+1)*(x+3)], [(x+1)*(x+4)]]) + sage: pmat = matrix([[(x+1)*(x+3)], [(x+1)*(x+4)]]) sage: pmat.minimal_kernel_basis() [6*x + 3 x + 3] sage: pmat.minimal_kernel_basis(row_wise=False) [] - sage: pmat = Matrix(pR, [[1, x, x**2]]) + sage: pmat = matrix(pR, [[1, x, x**2]]) sage: pmat.minimal_kernel_basis(row_wise=False, normal_form=True) [x 0] [6 x] @@ -4327,22 +4327,22 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Some particular cases (matrix is zero, dimension is zero, column is zero):: - sage: Matrix(pR, 2, 1).minimal_kernel_basis() + sage: matrix(pR, 2, 1).minimal_kernel_basis() [1 0] [0 1] - sage: Matrix(pR, 2, 0).minimal_kernel_basis() + sage: matrix(pR, 2, 0).minimal_kernel_basis() [1 0] [0 1] - sage: Matrix(pR, 0, 2).minimal_kernel_basis() + sage: matrix(pR, 0, 2).minimal_kernel_basis() [] - sage: Matrix(pR, 3, 2, [[1,0],[1,0],[1,0]]).minimal_kernel_basis() + sage: matrix(pR, 3, 2, [[1,0],[1,0],[1,0]]).minimal_kernel_basis() [6 1 0] [6 0 1] - sage: Matrix(pR, 3, 2, [[x,0],[1,0],[x+1,0]]).minimal_kernel_basis() + sage: matrix(pR, 3, 2, [[x,0],[1,0],[x+1,0]]).minimal_kernel_basis() [6 x 0] [6 6 1] From 8ce0c7dd083be70de2da938df686e3d38579d554 Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Sun, 16 Feb 2025 20:44:45 +0100 Subject: [PATCH 415/507] fix lint (too many blank lines) --- src/sage/matrix/matrix_polynomial_dense.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 06fdc71a15f..17a85cc696b 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4137,7 +4137,6 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): return intbas, rdeg - def is_minimal_kernel_basis(self, pmat, shifts=None, From b7ae258f69610e6373bdbbd7341e16964d8e1c79 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:56:51 +0700 Subject: [PATCH 416/507] Refactor test-git-no-uncommitted-changes to independent script --- .github/workflows/ci-meson.yml | 2 +- Makefile | 8 +------- tools/test-git-no-uncommitted-changes | 10 ++++++++++ 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100755 tools/test-git-no-uncommitted-changes diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 13ae8d9dc92..edac659b006 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -79,7 +79,7 @@ jobs: id: check_update_meson run: | python3 tools/update-meson.py - make test-git-no-uncommitted-changes + ./tools/test-git-no-uncommitted-changes continue-on-error: true - name: Show files changed by update-meson diff --git a/Makefile b/Makefile index 15dbbb411c0..4b5e9350d3e 100644 --- a/Makefile +++ b/Makefile @@ -254,13 +254,7 @@ TEST_TARGET = $@ TEST = ./sage -t --logfile=$(TEST_LOG) $(TEST_FLAGS) --optional=$(TEST_OPTIONAL) $(TEST_FILES) test-git-no-uncommitted-changes: - @UNCOMMITTED=$$(git status --porcelain); \ - if [ -n "$$UNCOMMITTED" ]; then \ - echo "Error: the git repo has uncommitted changes:"; \ - echo "$$UNCOMMITTED"; \ - echo; \ - exit 1; \ - fi + ./tools/test-git-no-uncommitted-changes test: all @echo '### make $(TEST_TARGET): Running $(TEST)' >> $(TEST_LOG) diff --git a/tools/test-git-no-uncommitted-changes b/tools/test-git-no-uncommitted-changes new file mode 100755 index 00000000000..f79997062f5 --- /dev/null +++ b/tools/test-git-no-uncommitted-changes @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +# Test that there is no uncommitted changes in the repository. Return failure if there is. +# Can also be invoked with `make test-git-no-uncommitted-changes` from top level. +UNCOMMITTED="$(git status --porcelain)"; +if [ -n "$UNCOMMITTED" ]; then + echo "Error: the git repo has uncommitted changes:"; + echo "$UNCOMMITTED"; + echo; + exit 1; +fi From 44e75720cd63667e1924d8752eceadbc3862e186 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:57:05 +0700 Subject: [PATCH 417/507] Show newly created files in git diff, if any --- .github/workflows/ci-meson.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index edac659b006..eb8784fb650 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -86,6 +86,7 @@ jobs: if: ${{ steps.check_update_meson.outcome == 'failure' }} shell: bash -l {0} run: | + git add --intent-to-add . # also show newly created files in git diff git status git diff From c3705802e4af59ef280bd973b593fcb2fe922f79 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:59:16 +0700 Subject: [PATCH 418/507] Merge two report steps --- .github/workflows/ci-meson.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index eb8784fb650..228767fc32e 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -82,14 +82,6 @@ jobs: ./tools/test-git-no-uncommitted-changes continue-on-error: true - - name: Show files changed by update-meson - if: ${{ steps.check_update_meson.outcome == 'failure' }} - shell: bash -l {0} - run: | - git add --intent-to-add . # also show newly created files in git diff - git status - git diff - - name: Verify dependencies shell: bash -l {0} run: pip check @@ -101,12 +93,14 @@ jobs: rm -R ./src/sage_setup/ ./sage -t --all -p4 - - name: Report update-meson failure + - name: Show files changed by update-meson if: ${{ steps.check_update_meson.outcome == 'failure' }} shell: bash -l {0} + # must be after "Test" since we still want to run test when check_update_meson fails run: | - # Please see step 'Show files changed by update-meson' above and apply the changes, - # or run tools/update-meson.py locally + git add --intent-to-add . # also show newly created files in git diff + git status + git diff false - name: Upload log From 7ac3f7db87959a823d32af7e94b7c416f6502905 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:23:41 +0700 Subject: [PATCH 419/507] Fix a nonfunctional long time doctest tag --- src/sage/modular/btquotients/pautomorphicform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 3d49d59bdfc..8358822f88e 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -2497,7 +2497,7 @@ def _make_invariant(self, F): sage: H = X.harmonic_cocycles(2,prec = 5) sage: A = X.padic_automorphic_forms(2,prec = 5) sage: h = H.basis()[0] - sage: A.lift(h) # indirect doctest long time + sage: A.lift(h) # indirect doctest, long time p-adic automorphic form of cohomological weight 0 """ S = self._source.get_stabilizers() From fc0e64084daa7334375da6937cf03b959f59485d Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:00:40 +0700 Subject: [PATCH 420/507] Apply suggestion --- src/sage/modular/btquotients/pautomorphicform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 8358822f88e..a059d4a5064 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -2497,7 +2497,7 @@ def _make_invariant(self, F): sage: H = X.harmonic_cocycles(2,prec = 5) sage: A = X.padic_automorphic_forms(2,prec = 5) sage: h = H.basis()[0] - sage: A.lift(h) # indirect doctest, long time + sage: A.lift(h) # indirect doctest, long time p-adic automorphic form of cohomological weight 0 """ S = self._source.get_stabilizers() From ae47c25043c856c044bb1064681b320af0301f00 Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Mon, 17 Feb 2025 15:32:41 +0100 Subject: [PATCH 421/507] Modifications suggested by David Coudert Copy of the input graph g only if g.has_multiple_edges(). New copy is called g as the input. --- src/sage/graphs/line_graph.pyx | 72 ++++++++++++++++------------------ 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index 57547206f02..e3788d9c680 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -263,7 +263,7 @@ def is_line_graph(g, certificate=False): return True -def line_graph(g, labels=True, origlabels=False): +def line_graph(g, labels=True, return_labels=False): """ Return the line graph of the (di)graph ``g`` (multiedges and loops allowed). @@ -275,12 +275,12 @@ def line_graph(g, labels=True, origlabels=False): of multiple edges, the vertices of the line graph will be triples ``(u,v,an integer)``. - - ``origlabels`` -- boolean (default: ``False``); wether edge labels should - be stored or not. If g has multiple edges, if ``origlabels=True``, the + - ``return_labels`` -- boolean (default: ``False``); whether edge labels should + be stored or not. If g has multiple edges and if ``return_labels=True``, the method returns a list the first element of which is the line-graph of g - and the second element is a dictionary {vertex of the line-graph: label of - the corresponding original edge}. If ``origlabels=False``, the method - returns only the line-graph. + and the second element is a dictionary {vertex of the line-graph: former + corresponding edge with its original label}. If ``return_labels=False``, + the method returns only the line-graph. The line graph of an undirected graph G is an undirected simple graph H such that the vertices of H are the edges of G and two vertices e and f of H are @@ -322,7 +322,7 @@ def line_graph(g, labels=True, origlabels=False): (1, 2, None), (1, 3, None), (2, 3, None)] - sage: h.am() # needs sage.modules + sage: h.am() # needs sage.modules [0 1 1 1 1 0] [1 0 1 1 0 1] [1 1 0 0 1 1] @@ -332,7 +332,7 @@ def line_graph(g, labels=True, origlabels=False): sage: h2 = g.line_graph(labels=False) sage: h2.vertices(sort=True) [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] - sage: h2.am() == h.am() # needs sage.modules + sage: h2.am() == h.am() # needs sage.modules True sage: g = DiGraph([[1..4], lambda i,j: i < j]) sage: h = g.line_graph() @@ -350,14 +350,14 @@ def line_graph(g, labels=True, origlabels=False): ((2, 3, None), (3, 4, None), None)] Examples with multiple edges:: - - sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph() + + sage: L=line_graph(Graph([(0,1),(0,1),(1,2)],multiedges=True)) sage: L.edges() [((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] sage: G = Graph([(0,1),(0,1,'a'),(0,1,'b'),(0,2),(1,2,'c')], ....: multiedges=True) - sage: L = G.line_graph(False,True) + sage: L=line_graph(G,False,True) sage: L[0].edges() [((0, 1, 1), (0, 1, 2), None), ((0, 1, 0), (0, 1, 2), None), ((0, 1, 2), (0, 2, 3), None), ((0, 1, 2), (1, 2, 4), None), ((0, 1, 0), @@ -371,18 +371,18 @@ def line_graph(g, labels=True, origlabels=False): (0, 2, 3): None, (1, 2, 4): 'c'} sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True) - sage: g.line_graph().edges() + sage: line_graph(g).edges() [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] An example with a loop:: - + sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True) - sage: L = g.line_graph() + sage: L = line_graph(g) sage: L.edges() [((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None), ((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None), ((0, 2, None), (1, 2, None), None)] - + TESTS: :issue:`13787`:: @@ -399,29 +399,25 @@ def line_graph(g, labels=True, origlabels=False): cdef dict origlabels_dic = {} # stores original labels of edges in case of multiple edges multiple = g.has_multiple_edges() - if multiple: - labels = True - - h = g.copy() - # replace labels of edges of g with integers in range(len(g.edges())) in order to distinguish multiple edges. if multiple: - for i, e in enumerate(h.edges()): - f = (e[0], e[1], i) - h.delete_edge(e) - h.add_edge(f) - if origlabels: - origlabels_dic[f] = e[2] - - if h._directed: + # As the edges of g are the vertices of its line graph, we need to distinguish between the mutliple edges of g. + # To this aim, we assign to each edge of g an integer label (between 0 and g.size() - 1) and set labels to True + # in order to keep these labels during the construction of the line graph. + labels = True + origlabels_dic = {(u, v, id): (u, v, label) + for id, (u, v, label) in enumerate(g.edge_iterator())} + g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True) + + if g._directed: from sage.graphs.digraph import DiGraph G = DiGraph() - G.add_vertices(h.edge_iterator(labels=labels)) - for v in h: + G.add_vertices(g.edge_iterator(labels=labels)) + for v in g: # Connect appropriate incident edges of the vertex v - G.add_edges((e, f) for e in h.incoming_edge_iterator(v, labels=labels) - for f in h.outgoing_edge_iterator(v, labels=labels)) - if origlabels and multiple: + G.add_edges((e, f) for e in g.incoming_edge_iterator(v, labels=labels) + for f in g.outgoing_edge_iterator(v, labels=labels)) + if return_labels and multiple: return [G, origlabels_dic] else: return G @@ -438,7 +434,7 @@ def line_graph(g, labels=True, origlabels=False): # pair in the dictionary of conflicts # 1) List of vertices in the line graph - for e in h.edge_iterator(labels=labels): + for e in g.edge_iterator(labels=labels): if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -450,13 +446,13 @@ def line_graph(g, labels=True, origlabels=False): elist.append(e) G.add_vertices(elist) - + # 2) adjacencies in the line graph - for v in h: + for v in g: elist = [] # Add the edge to the list, according to hashes, as previously - for e in h.edge_iterator(v, labels=labels): # iterates over the edges incident to v + for e in g.edge_iterator(v, labels=labels): # iterates over the edges incident to v if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -472,7 +468,7 @@ def line_graph(g, labels=True, origlabels=False): for y in elist: G.add_edge(x, y) - if origlabels and multiple: + if return_labels and multiple: return [G, origlabels_dic] else: return G From 738731bbc8306fbede604b475352c5694fde1c2f Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Mon, 17 Feb 2025 18:29:12 +0100 Subject: [PATCH 422/507] line_graph(g) -> g.line_graph() dans Examples --- src/sage/graphs/line_graph.pyx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index e3788d9c680..a2653422a4b 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -351,13 +351,13 @@ def line_graph(g, labels=True, return_labels=False): Examples with multiple edges:: - sage: L=line_graph(Graph([(0,1),(0,1),(1,2)],multiedges=True)) + sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph() sage: L.edges() [((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] sage: G = Graph([(0,1),(0,1,'a'),(0,1,'b'),(0,2),(1,2,'c')], ....: multiedges=True) - sage: L=line_graph(G,False,True) + sage: L = G.line_graph(False,True) sage: L[0].edges() [((0, 1, 1), (0, 1, 2), None), ((0, 1, 0), (0, 1, 2), None), ((0, 1, 2), (0, 2, 3), None), ((0, 1, 2), (1, 2, 4), None), ((0, 1, 0), @@ -371,13 +371,13 @@ def line_graph(g, labels=True, return_labels=False): (0, 2, 3): None, (1, 2, 4): 'c'} sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True) - sage: line_graph(g).edges() + sage: g.line_graph().edges() [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] An example with a loop:: sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True) - sage: L = line_graph(g) + sage: L = g.line_graph() sage: L.edges() [((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None), ((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None), @@ -408,7 +408,7 @@ def line_graph(g, labels=True, return_labels=False): origlabels_dic = {(u, v, id): (u, v, label) for id, (u, v, label) in enumerate(g.edge_iterator())} g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True) - + if g._directed: from sage.graphs.digraph import DiGraph G = DiGraph() @@ -446,13 +446,13 @@ def line_graph(g, labels=True, return_labels=False): elist.append(e) G.add_vertices(elist) - + # 2) adjacencies in the line graph for v in g: elist = [] # Add the edge to the list, according to hashes, as previously - for e in g.edge_iterator(v, labels=labels): # iterates over the edges incident to v + for e in g.edge_iterator(v, labels=labels): # iterates over the edges incident to v if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): From 5daa54618a604af5119d6e9a236a048504d428bc Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Mon, 17 Feb 2025 20:56:52 +0100 Subject: [PATCH 423/507] Remove dead mailing lists. AFAICT, the `@scipy.org` mailing lists are dead since 2018 technically ipython-dev was migrated to `@python.org`, but had no messages since 2023 and -user was never migrated. --- build/pkgs/ipython/SPKG.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build/pkgs/ipython/SPKG.rst b/build/pkgs/ipython/SPKG.rst index fa370a16841..5a1a7cb7913 100644 --- a/build/pkgs/ipython/SPKG.rst +++ b/build/pkgs/ipython/SPKG.rst @@ -33,7 +33,3 @@ Upstream Contact ---------------- http://ipython.org - -ipython-dev@scipy.org - -ipython-user@scipy.org From 5e6c51e920a1a97edd4fb355e0eee49a32df0632 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Tue, 18 Feb 2025 07:24:05 +0700 Subject: [PATCH 424/507] Hide more chronic diff on CI --- .github/workflows/doc-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index f46317ecda7..af4584dc6fa 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -230,6 +230,7 @@ jobs: -e '\;; d' \ -e 's;#L[0-9]*";";' \ -e 's;tab-set--[0-9]*-;tab-set-;' \ + -e 's;"tab-set--[0-9]*";"tab-set";' \ && git commit -a -m 'wipe-out') # Since HEAD is at commit 'wipe-out', HEAD~1 is commit 'new' (new doc), HEAD~2 is commit 'old' (old doc) (cd doc && git diff $(git rev-parse HEAD~2) -- "*.html") > diff.txt From 6db279f74c8ef9b5f12e8a12fe72dbe012977c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 18 Feb 2025 08:33:40 +0100 Subject: [PATCH 425/507] add missing import --- src/sage/combinat/posets/posets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 0c7e6c4f414..5bea9176d03 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -7319,6 +7319,7 @@ def apozeta_polynomial(self): sage: parent(_) Univariate Polynomial Ring in q over Rational Field """ + from sage.functions.other import binomial R = PolynomialRing(QQ, 'q') q = R.gen() From 2fc50b022d411feb165fd36656e2bd8423ddc916 Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Tue, 18 Feb 2025 11:05:53 +0100 Subject: [PATCH 426/507] Modifications to pass the tests (hopefully) - Removed white spaces in blanklines (in the Examples) - Add import command for parent --- src/sage/graphs/line_graph.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index a2653422a4b..b28cd239a48 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -350,7 +350,7 @@ def line_graph(g, labels=True, return_labels=False): ((2, 3, None), (3, 4, None), None)] Examples with multiple edges:: - + sage: L = Graph([(0,1),(0,1),(1,2)],multiedges=True).line_graph() sage: L.edges() [((0, 1, 0), (0, 1, 1), None), ((0, 1, 1), (1, 2, 2), None), @@ -375,14 +375,14 @@ def line_graph(g, labels=True, return_labels=False): [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] An example with a loop:: - + sage: g = Graph([(0,0),(0,1),(0,2),(1,2)],multiedges=True,loops=True) sage: L = g.line_graph() sage: L.edges() [((0, 0, None), (0, 1, None), None), ((0, 0, None), (0, 2, None), None), ((0, 1, None), (0, 2, None), None), ((0, 1, None), (1, 2, None), None), ((0, 2, None), (1, 2, None), None)] - + TESTS: :issue:`13787`:: @@ -407,6 +407,7 @@ def line_graph(g, labels=True, return_labels=False): labels = True origlabels_dic = {(u, v, id): (u, v, label) for id, (u, v, label) in enumerate(g.edge_iterator())} + from sage.structure.element cimport parent g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True) if g._directed: From d518ffe710a4671e357b410bf279285e72fabe9f Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Tue, 18 Feb 2025 19:11:04 -0700 Subject: [PATCH 427/507] Changed :trac: into :issue:. --- src/sage/rings/number_field/number_field_element_quadratic.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 9bbab40d011..35b84ba6ee9 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -449,7 +449,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: len(set(K.random_element() for _ in range(100))) >= 40 True - Verify that :trac:`30017` is fixed:: + Verify that :issue:`30017` is fixed:: sage: all(K.random_element().is_integral() for s in range(100)) False From 60487a7f6e77eba2fdaa75471a067c47f2d900c9 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Tue, 18 Feb 2025 23:57:29 -0700 Subject: [PATCH 428/507] Fixed issue causing logarithm of base 0 to not give either of the correct answers. --- src/sage/misc/functional.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 15f665b5df1..cdaa44a4348 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1148,9 +1148,15 @@ def log(*args, **kwds): -Infinity sage: log(int(0), 1/2) +Infinity + + Check if sub-issue detailed in :issue:`38971` is fixed:: + sage: log(6, base=0) + 0 + sage: log(e, base=0) + 0 """ base = kwds.pop('base', None) - if base: + if base is not None: args = args + (base,) if not args: raise TypeError("log takes at least 1 arguments (0 given)") From 65a494669ec20cdb3bcd4ccbfd1f6f0f018e6ad9 Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Wed, 19 Feb 2025 08:08:36 +0100 Subject: [PATCH 429/507] Moved cimport parent at the top of the file --- src/sage/graphs/line_graph.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index b28cd239a48..e4cb99fb05c 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -1,4 +1,6 @@ # cython: binding=True + +from sage.structure.element cimport parent r""" Line graphs @@ -407,7 +409,6 @@ def line_graph(g, labels=True, return_labels=False): labels = True origlabels_dic = {(u, v, id): (u, v, label) for id, (u, v, label) in enumerate(g.edge_iterator())} - from sage.structure.element cimport parent g = parent(g)([g, origlabels_dic.keys()], format='vertices_and_edges', multiedges=True) if g._directed: From 0421445dafa0604ae80739520370283133b881b1 Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Wed, 19 Feb 2025 11:14:04 +0100 Subject: [PATCH 430/507] Modification of examples and new location for import of parent In case return_labels == True, the returned dictionary is different from a previous version. An example output had to be modified accordingly. --- src/sage/graphs/line_graph.pyx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index e4cb99fb05c..ae799621708 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -1,6 +1,4 @@ # cython: binding=True - -from sage.structure.element cimport parent r""" Line graphs @@ -129,6 +127,7 @@ Functions --------- """ +from sage.structure.element cimport parent def is_line_graph(g, certificate=False): r""" @@ -367,11 +366,11 @@ def line_graph(g, labels=True, return_labels=False): (1, 2, 4), None), ((0, 1, 0), (0, 2, 3), None), ((0, 1, 0), (1, 2, 4), None), ((0, 2, 3), (1, 2, 4), None)] sage: L[1] - {(0, 1, 0): None, - (0, 1, 1): 'a', - (0, 1, 2): 'b', - (0, 2, 3): None, - (1, 2, 4): 'c'} + {(0, 1, 0): (0, 1, None), + (0, 1, 1): (0, 1, 'b'), + (0, 1, 2): (0, 1, 'a'), + (0, 2, 3): (0, 2, None), + (1, 2, 4): (1, 2, 'c')} sage: g = DiGraph([(0,1),(0,1),(1,2)],multiedges=True) sage: g.line_graph().edges() [((0, 1, 1), (1, 2, 2), None), ((0, 1, 0), (1, 2, 2), None)] From 34bece3ad817112be90d855641fde8ffbced08eb Mon Sep 17 00:00:00 2001 From: Fabien Vignes-Tourneret Date: Wed, 19 Feb 2025 13:20:44 +0100 Subject: [PATCH 431/507] Added a second blank line below an import statement --- src/sage/graphs/line_graph.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index ae799621708..bc655b50003 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -129,6 +129,7 @@ Functions from sage.structure.element cimport parent + def is_line_graph(g, certificate=False): r""" Check whether the graph `g` is a line graph. From d796a29cf8503ddaef7c219d42ef41fee6b0260e Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 19 Feb 2025 23:29:31 +0700 Subject: [PATCH 432/507] Disable running test step when update-meson check fails --- .github/workflows/ci-meson.yml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 228767fc32e..3c433e130af 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -76,11 +76,14 @@ jobs: # this step must be after build, because meson.build creates a number of __init__.py files # that is needed to make tools/update-meson.py run correctly shell: bash -l {0} - id: check_update_meson run: | python3 tools/update-meson.py - ./tools/test-git-no-uncommitted-changes - continue-on-error: true + if ! ./tools/test-git-no-uncommitted-changes; then + git add --intent-to-add . # also show newly created files in git diff + git status + git diff + false + fi - name: Verify dependencies shell: bash -l {0} @@ -93,16 +96,6 @@ jobs: rm -R ./src/sage_setup/ ./sage -t --all -p4 - - name: Show files changed by update-meson - if: ${{ steps.check_update_meson.outcome == 'failure' }} - shell: bash -l {0} - # must be after "Test" since we still want to run test when check_update_meson fails - run: | - git add --intent-to-add . # also show newly created files in git diff - git status - git diff - false - - name: Upload log uses: actions/upload-artifact@v4.5.0 if: failure() From 6bf8c80c19d9777bf3fdc2cec512d57c7dbfad67 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 19 Feb 2025 23:58:08 +0700 Subject: [PATCH 433/507] Update an outdated comment --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c0a86090cad..9190feb4b13 100644 --- a/.gitignore +++ b/.gitignore @@ -469,6 +469,6 @@ src/sage/calculus/transforms/__init__.py src/sage/calculus/__init__.py # Temporary files generated by Meson CI (needed to make test pass because -# ci-meson.yml runs a `make test-git-no-uncommitted-changes` step) +# ci-meson.yml runs a `./tools/test-git-no-uncommitted-changes` step) /.ccache /setup-miniconda-patched-environment-*.yml From 68d998a8eeb970adf9e9055882934685731ff7dc Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Wed, 19 Feb 2025 23:14:42 -0700 Subject: [PATCH 434/507] Mentioned relevant issue, removed whitespace at end of line --- src/sage/combinat/designs/latin_squares.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 631f19e9dda..e0e6b09162a 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -384,6 +384,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): matrices = construction()[:k] # Implements the construction from Theorem 5.2.4 of [KD2015]_. + # This was implemented to fix :issue:`26107` elif is_prime_power(n): F = list(GF(n)) @@ -392,7 +393,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): # This dictionary is used to convert from field elements to integers conv = {F[i] : i for i in range(n)} - + # Make the matrices matrices = [Matrix([[conv[F[i] + F[r]*F[j]] for i in range(n)] for j in range(n)]) for r in range(1, k+1)] From f429f5bba5a2ad75629080a8158c9ff661623f06 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Wed, 19 Feb 2025 23:24:00 -0700 Subject: [PATCH 435/507] Made comment more clear --- src/sage/combinat/designs/latin_squares.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index e0e6b09162a..937976b9206 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -383,8 +383,9 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): matrices = construction()[:k] - # Implements the construction from Theorem 5.2.4 of [KD2015]_. - # This was implemented to fix :issue:`26107` + # Implements the construction from Theorem 5.2.4 of [KD2015]_ for primw powers. + # This was implemented to fix :issue:`26107`, which pointed out that this + # function was unacceptably slow when n was a large prime power elif is_prime_power(n): F = list(GF(n)) From f2f28d63dab375e925b6e18596ccdd7ac465d63e Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Wed, 19 Feb 2025 23:52:37 -0700 Subject: [PATCH 436/507] Renamed parameter check->verify and variable originalA->original_a, also removed whitespace at end of line --- src/sage/groups/generic.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index e28fcd0c9bb..f5684e79ccb 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -686,7 +686,7 @@ def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=No raise ValueError("Pollard rho algorithm failed to find a logarithm") -def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs', check=True): +def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs', *, verify=True): r""" Totally generic discrete log function. @@ -702,8 +702,8 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i - ``op`` -- function of 2 arguments ``x``, ``y``, returning ``x*y`` in the group - ``algorithm`` -- string denoting what algorithm to use for prime-order logarithms: ``'bsgs'``, ``'rho'``, ``'lambda'`` - - ``check`` -- boolean (default: ``True``); whether to check that output is - correct before returning it. + - ``verify`` -- boolean (default: ``True``); whether to verify that output is + correct before returning it. ``a`` and ``base`` must be elements of some group with identity given by ``identity``, inverse of ``x`` by ``inverse(x)``, and group @@ -908,7 +908,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i from operator import mul, add, pow power = mul if operation in addition_names else pow mult = add if operation in addition_names else mul - originalA = a # Stores the original value of a so we can check the answer + original_a = a # Store the original value of a so we can verify the answer if op: mult = op power = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op) @@ -976,7 +976,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i l = l[:i + 1] from sage.arith.misc import CRT_list result = (CRT_list(l, mods) + offset) % ord - if (check and power(base, result) != originalA): + if (verify and power(base, result) != original_a): raise ValueError return result except ValueError: From 6844b0949bcc1af026fd2ff1b190378d6139e32a Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Thu, 20 Feb 2025 00:00:12 -0700 Subject: [PATCH 437/507] Modified ValueError message to use original_a and include bounds when provided, fixed failing doctests --- src/sage/groups/generic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index f5684e79ccb..6af3b1c863b 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -786,7 +786,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i sage: discrete_log(eta,eps,bounds=(0,100)) # needs sage.rings.number_field Traceback (most recent call last): ... - ValueError: no discrete log of -11515*a - 55224 found to base 5*a - 24 + ValueError: no discrete log of -11515*a - 55224 found to base 5*a - 24 with bounds (0, 100) But we can invert the base (and negate the result) instead:: @@ -897,7 +897,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i sage: discrete_log(a, base, bounds=(1,2), operation="*") Traceback (most recent call last): ... - ValueError: no discrete log of 2 found to base 3 + ValueError: no discrete log of 1 found to base 3 with bounds (1, 2) AUTHORS: @@ -980,7 +980,8 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i raise ValueError return result except ValueError: - raise ValueError("no discrete log of %s found to base %s" % (a, base)) + with_bounds = f" with bounds {bounds}" if bounds else "" + raise ValueError(f"no discrete log of {original_a} found to base {base}{with_bounds}") def discrete_log_generic(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs'): From 7471239ba7ed7795994a8bbae8c1a19dd4312f31 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 20 Feb 2025 14:32:33 +0700 Subject: [PATCH 438/507] Apply suggestions --- src/sage/rings/fraction_field.py | 36 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index fabe94c58da..625abbb3f54 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -575,8 +575,18 @@ def _convert_from_finite_precision_laurent_series(self, x): Uses the algorithm described in ``_. This may be changed to use Berlekamp--Massey algorithm or something else to compute Padé approximant in the future. + + TESTS:: + + sage: F = QQ['x'].fraction_field() + sage: R = LaurentSeriesRing(QQ, 'x') + sage: f = ~R(x^2 + x + 3); f + 1/3 - 1/9*x - 2/27*x^2 + 5/81*x^3 + ... + O(x^20) + sage: F._convert_from_finite_precision_laurent_series(f) + 1/(x^2 + x + 3) """ - integral_part, fractional_part = self(x.truncate(1)), x.truncate_neg(1) + integral_part = self(x.truncate(1)) + fractional_part = x.truncate_neg(1) if fractional_part.is_zero(): return integral_part return integral_part + ~self._convert_from_finite_precision_laurent_series(~fractional_part) @@ -678,7 +688,7 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: F(x) -1/2/(a^2 + a) - Conversion from power series to rational function field truncates, but is deprecated:: + Conversion from power series to rational function field gives an approximation:: sage: F. = Frac(QQ['x']) sage: R. = QQ[[]] @@ -687,8 +697,15 @@ def _element_constructor_(self, x, y=None, coerce=True): Power Series Ring in x over Rational Field sage: F(f) doctest:warning... - DeprecationWarning: Conversion from power series to rational function field is deprecated, use .truncate() instead + DeprecationWarning: Previously conversion from power series to rational function field truncates + instead of gives an approximation. Use .truncate() to recover the old behavior See https://github.com/sagemath/sage/issues/39485 for details. + 1/(x + 1) + + Previously, the power series was truncated. To recover the old behavior, use + :meth:`~sage.rings.power_series_ring_element.PowerSeries.truncate`:: + + sage: F(f.truncate()) -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 Conversion from Laurent series to rational function field gives an approximation:: @@ -716,12 +733,14 @@ def _element_constructor_(self, x, y=None, coerce=True): sage: K. = FunctionField(QQ) sage: R. = QQ[[]] - sage: f = 1/(x+1) + sage: f = 1/(x+1); f.parent() + Power Series Ring in x over Rational Field sage: K(f) doctest:warning... - DeprecationWarning: Conversion from power series to rational function field is deprecated, use .truncate() instead + DeprecationWarning: Previously conversion from power series to rational function field truncates + instead of gives an approximation. Use .truncate() to recover the old behavior See https://github.com/sagemath/sage/issues/39485 for details. - -x^19 + x^18 - x^17 + x^16 - x^15 + x^14 - x^13 + x^12 - x^11 + x^10 - x^9 + x^8 - x^7 + x^6 - x^5 + x^4 - x^3 + x^2 - x + 1 + 1/(x + 1) sage: f = Frac(R)(1/(x+1)) sage: K(f) 1/(x + 1) @@ -742,8 +761,9 @@ def _element_constructor_(self, x, y=None, coerce=True): from sage.misc.superseded import deprecation deprecation( 39485, - "Conversion from power series to rational function field is deprecated, use .truncate() instead", - ) + "Previously conversion from power series to rational function field truncates " + "instead of gives an approximation. Use .truncate() to recover the old behavior") + x = x.laurent_series() if isinstance(x, LaurentSeries): from sage.rings.infinity import infinity if x.prec() == infinity: From 383cefbe8368769f31983a488be91f54583b112c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:06:17 +0700 Subject: [PATCH 439/507] Add Ideal.radical and Ring.nilradical --- src/sage/categories/rings.py | 15 +++++++ .../rings/finite_rings/integer_mod_ring.py | 12 +++--- src/sage/rings/ideal.py | 11 +++++ .../polynomial/multi_polynomial_ideal.py | 3 +- src/sage/rings/quotient_ring.py | 43 +++++++++++++++---- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index e0769336c6f..ed606e14811 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -854,6 +854,21 @@ def zero_ideal(self): """ return self._ideal_class_(1)(self, [self.zero()]) + def nilradical(self): + """ + Return the nilradical of this ring. + + EXAMPLES:: + + sage: QQ['x,y'].nilradical() + Ideal (0) of Multivariate Polynomial Ring in x, y over Rational Field + + .. SEEALSO:: + + :meth:`~sage.categories.finite_dimensional_lie_algebras_with_basis.ParentMethods.nilradical` + """ + return self.zero_ideal().radical() + @cached_method def unit_ideal(self): """ diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 70ab41447b4..be4b6076343 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -750,12 +750,12 @@ def is_field(self, proof=None): self._factory_data[3]['category'] = Fields() else: if self.category().is_subcategory(Fields()): - raise ValueError("""THIS SAGE SESSION MIGHT BE SERIOUSLY COMPROMISED! -The order {} is not prime, but this ring has been put -into the category of fields. This may already have consequences -in other parts of Sage. Either it was a mistake of the user, -or a probabilistic primality test has failed. -In the latter case, please inform the developers.""".format(self.order())) + raise ValueError(("THIS SAGE SESSION MIGHT BE SERIOUSLY COMPROMISED!\n" + "The order {} is not prime, but this ring has been put\n" + "into the category of fields. This may already have consequences\n" + "in other parts of Sage. Either it was a mistake of the user,\n" + "or a probabilistic primality test has failed.\n" + "In the latter case, please inform the developers.").format(self.order())) return is_prime @cached_method diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 96520442c49..2d3ac20b9eb 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1695,6 +1695,17 @@ def residue_field(self): return ZZ.residue_field(self, check=False) raise NotImplementedError("residue_field() is only implemented for ZZ and rings of integers of number fields.") + def radical(self): + r""" + Return the radical of this ideal. + + EXAMPLES:: + + sage: ZZ.ideal(12).radical() + Principal ideal (6) of Integer Ring + """ + return self.ring().ideal(self.gen().radical()) + class Ideal_fractional(Ideal_generic): """ diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index d5415a8bea4..a2cf5b18a80 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -244,6 +244,7 @@ from sage.misc.misc_c import prod from sage.misc.verbose import get_verbose, verbose from sage.rings.ideal import Ideal_generic +from sage.rings.quotient_ring import QuotientRingIdeal_generic from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.noncommutative_ideals import Ideal_nc @@ -5632,7 +5633,7 @@ def weil_restriction(self): return result_ring.ideal(result) -class MPolynomialIdeal_quotient(MPolynomialIdeal): +class MPolynomialIdeal_quotient(QuotientRingIdeal_generic, MPolynomialIdeal): r""" An ideal in a quotient of a multivariate polynomial ring. diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 45f8147cde9..27967386bb5 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -1505,6 +1505,27 @@ class QuotientRingIdeal_generic(ideal.Ideal_generic): Ideal (3, 0) of Ring of integers modulo 9 """ + def _lift(self): + """ + Return an ideal of the cover ring that corresponds to this ideal. + + EXAMPLES:: + + sage: Zmod(15).ideal(6)._lift() + Principal ideal (3) of Integer Ring + sage: R. = QQ[] + sage: S = R.quotient(x) + sage: S.ideal(y)._lift() + Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field + """ + R = self.ring() + if hasattr(R, 'defining_ideal'): + Igens = list(R.defining_ideal().gens()) + else: + Igens = [R.modulus()] + Igens += [g.lift() for g in self.gens()] + return R.cover_ring().ideal(Igens) + def _contains_(self, other): r""" Check whether this ideal contains the given element. @@ -1534,15 +1555,19 @@ def _contains_(self, other): sage: 5-5*t in S.ideal(t^2 - 1) True """ - R = self.ring() - assert other in R - if hasattr(R, 'defining_ideal'): - Igens = list(R.defining_ideal().gens()) - else: - Igens = [R.modulus()] - Igens += [g.lift() for g in self.gens()] - J = R.cover_ring().ideal(Igens) - return other.lift() in J + assert other in self.ring() + return other.lift() in self._lift() + + def radical(self): + """ + Return the radical of this ideal. + + EXAMPLES:: + + sage: Zmod(16).ideal(4).radical() + Principal ideal (2) of Ring of integers modulo 16 + """ + return self.ring().ideal(self._lift().radical()) class QuotientRingIdeal_principal(ideal.Ideal_principal, QuotientRingIdeal_generic): From bbf6907df2595fa9646b4e0473df6b470dac8ea8 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 20 Feb 2025 18:13:12 +0700 Subject: [PATCH 440/507] Fix link in documentation --- src/sage/categories/rings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index ed606e14811..e4cf8ed94cb 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -865,7 +865,7 @@ def nilradical(self): .. SEEALSO:: - :meth:`~sage.categories.finite_dimensional_lie_algebras_with_basis.ParentMethods.nilradical` + :meth:`~sage.categories.finite_dimensional_lie_algebras_with_basis.FiniteDimensionalLieAlgebrasWithBasis.ParentMethods.nilradical` """ return self.zero_ideal().radical() @@ -1027,7 +1027,7 @@ def ideal(self, *args, **kwds): from sage.rings.ideal import Ideal_generic if not args: - gens = [self(0)] + return self.zero_ideal() else: gens = args while isinstance(gens, (list, tuple, GeneratorType)) and len(gens) == 1: @@ -1051,7 +1051,7 @@ def ideal(self, *args, **kwds): break if not gens: - gens = [self.zero()] + return self.zero_ideal() elif coerce: gens = [self(g) for g in gens] From 53fb436a37fb44dc64f157b250d13fee0ee467e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 20 Feb 2025 13:32:45 +0100 Subject: [PATCH 441/507] no meaning for gp in pari/gp --- src/doc/de/tutorial/interfaces.rst | 4 ++-- src/doc/en/tutorial/interfaces.rst | 4 ++-- src/doc/fr/tutorial/interfaces.rst | 4 ++-- src/doc/ja/tutorial/interfaces.rst | 4 ++-- src/doc/pt/tutorial/interfaces.rst | 4 ++-- src/doc/ru/tutorial/interfaces.rst | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/de/tutorial/interfaces.rst b/src/doc/de/tutorial/interfaces.rst index c1ce7d14df9..e87c1d08790 100644 --- a/src/doc/de/tutorial/interfaces.rst +++ b/src/doc/de/tutorial/interfaces.rst @@ -29,9 +29,9 @@ dessen primärer Fokus Zahlentheorie ist. Es gibt zwei sehr verschiedene Schnittstellen, die Sie in Sage nutzen können: -- ``gp`` -- Der "**G** o **P** ARI" Interpreter und +- ``gp`` -- Der **PARI** Interpreter und -- ``pari`` -- Die PARI-C-Bibliothek. +- ``pari`` -- Die **PARI** C-Bibliothek. Die folgenden Zeilen zum Beispiel sind zwei Wege, genau das gleiche zu diff --git a/src/doc/en/tutorial/interfaces.rst b/src/doc/en/tutorial/interfaces.rst index 16019427a34..d41f425218c 100644 --- a/src/doc/en/tutorial/interfaces.rst +++ b/src/doc/en/tutorial/interfaces.rst @@ -32,9 +32,9 @@ primary focus is number theory. There are two very distinct interfaces that you can use in Sage: -- ``gp`` -- the "**G** o **P** ARI" interpreter, and +- ``gp`` -- the **PARI** interpreter, and -- ``pari`` -- the PARI C library. +- ``pari`` -- the **PARI** C library. For example, the following are two ways of doing the same thing. diff --git a/src/doc/fr/tutorial/interfaces.rst b/src/doc/fr/tutorial/interfaces.rst index 9f314601bf8..3efac4f2cfe 100644 --- a/src/doc/fr/tutorial/interfaces.rst +++ b/src/doc/fr/tutorial/interfaces.rst @@ -31,9 +31,9 @@ PARI est un programme C compact, mature, fortement optimisé et spécialisé en théorie des nombres. Il possède deux interfaces très différentes utilisables depuis Sage : -- ``gp`` -- l'interpréteur "**G** o **P** ARI", et +- ``gp`` -- l'interpréteur **PARI**, et -- ``pari`` -- la bibliothèque C PARI. +- ``pari`` -- la bibliothèque C **PARI**. Ainsi, les deux commandes suivantes font le même calcul de deux façons diff --git a/src/doc/ja/tutorial/interfaces.rst b/src/doc/ja/tutorial/interfaces.rst index 4a93efa92f5..91d88319b97 100644 --- a/src/doc/ja/tutorial/interfaces.rst +++ b/src/doc/ja/tutorial/interfaces.rst @@ -27,9 +27,9 @@ GP/PARI 数論関係の演算処理を主目的とするPARIは,コンパクトで非常に練れたプログラムで,高度に最適化されている. SageからPARIを使うには,大きく異なる2種類のインターフェイスを選ぶことができる: -- ``gp`` -- "**G** o **P** ARI" インタープリタ +- ``gp`` -- **PARI** インタープリタ -- ``pari`` -- PARI Cライブラリ +- ``pari`` -- **PARI** Cライブラリ 以下の例では,同一の計算を二通りのやり方で実行している. 一見同じように見えても実は出力内容は同一ではないし,画面の奥で実際に行なわれている処理過程は二つの方法で全くと言っていいほど異なっているのだ. diff --git a/src/doc/pt/tutorial/interfaces.rst b/src/doc/pt/tutorial/interfaces.rst index 79388ef9d43..b1b4494bf9d 100644 --- a/src/doc/pt/tutorial/interfaces.rst +++ b/src/doc/pt/tutorial/interfaces.rst @@ -32,9 +32,9 @@ otimizado cujo foco primário é teoria de números. Existem duas interfaces distintas que podem ser usadas no Sage: -- ``gp`` -- o "**G** do **P** ARI" interpretador, e +- ``gp`` -- o **PARI** interpretador, e -- ``pari`` -- a biblioteca C do PARI. +- ``pari`` -- a biblioteca C do **PARI**. Por exemplo, os seguintes comandos são duas formas de realizar a mesma coisa. Eles parecem idênticos, mas o resultado é na verdade diff --git a/src/doc/ru/tutorial/interfaces.rst b/src/doc/ru/tutorial/interfaces.rst index ab4850ca119..6d1ac552c8c 100644 --- a/src/doc/ru/tutorial/interfaces.rst +++ b/src/doc/ru/tutorial/interfaces.rst @@ -28,9 +28,9 @@ PARI это компактная, очень продуманная и хоро на C, сосредоточенная на теории чисел. Существует два раздельных интерфейса, которые вы можете использовать в Sage: -- ``gp`` -- gp - интерпретатор "**G** o **P** ARI" , и +- ``gp`` -- gp - интерпретатор **PARI** , и -- ``pari`` -- pari - С-библиотека PARI. +- ``pari`` -- pari - С-библиотека **PARI**. Например, следующие две строчки выполняют одну и ту же операцию. Они выглядят From b3e6c0b60c064f2945227a8bfbfd38c02c69b6c4 Mon Sep 17 00:00:00 2001 From: Andreas Enge Date: Thu, 20 Feb 2025 15:44:06 +0100 Subject: [PATCH 442/507] Correct an error message. * src/sage/sat/solvers/dimacs.py: The particle "not" makes all the difference. --- src/sage/sat/solvers/dimacs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/sat/solvers/dimacs.py b/src/sage/sat/solvers/dimacs.py index 09ec052b914..f005acfdb24 100644 --- a/src/sage/sat/solvers/dimacs.py +++ b/src/sage/sat/solvers/dimacs.py @@ -425,7 +425,7 @@ def _run(self): try: process = subprocess.Popen(args, stdout=subprocess.PIPE) except OSError: - raise OSError("Could run '%s', perhaps you need to add your SAT solver to $PATH?" % (" ".join(args))) + raise OSError("Could not run '%s', perhaps you need to add your SAT solver to $PATH?" % (" ".join(args))) try: while process.poll() is None: From 1e4e81e9a899f201c17d29c5a9a59fc705f0da19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 20 Feb 2025 19:59:00 +0100 Subject: [PATCH 443/507] fix and activate the check for pep E272 --- src/sage/combinat/designs/resolvable_bibd.py | 344 ++++++++++-------- .../arithmetic_dynamics/projective_ds.py | 4 +- src/tox.ini | 2 +- 3 files changed, 192 insertions(+), 158 deletions(-) diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index c6e0bbee33b..9ebe55cd577 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -83,21 +83,23 @@ def resolvable_balanced_incomplete_block_design(v, k, existence=False): EXAMPLES:: - sage: KTS15 = designs.resolvable_balanced_incomplete_block_design(15,3); KTS15 + sage: KTS15 = designs.resolvable_balanced_incomplete_block_design(15,3) + sage: KTS15 (15,3,1)-Balanced Incomplete Block Design sage: KTS15.is_resolvable() True TESTS:: + sage: bibd = designs.resolvable_balanced_incomplete_block_design sage: for v in range(40): ....: for k in range(v): - ....: if designs.resolvable_balanced_incomplete_block_design(v,k,existence=True) is True: - ....: _ = designs.resolvable_balanced_incomplete_block_design(v,k) + ....: if bibd(v,k,existence=True) is True: + ....: _ = bibd(v,k) """ # Trivial cases if v == 1 or k == v: - return balanced_incomplete_block_design(v,k,existence=existence) + return balanced_incomplete_block_design(v, k, existence=existence) # Non-existence of resolvable BIBD if (v < k or @@ -114,31 +116,31 @@ def resolvable_balanced_incomplete_block_design(v, k, existence=False): (v*(v-1))/(k*(k-1)) < v): if existence: return False - raise EmptySetError("There exists no ({},{},{})-RBIBD".format(v,k,1)) + raise EmptySetError("There exists no ({},{},{})-RBIBD".format(v, k, 1)) if k == 2: if existence: return True - classes = [[[(c+i) % (v-1),(c+v-i) % (v-1)] for i in range(1, v//2)] + classes = [[[(c+i) % (v-1), (c+v-i) % (v-1)] for i in range(1, v//2)] for c in range(v-1)] - for i,classs in enumerate(classes): - classs.append([v-1,i]) + for i, classs in enumerate(classes): + classs.append([v-1, i]) B = BalancedIncompleteBlockDesign(v, - sum(classes,[]), + sum(classes, []), k=k, check=True, copy=False) B._classes = classes return B elif k == 3: - return kirkman_triple_system(v,existence=existence) + return kirkman_triple_system(v, existence=existence) elif k == 4: - return v_4_1_rbibd(v,existence=existence) - else: - if existence: - return Unknown - raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,3)) + return v_4_1_rbibd(v, existence=existence) + + if existence: + return Unknown + raise NotImplementedError(f"I don't know how to build a ({v},{3},1)-RBIBD!") def kirkman_triple_system(v, existence=False): @@ -193,14 +195,15 @@ def kirkman_triple_system(v, existence=False): return False elif v == 3: - return BalancedIncompleteBlockDesign(3,[[0,1,2]],k=3,lambd=1) + return BalancedIncompleteBlockDesign(3, [[0, 1, 2]], k=3, lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] - KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl], + k=3, lambd=1, copy=False) KTS._classes = classes return KTS @@ -210,7 +213,7 @@ def kirkman_triple_system(v, existence=False): elif ((v-1)//2) % 6 == 1 and is_prime_power((v-1)//2): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//2 - K = GF(q,'x') + K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 @@ -220,31 +223,33 @@ def kirkman_triple_system(v, existence=False): assert 2*a**m == a**t+1 # First parallel class - first_class = [[(0,1),(0,2),'inf']] + first_class = [[(0, 1), (0, 2), 'inf']] b0 = K.one() b1 = a**t b2 = a**m - first_class.extend([(b0*a**i,1),(b1*a**i,1),(b2*a**i,2)] - for i in list(range(t))+list(range(2*t,3*t))+list(range(4*t,5*t))) + first_class.extend([(b0*a**i, 1), (b1*a**i, 1), (b2*a**i, 2)] + for i in list(range(t))+list(range(2*t, 3*t))+list(range(4*t, 5*t))) b0 = a**(m+t) b1 = a**(m+3*t) b2 = a**(m+5*t) - first_class.extend([[(b0*a**i,2),(b1*a**i,2),(b2*a**i,2)] + first_class.extend([[(b0*a**i, 2), (b1*a**i, 2), (b2*a**i, 2)] for i in range(t)]) # Action of K on the points - action = lambda v,x : (v+x[0],x[1]) if len(x) == 2 else x + def action(v, x): + return (v + x[0], x[1]) if len(x) == 2 else x # relabel to integer - relabel = {(p,x): i+(x-1)*q - for i,p in enumerate(K) - for x in [1,2]} + relabel = {(p, x): i+(x-1)*q + for i, p in enumerate(K) + for x in [1, 2]} relabel['inf'] = 2*q - classes = [[[relabel[action(p,x)] for x in tr] for tr in first_class] + classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class] for p in K] - KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl], + k=3, lambd=1, copy=False) KTS._classes = classes return KTS @@ -255,32 +260,34 @@ def kirkman_triple_system(v, existence=False): elif (v//3) % 6 == 1 and is_prime_power(v//3): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = v//3 - K = GF(q,'x') + K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 - A0 = [(0,0),(0,1),(0,2)] - B = [[(a**i,j),(a**(i+2*t),j),(a**(i+4*t),j)] for j in range(3) - for i in range(t)] - A = [[(a**i,0),(a**(i+2*t),1),(a**(i+4*t),2)] for i in range(6*t)] + A0 = [(0, 0), (0, 1), (0, 2)] + B = [[(a**i, j), (a**(i+2*t), j), (a**(i+4*t), j)] for j in range(3) + for i in range(t)] + A = [[(a**i, 0), (a**(i+2*t), 1), (a**(i+4*t), 2)] for i in range(6*t)] # Action of K on the points - action = lambda v,x: (v+x[0],x[1]) + def action(v, x): + return (v + x[0], x[1]) # relabel to integer - relabel = {(p,j): i+j*q - for i,p in enumerate(K) + relabel = {(p, j): i+j*q + for i, p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t] # Classes - classes = [[[relabel[action(p,x)] for x in tr] for tr in B0] + classes = [[[relabel[action(p, x)] for x in tr] for tr in B0] for p in K] - for i in list(range(t))+list(range(2*t,3*t))+list(range(4*t,5*t)): - classes.append([[relabel[action(p,x)] for x in A[i]] for p in K]) + for i in list(range(t))+list(range(2*t, 3*t))+list(range(4*t, 5*t)): + classes.append([[relabel[action(p, x)] for x in A[i]] for p in K]) - KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + KTS = BalancedIncompleteBlockDesign(v, [tr for cl in classes for tr in cl], + k=3, lambd=1, copy=False) KTS._classes = classes return KTS @@ -310,14 +317,14 @@ def kirkman_triple_system(v, existence=False): for b in X: b.remove(8) X = sum(X, []) + [8] - gdd4.relabel({v:i for i,v in enumerate(X)}) + gdd4.relabel({v: i for i, v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] - gdd7.relabel({v:i for i,v in enumerate(X)}) + gdd7.relabel({v: i for i, v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains @@ -348,13 +355,14 @@ def kirkman_triple_system(v, existence=False): # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for _ in repeat(None, (v - 1) // 2)] gdd = {4: gdd4, 7: gdd7} - for B in PBD_4_7((v-1)//2,check=False): - for i,classs in enumerate(gdd[len(B)]): - classes[B[i]].extend([[2*B[x//2]+x % 2 for x in BB] for BB in classs]) + for B in PBD_4_7((v-1)//2, check=False): + for i, classs in enumerate(gdd[len(B)]): + classes[B[i]].extend([2*B[x//2]+x % 2 for x in BB] + for BB in classs) # The {x,x',\infty} blocks - for i,classs in enumerate(classes): - classs.append([2*i,2*i+1,v-1]) + for i, classs in enumerate(classes): + classs.append([2*i, 2*i+1, v-1]) KTS = BalancedIncompleteBlockDesign(v, blocks=[tr for cl in classes for tr in cl], @@ -409,28 +417,28 @@ def v_4_1_rbibd(v, existence=False): if v % 3 != 1 or not is_prime_power((v-1)//3): if existence: return Unknown - raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,4)) + raise NotImplementedError(f"I don't know how to build a ({v},{4},1)-RBIBD!") from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//3 nn = (q-1)//4 - G = GF(q,'x') + G = GF(q, 'x') w = G.primitive_element() e = w**(nn) assert e**2 == -1 - first_class = [[(w**i,j),(-w**i,j),(e*w**i,j+1),(-e*w**i,j+1)] + first_class = [[(w**i, j), (-w**i, j), (e*w**i, j+1), (-e*w**i, j+1)] for i in range(nn) for j in range(3)] - first_class.append([(0,0),(0,1),(0,2),'inf']) + first_class.append([(0, 0), (0, 1), (0, 2), 'inf']) - label = {p:i for i,p in enumerate(G)} + label = {p: i for i, p in enumerate(G)} classes = [[[v-1 if x == 'inf' else (x[1] % 3)*q+label[x[0]+g] for x in S] for S in first_class] for g in G] BIBD = BalancedIncompleteBlockDesign(v, - blocks=sum(classes,[]), + blocks=sum(classes, []), k=4, check=True, copy=False) @@ -443,9 +451,9 @@ def PBD_4_7(v, check=True, existence=False): r""" Return a `(v,\{4,7\})`-PBD. - For all `v` such that `n\equiv 1\pmod{3}` and `n\neq 10,19, 31` there exists - a `(v,\{4,7\})`-PBD. This is proved in Proposition IX.4.5 from [BJL99]_, - which this method implements. + For all `v` such that `n\equiv 1\pmod{3}` and `n\neq 10,19, 31` there + exists a `(v,\{4,7\})`-PBD. This is proved in Proposition IX.4.5 + from [BJL99]_, which this method implements. This construction of PBD is used by the construction of Kirkman Triple Systems. @@ -465,7 +473,7 @@ def PBD_4_7(v, check=True, existence=False): ....: assert PBD_4_7(i,existence=True) is True ....: _ = PBD_4_7(i,check=True) """ - if v % 3 != 1 or v in [10,19,31]: + if v % 3 != 1 or v in [10, 19, 31]: if existence: return Unknown raise NotImplementedError @@ -481,88 +489,113 @@ def PBD_4_7(v, check=True, existence=False): # Beth/Jungnickel/Lenz: take KTS(15) and extend each of the 7 classes # with a new point. Make those new points a 7-set. KTS15 = kirkman_triple_system(15) - blocks = [S+[i+15] for i,classs in enumerate(KTS15._classes) for S in classs]+[list(range(15, 22))] + blocks = [S+[i+15] for i, classs in enumerate(KTS15._classes) for S in classs]+[list(range(15, 22))] elif v == 34: # [BJL99] (p527,vol1), but originally Brouwer - A = [(0,0),(1,1),(2,0),(4,1)] - B = [(0,0),(1,0),(4,2)] - C = [(0,0),(2,2),(5,0)] - D = [(0,0),(0,1),(0,2)] - - A = [[(x+i, y+j) for x,y in A] for i in range(9) for j in range(3)] - B = [[(x+i, y+i+j) for x,y in B]+[27+j] for i in range(9) for j in range(3)] - C = [[(x+i+j,y+2*i+j) for x,y in C]+[30+j] for i in range(9) for j in range(3)] - D = [[(x+i, y+i) for x,y in D]+[33] for i in range(9)] - - blocks = [[int(x) if not isinstance(x,tuple) else (x[1] % 3)*9+(x[0] % 9) for x in S] - for S in A+B+C+D+[list(range(27,34))]] + A = [(0, 0), (1, 1), (2, 0), (4, 1)] + B = [(0, 0), (1, 0), (4, 2)] + C = [(0, 0), (2, 2), (5, 0)] + D = [(0, 0), (0, 1), (0, 2)] + + A = [[(x+i, y+j) for x, y in A] + for i in range(9) for j in range(3)] + B = [[(x+i, y+i+j) for x, y in B]+[27+j] + for i in range(9) for j in range(3)] + C = [[(x+i+j, y+2*i+j) for x, y in C]+[30+j] + for i in range(9) for j in range(3)] + D = [[(x+i, y+i) for x, y in D]+[33] + for i in range(9)] + + blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*9+(x[0] % 9) + for x in S] + for S in A+B+C+D+[list(range(27, 34))]] elif v == 46: # [BJL99] (p527,vol1), but originally Brouwer - A = [(1,0),(3,0),(9,0),(0,1)] - B = [(2,0),(6,0),(5,0),(0,1)] - C = [(0,0),(1,1),(4,2)] - D = [(0,0),(2,1),(7,2)] - E = [(0,0),(0,1),(0,2)] - - A = [[(x+i, y+j) for x,y in A] for i in range(13) for j in range(3)] - B = [[(x+i, y+j) for x,y in B] for i in range(13) for j in range(3)] - C = [[(x+i, y+j) for x,y in C]+[39+j] for i in range(13) for j in range(3)] - D = [[(x+i, y+j) for x,y in D]+[42+j] for i in range(13) for j in range(3)] - E = [[(x+i, y+i) for x,y in E]+[45] for i in range(13)] - - blocks = [[int(x) if not isinstance(x,tuple) else (x[1] % 3)*13+(x[0] % 13) for x in S] + A = [(1, 0), (3, 0), (9, 0), (0, 1)] + B = [(2, 0), (6, 0), (5, 0), (0, 1)] + C = [(0, 0), (1, 1), (4, 2)] + D = [(0, 0), (2, 1), (7, 2)] + E = [(0, 0), (0, 1), (0, 2)] + + A = [[(x+i, y+j) for x, y in A] + for i in range(13) for j in range(3)] + B = [[(x+i, y+j) for x, y in B] + for i in range(13) for j in range(3)] + C = [[(x+i, y+j) for x, y in C]+[39+j] + for i in range(13) for j in range(3)] + D = [[(x+i, y+j) for x, y in D]+[42+j] + for i in range(13) for j in range(3)] + E = [[(x+i, y+i) for x, y in E]+[45] + for i in range(13)] + + blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*13+(x[0] % 13) + for x in S] for S in A+B+C+D+E+[list(range(39, 46))]] elif v == 58: # [BJL99] (p527,vol1), but originally Brouwer - A = [(0,0),(1,0),(4,0),( 5,1)] - B = [(0,0),(2,0),(8,0),(11,1)] - C = [(0,0),(5,0),(2,1),(12,1)] - D = [(0,0),(8,1),(7,2)] - E = [(0,0),(6,1),(4,2)] - F = [(0,0),(0,1),(0,2)] - - A = [[(x+i, y+j) for x,y in A] for i in range(17) for j in range(3)] - B = [[(x+i, y+j) for x,y in B] for i in range(17) for j in range(3)] - C = [[(x+i, y+j) for x,y in C] for i in range(17) for j in range(3)] - D = [[(x+i, y+j) for x,y in D]+[51+j] for i in range(17) for j in range(3)] - E = [[(x+i, y+j) for x,y in E]+[54+j] for i in range(17) for j in range(3)] - F = [[(x+i, y+i) for x,y in F]+[57] for i in range(17)] - - blocks = [[int(x) if not isinstance(x,tuple) else (x[1] % 3)*17+(x[0] % 17) for x in S] - for S in A+B+C+D+E+F+[list(range(51,58))]] + A = [(0, 0), (1, 0), (4, 0), (5, 1)] + B = [(0, 0), (2, 0), (8, 0), (11, 1)] + C = [(0, 0), (5, 0), (2, 1), (12, 1)] + D = [(0, 0), (8, 1), (7, 2)] + E = [(0, 0), (6, 1), (4, 2)] + F = [(0, 0), (0, 1), (0, 2)] + + A = [[(x+i, y+j) for x, y in A] + for i in range(17) for j in range(3)] + B = [[(x+i, y+j) for x, y in B] + for i in range(17) for j in range(3)] + C = [[(x+i, y+j) for x, y in C] + for i in range(17) for j in range(3)] + D = [[(x+i, y+j) for x, y in D]+[51+j] + for i in range(17) for j in range(3)] + E = [[(x+i, y+j) for x, y in E]+[54+j] + for i in range(17) for j in range(3)] + F = [[(x+i, y+i) for x, y in F]+[57] + for i in range(17)] + + blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*17+(x[0] % 17) + for x in S] + for S in A+B+C+D+E+F+[list(range(51, 58))]] elif v == 70: # [BJL99] (p527,vol1), but originally Brouwer - A = [(0,0),(1,0),(5,1),(13,1)] - B = [(0,0),(4,0),(20,1),(10,1)] - C = [(0,0),(16,0),(17,1),(19,1)] - D = [(0,0),(2,1),(8,1),(11,1)] - E = [(0,0),(3,2),(9,1)] - F = [(0,0),(7,0),(14,1)] - H = [(0,0),(0,1),(0,2)] - - A = [[(x+i, y+j) for x,y in A] for i in range(21) for j in range(3)] - B = [[(x+i, y+j) for x,y in B] for i in range(21) for j in range(3)] - C = [[(x+i, y+j) for x,y in C] for i in range(21) for j in range(3)] - D = [[(x+i, y+j) for x,y in D] for i in range(21) for j in range(3)] - E = [[(x+i, y+j) for x,y in E]+[63+j] for i in range(21) for j in range(3)] - F = [[(x+3*i+j, y+ii+j) for x,y in F]+[66+j] for i in range( 7) for j in range(3) for ii in range(3)] - H = [[(x+i, y+i) for x,y in H]+[69] for i in range(21)] - - blocks = [[int(x) if not isinstance(x,tuple) else (x[1] % 3)*21+(x[0] % 21) + A = [(0, 0), (1, 0), (5, 1), (13, 1)] + B = [(0, 0), (4, 0), (20, 1), (10, 1)] + C = [(0, 0), (16, 0), (17, 1), (19, 1)] + D = [(0, 0), (2, 1), (8, 1), (11, 1)] + E = [(0, 0), (3, 2), (9, 1)] + F = [(0, 0), (7, 0), (14, 1)] + H = [(0, 0), (0, 1), (0, 2)] + + A = [[(x+i, y+j) for x, y in A] + for i in range(21) for j in range(3)] + B = [[(x+i, y+j) for x, y in B] + for i in range(21) for j in range(3)] + C = [[(x+i, y+j) for x, y in C] + for i in range(21) for j in range(3)] + D = [[(x+i, y+j) for x, y in D] + for i in range(21) for j in range(3)] + E = [[(x+i, y+j) for x, y in E]+[63+j] + for i in range(21) for j in range(3)] + F = [[(x+3*i+j, y+ii+j) for x, y in F]+[66+j] + for i in range(7) for j in range(3) for ii in range(3)] + H = [[(x+i, y+i) for x, y in H]+[69] + for i in range(21)] + + blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*21+(x[0] % 21) for x in S] - for S in A+B+C+D+E+F+H+[list(range(63,70))]] + for S in A+B+C+D+E+F+H+[list(range(63, 70))]] elif v == 82: # This construction is Theorem IX.3.16 from [BJL99] (p.627). # # A (15,{4},{3})-GDD from a (16,4)-BIBD from .group_divisible_designs import group_divisible_design - from .orthogonal_arrays import transversal_design - GDD = group_divisible_design(3*5,K=[4],G=[3],check=False) - TD = transversal_design(5,5) + from .orthogonal_arrays import transversal_design + GDD = group_divisible_design(3*5, K=[4], G=[3], check=False) + TD = transversal_design(5, 5) # A (75,{4},{15})-GDD GDD2 = [[3*B[x//3]+x % 3 for x in BB] for B in TD for BB in GDD] @@ -574,7 +607,8 @@ def PBD_4_7(v, check=True, existence=False): # new points are a set of the final PBD PBD22 = PBD_4_7(15+7) S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7 - PBD22.relabel({v:i for i,v in enumerate([i for i in range(15+7) if i not in S] + S)}) + PBD22.relabel({v: i for i, v in enumerate([i for i in range(15+7) + if i not in S] + S)}) for B in PBD22: if B == S: @@ -582,7 +616,7 @@ def PBD_4_7(v, check=True, existence=False): for i in range(5): GDD2.append([x+i*15 if x < 15 else x+60 for x in B]) - GDD2.append(list(range(75,82))) + GDD2.append(list(range(75, 82))) blocks = GDD2 elif v == 94: @@ -591,7 +625,7 @@ def PBD_4_7(v, check=True, existence=False): # take 4 parallel lines from an affine plane of order 7, and a 5th # one. This is a (31,{4,5,7})-BIBD. And 94=3*31+1. from sage.combinat.designs.block_design import AffineGeometryDesign - AF = AffineGeometryDesign(2,1,7) + AF = AffineGeometryDesign(2, 1, 7) parall = [] plus_one = None for S in AF: @@ -607,10 +641,10 @@ def PBD_4_7(v, check=True, existence=False): S_4_5_7 = [S for S in S_4_5_7 if len(S) > 1] S_4_5_7 = PairwiseBalancedDesign(X, blocks=S_4_5_7, - K=[4,5,7], + K=[4, 5, 7], check=False) S_4_5_7.relabel() - return PBD_4_7_from_Y(S_4_5_7,check=check) + return PBD_4_7_from_Y(S_4_5_7, check=check) elif v == 127 or v == 142: # IX.4.5.o from [BJL99]. @@ -620,22 +654,22 @@ def PBD_4_7(v, check=True, existence=False): points_to_add = 2 if v == 127 else 7 rBIBD4 = v_4_1_rbibd(40) GDD = [S+[40+i] if i < points_to_add else S - for i,classs in enumerate(rBIBD4._classes) + for i, classs in enumerate(rBIBD4._classes) for S in classs] if points_to_add == 7: GDD.append(list(range(40, 40 + points_to_add))) groups = [[x] for x in range(40+points_to_add)] else: groups = [[x] for x in range(40)] - groups.append(list(range(40,40+points_to_add))) + groups.append(list(range(40, 40+points_to_add))) GDD = GroupDivisibleDesign(40+points_to_add, groups=groups, blocks=GDD, - K=[2,4,5,7], + K=[2, 4, 5, 7], check=False, copy=False) - return PBD_4_7_from_Y(GDD,check=check) + return PBD_4_7_from_Y(GDD, check=check) elif v % 6 == 1 and GDD_4_2((v - 1) // 6, existence=True) is True: # VII.5.17 from [BJL99] @@ -644,14 +678,14 @@ def PBD_4_7(v, check=True, existence=False): elif v == 202: # IV.4.5.p from [BJL99] - PBD = PBD_4_7(22,check=False) - PBD = PBD_4_7_from_Y(PBD,check=False) - return PBD_4_7_from_Y(PBD,check=check) - - elif balanced_incomplete_block_design(v,4,existence=True) is True: - return balanced_incomplete_block_design(v,4) - elif balanced_incomplete_block_design(v,7,existence=True) is True: - return balanced_incomplete_block_design(v,7) + PBD = PBD_4_7(22, check=False) + PBD = PBD_4_7_from_Y(PBD, check=False) + return PBD_4_7_from_Y(PBD, check=check) + + elif balanced_incomplete_block_design(v, 4, existence=True) is True: + return balanced_incomplete_block_design(v, 4) + elif balanced_incomplete_block_design(v, 7, existence=True) is True: + return balanced_incomplete_block_design(v, 7) else: from sage.combinat.designs.orthogonal_arrays import orthogonal_array # IX.4.5.m from [BJL99]. @@ -665,23 +699,23 @@ def PBD_4_7(v, check=True, existence=False): vv = (v - 1) // 3 for g in range((vv + 5 - 1) // 5, vv // 4 + 1): u = vv-4*g - if (orthogonal_array(5,g,existence=True) is True and - PBD_4_7(3*g+1,existence=True) is True and - PBD_4_7(3*u+1,existence=True) is True): + if (orthogonal_array(5, g, existence=True) is True and + PBD_4_7(3*g+1, existence=True) is True and + PBD_4_7(3*u+1, existence=True) is True): from .orthogonal_arrays import transversal_design domain = set(range(vv)) - GDD = transversal_design(5,g) + GDD = transversal_design(5, g) GDD = GroupDivisibleDesign(vv, groups=[[x for x in gr if x in domain] for gr in GDD.groups()], blocks=[[x for x in B if x in domain] for B in GDD], - G=set([g,u]), - K=[4,5], + G=set([g, u]), + K=[4, 5], check=False) - return PBD_4_7_from_Y(GDD,check=check) + return PBD_4_7_from_Y(GDD, check=check) return PairwiseBalancedDesign(v, blocks=blocks, - K=[4,7], + K=[4, 7], check=check, copy=False) @@ -729,33 +763,33 @@ def PBD_4_7_from_Y(gdd, check=True): """ from .group_divisible_designs import group_divisible_design from .bibd import PairwiseBalancedDesign - block_sizes = set(map(len,gdd._blocks)) - group_sizes = set(map(len,gdd._groups)) + block_sizes = set(map(len, gdd._blocks)) + group_sizes = set(map(len, gdd._groups)) if not block_sizes.issubset([4, 5, 7]): txt = list(block_sizes.difference([4, 5, 7])) raise ValueError("The GDD should only contain blocks of size {{4,5,7}} " "but there are other: {}".format(txt)) for gs in group_sizes: - if PBD_4_7(3*gs+1,existence=True) is not True: + if PBD_4_7(3*gs+1, existence=True) is not True: raise RuntimeError("A group has size {} but I do not know how to " - "build a ({},[4,7])-PBD".format(gs,3*gs+1)) + "build a ({},[4,7])-PBD".format(gs, 3*gs+1)) GDD = {} # the GDD we will need if 4 in block_sizes: # GDD[4] = GDD_from_BIBD(3*4,4) - GDD[4] = group_divisible_design(3*4,K=[4],G=[3]) + GDD[4] = group_divisible_design(3*4, K=[4], G=[3]) if 5 in block_sizes: # GDD[5] = GDD_from_BIBD(3*5,4) - GDD[5] = group_divisible_design(3*5,K=[4],G=[3]) + GDD[5] = group_divisible_design(3*5, K=[4], G=[3]) if 7 in block_sizes: # It is obtained from a PBD_4_7(22) by removing a point only contained # in sets of size 4 GDD[7] = PBD_4_7(22) x = set(range(22)).difference(*[S for S in GDD[7] if len(S) != 4]).pop() - relabel = sum((S for S in GDD[7] if x in S),[]) # the groups must be 012,345,... + relabel = sum((S for S in GDD[7] if x in S), []) # the groups must be 012,345,... relabel = [xx for xx in relabel if xx != x]+[x] - GDD[7].relabel({v:i for i,v in enumerate(relabel)}) + GDD[7].relabel({v: i for i, v in enumerate(relabel)}) GDD[7] = [S for S in GDD[7] if 21 not in S] PBD = [] @@ -766,7 +800,7 @@ def PBD_4_7_from_Y(gdd, check=True): PBD.append([3*B[x//3]+(x % 3) for x in B_GDD]) # The groups - group_PBD = {gs:PBD_4_7(3*gs+1) for gs in group_sizes} + group_PBD = {gs: PBD_4_7(3*gs+1) for gs in group_sizes} for G in gdd.groups(): gs = len(G) for B in group_PBD[gs]: @@ -775,6 +809,6 @@ def PBD_4_7_from_Y(gdd, check=True): return PairwiseBalancedDesign(3*gdd.num_points()+1, blocks=PBD, - K=[4,7], + K=[4, 7], check=check, copy=False) diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 8cc1d8358bc..46216ea4e12 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -2117,9 +2117,9 @@ def green_function(self, P, v, **kwds): h = max([R(emb(c).abs()) for c in poly.coefficients()]) else: #non-archimedean if BR == QQ: - h = max([R(v)**(-R(c.valuation(v))) for c in poly.coefficients()]) + h = max(R(v)**(-R(c.valuation(v))) for c in poly.coefficients()) else: - h = max([R(c.abs_non_arch(v, prec=prec)) for c in poly.coefficients()]) + h = max(R(c.abs_non_arch(v, prec=prec)) for c in poly.coefficients()) maxh = max(h, maxh) if maxh == 0: maxh = 1 #avoid division by 0 diff --git a/src/tox.ini b/src/tox.ini index 4ca4f5bd283..cf7ece8a0d7 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -180,7 +180,7 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E115,E21,E221,E222,E225,E227,E228,E25,E271,E275,E302,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E115,E21,E221,E222,E225,E227,E228,E25,E271,E272,E275,E302,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] From 5eef4ec4664342cab4f250eac977a2b70bec9035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 20 Feb 2025 21:10:43 +0100 Subject: [PATCH 444/507] make algdep always an alias for algebraic_dependency --- src/sage/arith/all.py | 3 +- src/sage/arith/misc.py | 18 +++--- src/sage/calculus/calculus.py | 4 +- src/sage/rings/complex_double.pyx | 26 ++++---- src/sage/rings/complex_mpc.pyx | 12 ++-- src/sage/rings/complex_mpfr.pyx | 12 ++-- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 2 +- .../rings/padics/padic_generic_element.pyx | 63 ++++--------------- src/sage/rings/real_double.pyx | 2 +- src/sage/rings/real_mpfi.pyx | 17 ++--- src/sage/rings/real_mpfr.pyx | 2 +- src/sage/schemes/elliptic_curves/heegner.py | 13 ++-- .../riemann_surfaces/riemann_surface.py | 10 +-- 13 files changed, 81 insertions(+), 103 deletions(-) diff --git a/src/sage/arith/all.py b/src/sage/arith/all.py index f1fc716071c..517d77f5791 100644 --- a/src/sage/arith/all.py +++ b/src/sage/arith/all.py @@ -1,6 +1,7 @@ from sage.misc.lazy_import import lazy_import -from sage.arith.misc import (algdep, bernoulli, is_prime, is_prime_power, +from sage.arith.misc import (algdep, algebraic_dependency, + bernoulli, is_prime, is_prime_power, is_pseudoprime, is_pseudoprime_power, prime_powers, primes_first_n, eratosthenes, primes, next_prime_power, next_probable_prime, next_prime, diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index f4d8f87b78b..c1a6d0ff7c9 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -40,8 +40,9 @@ ################################################################## -def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, - use_digits=None, height_bound=None, proof=False): +def algebraic_dependency(z, degree, known_bits=None, + use_bits=None, known_digits=None, + use_digits=None, height_bound=None, proof=False): """ Return an irreducible polynomial of degree at most `degree` which is approximately satisfied by the number `z`. @@ -62,9 +63,9 @@ def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, indicating that higher precision is required. ALGORITHM: Uses LLL for real/complex inputs, PARI C-library - ``algdep`` command otherwise. + :pari:`algdep` command otherwise. - Note that ``algebraic_dependency`` is a synonym for ``algdep``. + Note that ``algdep`` is a synonym for ``algebraic_dependency``. INPUT: @@ -79,7 +80,7 @@ def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, EXAMPLES:: - sage: algdep(1.888888888888888, 1) # needs sage.libs.pari + sage: algebraic_dependency(1.888888888888888, 1) # needs sage.libs.pari 9*x - 17 sage: algdep(0.12121212121212, 1) # needs sage.libs.pari 33*x - 4 @@ -266,15 +267,14 @@ def norm(v): else: from sage.libs.pari import pari - y = pari(z) - f = y.algdep(degree) + f = pari(z).algdep(degree) # f might be reducible. Find the best fitting irreducible factor - factors = [p for p, e in R(f).factor()] + factors = (p for p, _ in R(f).factor()) return min(factors, key=lambda f: abs(f(z))) -algebraic_dependency = algdep +alg_dep = algebraic_dependency def bernoulli(n, algorithm='default', num_threads=1): diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 64881aba812..9319166c112 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -419,7 +419,7 @@ """ import re -from sage.arith.misc import algdep +from sage.arith.misc import algebraic_dependency from sage.rings.integer import Integer from sage.rings.rational_field import QQ from sage.rings.real_double import RealDoubleElement @@ -1117,7 +1117,7 @@ def minpoly(ex, var='x', algorithm=None, bits=None, degree=None, epsilon=0): for degree in degree_list: - f = QQ[var](algdep(a, degree)) # TODO: use the known_bits parameter? + f = QQ[var](algebraic_dependency(a, degree)) # TODO: use the known_bits parameter? # If indeed we have found a minimal polynomial, # it should be accurate to a much higher precision. error = abs(f(aa)) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 98477e0dd72..8c28a0728bf 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -2439,34 +2439,38 @@ cdef class ComplexDoubleElement(FieldElement): from sage.libs.pari.convert_sage_complex_double import complex_double_element_zeta return complex_double_element_zeta(self) - def algdep(self, long n): + def algebraic_dependency(self, long n): """ Return a polynomial of degree at most `n` which is - approximately satisfied by this complex number. Note that the - returned polynomial need not be irreducible, and indeed usually - won't be if `z` is a good approximation to an algebraic - number of degree less than `n`. + approximately satisfied by this complex number. - ALGORITHM: Uses the PARI C-library algdep command. + Note that the returned polynomial need not be irreducible, and + indeed usually will not be if `z` is a good approximation to an + algebraic number of degree less than `n`. + + ALGORITHM: Uses the PARI C-library :pari:`algdep` command. EXAMPLES:: sage: z = (1/2)*(1 + RDF(sqrt(3)) * CDF.0); z # abs tol 1e-16 # needs sage.symbolic 0.5 + 0.8660254037844387*I - sage: p = z.algdep(5); p # needs sage.libs.pari sage.symbolic + sage: p = z.algebraic_dependency(5); p # needs sage.libs.pari sage.symbolic x^2 - x + 1 sage: abs(z^2 - z + 1) < 1e-14 # needs sage.symbolic True :: - sage: CDF(0,2).algdep(10) # needs sage.libs.pari + sage: CDF(0,2).algebraic_dependency(10) # needs sage.libs.pari x^2 + 4 - sage: CDF(1,5).algdep(2) # needs sage.libs.pari + sage: CDF(1,5).algebraic_dependency(2) # needs sage.libs.pari x^2 - 2*x + 26 """ - from sage.arith.misc import algdep - return algdep(self, n) + from sage.arith.misc import algebraic_dependency + return algebraic_dependency(self, n) + + algdep = algebraic_dependency + cdef class FloatToCDF(Morphism): """ diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 64295a773ad..94bdb2a5023 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -1365,8 +1365,10 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): ALGORITHM: Uses the PARI C-library :pari:`algdep` command. - INPUT: Type ``algdep?`` at the top level prompt. All additional - parameters are passed onto the top-level algdep command. + INPUT: Type ``algebraic_dependency?`` at the top level prompt. + + All additional parameters are passed onto the top-level + ``algebraic_dependency`` command. EXAMPLES:: @@ -1379,8 +1381,10 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: p(z) 1.11022302462516e-16 """ - from sage.arith.misc import algdep - return algdep(self, n, **kwds) + from sage.arith.misc import algebraic_dependency + return algebraic_dependency(self, n, **kwds) + + algdep = algebraic_dependency ################################ # Basic Arithmetic diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 720ec37b969..bb7f09e897b 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -3257,21 +3257,23 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): ALGORITHM: Uses the PARI C-library :pari:`algdep` command. - INPUT: Type ``algdep?`` at the top level prompt. All additional - parameters are passed onto the top-level :func:`algdep` command. + INPUT: Type ``algebraic_dependency?`` at the top level prompt. + + All additional parameters are passed onto the top-level + :func:`algebraic_dependency` command. EXAMPLES:: sage: C = ComplexField() sage: z = (1/2)*(1 + sqrt(3.0) *C.0); z 0.500000000000000 + 0.866025403784439*I - sage: p = z.algdep(5); p + sage: p = z.algebraic_dependency(5); p x^2 - x + 1 sage: p(z) 1.11022302462516e-16 """ - from sage.arith.misc import algdep - return algdep(self, n, **kwds) + from sage.arith.misc import algebraic_dependency + return algebraic_dependency(self, n, **kwds) # Alias algdep = algebraic_dependency diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index 9e907b25899..81f0996eeae 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -513,7 +513,7 @@ cdef class pAdicZZpXElement(pAdicExtElement): Return a rational approximation of ``self``. This does not try to optimize which rational is picked: see - ``algdep`` for another option. + ``algebraic_dependency`` for another option. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 42cb62c3d39..7d1433ad0b3 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -1117,57 +1117,13 @@ cdef class pAdicGenericElement(LocalGenericElement): extdeg = parent.absolute_degree() // (base.absolute_degree() * polydeg) return -extdeg * poly[polydeg-1] - def algdep(self, n): - """ - Return a polynomial of degree at most `n` which is approximately - satisfied by this number. Note that the returned polynomial need not be - irreducible, and indeed usually won't be if this number is a good - approximation to an algebraic number of degree less than `n`. - - ALGORITHM: Uses the PARI C-library :pari:`algdep` command. - - INPUT: - - - ``self`` -- a `p`-adic element - - ``n`` -- integer - - OUTPUT: polynomial; degree `n` polynomial approximately satisfied by ``self`` - - EXAMPLES:: - - sage: K = Qp(3,20,'capped-rel','series'); R = Zp(3,20,'capped-rel','series') - sage: a = K(7/19); a - 1 + 2*3 + 3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^8 + 2*3^9 + 3^11 + 3^12 - + 2*3^15 + 2*3^16 + 3^17 + 2*3^19 + O(3^20) - sage: a.algdep(1) - 19*x - 7 - sage: K2 = Qp(7,20,'capped-rel') - sage: b = K2.zeta(); b.algdep(2) - x^2 - x + 1 - sage: K2 = Qp(11,20,'capped-rel') - sage: b = K2.zeta(); b.algdep(4) - x^4 - x^3 + x^2 - x + 1 - sage: a = R(7/19); a - 1 + 2*3 + 3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^8 + 2*3^9 + 3^11 + 3^12 - + 2*3^15 + 2*3^16 + 3^17 + 2*3^19 + O(3^20) - sage: a.algdep(1) - 19*x - 7 - sage: R2 = Zp(7,20,'capped-rel') - sage: b = R2.zeta(); b.algdep(2) - x^2 - x + 1 - sage: R2 = Zp(11,20,'capped-rel') - sage: b = R2.zeta(); b.algdep(4) - x^4 - x^3 + x^2 - x + 1 - """ - # TODO: figure out if this works for extension rings. If not, move this to padic_base_generic_element. - from sage.arith.misc import algdep - return algdep(self, n) - def algebraic_dependency(self, n): """ Return a polynomial of degree at most `n` which is approximately - satisfied by this number. Note that the returned polynomial need not - be irreducible, and indeed usually won't be if this number is a good + satisfied by this number. + + Note that the returned polynomial need not be irreducible, and + indeed usually will not be if this number is a good approximation to an algebraic number of degree less than `n`. ALGORITHM: Uses the PARI C-library :pari:`algdep` command. @@ -1177,7 +1133,9 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``self`` -- a `p`-adic element - ``n`` -- integer - OUTPUT: polynomial; degree `n` polynomial approximately satisfied by ``self`` + OUTPUT: + + polynomial; degree `n` polynomial approximately satisfied by ``self`` EXAMPLES:: @@ -1205,7 +1163,12 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: b = R2.zeta(); b.algebraic_dependency(4) x^4 - x^3 + x^2 - x + 1 """ - return self.algdep(n) + # TODO: figure out if this works for extension rings. + # If not, move this to padic_base_generic_element. + from sage.arith.misc import algebraic_dependency + return algebraic_dependency(self, n) + + algdep = algebraic_dependency #def exp_artin_hasse(self): # """ diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 5efa0c87b65..cac66b4ac75 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -1955,7 +1955,7 @@ cdef class RealDoubleElement(FieldElement): sage: r.algebraic_dependency(5) # needs sage.libs.pari x^2 - 2 """ - return sage.arith.misc.algdep(self, n) + return sage.arith.misc.algebraic_dependency(self, n) algdep = algebraic_dependency diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index bbe72c26ba2..55421090926 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -4996,7 +4996,7 @@ cdef class RealIntervalFieldElement(RingElement): """ return (~self).arctanh() - def algdep(self, n): + def algebraic_dependency(self, n): r""" Return a polynomial of degree at most `n` which is approximately satisfied by ``self``. @@ -5012,13 +5012,13 @@ cdef class RealIntervalFieldElement(RingElement): ALGORITHM: - Uses the PARI C-library ``algdep`` command. + This uses the PARI C-library :pari:`algdep` command. EXAMPLES:: sage: r = sqrt(RIF(2)); r 1.414213562373095? - sage: r.algdep(5) + sage: r.algebraic_dependency(5) x^2 - 2 If we compute a wrong, but precise, interval, we get a wrong @@ -5026,7 +5026,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: r = sqrt(RealIntervalField(200)(2)) + (1/2)^40; r 1.414213562374004543503461652447613117632171875376948073176680? - sage: r.algdep(5) + sage: r.algebraic_dependency(5) 7266488*x^5 + 22441629*x^4 - 90470501*x^3 + 23297703*x^2 + 45778664*x + 13681026 But if we compute an interval that includes the number we mean, @@ -5034,13 +5034,13 @@ cdef class RealIntervalFieldElement(RingElement): interval is very imprecise:: sage: r = r.union(sqrt(2.0)) - sage: r.algdep(5) + sage: r.algebraic_dependency(5) x^2 - 2 Even on this extremely imprecise interval we get an answer which is technically correct:: - sage: RIF(-1, 1).algdep(5) + sage: RIF(-1, 1).algebraic_dependency(5) x """ # If 0 is in the interval, then we have no known bits! But @@ -5053,7 +5053,10 @@ cdef class RealIntervalFieldElement(RingElement): known_bits = -self.relative_diameter().log2() - return sage.arith.misc.algdep(self.center(), n, known_bits=known_bits) + return sage.arith.misc.algebraic_dependency(self.center(), + n, known_bits=known_bits) + + algdep = algebraic_dependency def factorial(self): """ diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 31d0bb96aed..4db177734e1 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -5397,7 +5397,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: r.algebraic_dependency(5) x^2 - 2 """ - return sage.arith.misc.algdep(self, n) + return sage.arith.misc.algebraic_dependency(self, n) algdep = algebraic_dependency diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 52820d8166b..c7aa3b8ff22 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -3355,7 +3355,7 @@ def numerical_approx(self, prec=53, algorithm=None): the discriminant below is strong confirmation -- but not proof -- that this polynomial is correct:: - sage: f = P.numerical_approx(70)[0].algdep(6); f + sage: f = P.numerical_approx(70)[0].algebraic_dependency(6); f 1225*x^6 + 1750*x^5 - 21675*x^4 - 380*x^3 + 110180*x^2 - 129720*x + 48771 sage: f.discriminant().factor() 2^6 * 3^2 * 5^11 * 7^4 * 13^2 * 19^6 * 199^2 * 719^2 * 26161^2 @@ -3460,7 +3460,7 @@ def x_poly_exact(self, prec=53, algorithm='lll'): if algorithm == 'lll': P = self.numerical_approx(prec) - f = P[0].algdep(n) + f = P[0].algebraic_dependency(n) if f.is_irreducible() and self._check_poly_discriminant(f): return f.monic() else: @@ -3543,7 +3543,7 @@ def point_exact(self, prec=53, algorithm='lll', var='a', optimize=False): sage: z = P.point_exact(200, optimize=True) sage: z[1].charpoly() x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/1838265625 - sage: f = P.numerical_approx(500)[1].algdep(12); f / f.leading_coefficient() + sage: f = P.numerical_approx(500)[1].algebraic_dependency(12); f / f.leading_coefficient() x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/1838265625 sage: E = EllipticCurve('5077a') @@ -4223,13 +4223,14 @@ def point_exact(self, prec=53): if E.root_number() == -1: return self._recognize_point_over_QQ(P, 2*self.index()) else: - # root number +1. We use algdep to recognize the x + # root number +1. We use algebraic_dependency + # to recognize the x # coordinate, stick it in the appropriate quadratic # field, then make sure that we got the right # embedding, and if not fix things so we do. x = P[0] C = x.parent() - f = x.algdep(2) + f = x.algebraic_dependency(2) K = self.quadratic_field() roots = [r[0] for r in f.roots(K)] if not roots: @@ -6414,7 +6415,7 @@ def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True): Working out the details manually:: sage: P = E.heegner_point(-47).numerical_approx(prec=200) - sage: f = algdep(P[0], 5); f + sage: f = algebraic_dependency(P[0], 5); f x^5 - x^4 + x^3 + x^2 - 2*x + 1 sage: f.discriminant().factor() 47^2 diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index ff14a7a7b10..c6ff22c693c 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -114,7 +114,7 @@ from scipy.spatial import Voronoi from sage.arith.functions import lcm -from sage.arith.misc import GCD, algdep +from sage.arith.misc import GCD, algebraic_dependency from sage.ext.fast_callable import fast_callable from sage.graphs.graph import Graph from sage.groups.matrix_gps.finitely_generated import MatrixGroup @@ -617,7 +617,7 @@ class RiemannSurface: sage: f = Y^2+X*Y+phi*Y-(X^3-X^2-2*phi*X+phi) sage: S = RiemannSurface(f,prec=prec, differentials=[1]) sage: tau = S.riemann_matrix()[0, 0] - sage: tau.algdep(6).degree() == 2 + sage: tau.algebraic_dependency(6).degree() == 2 True """ @@ -2292,7 +2292,7 @@ def matrix_of_integral_values(self, differentials, integration_method='heuristic sage: m = S.matrix_of_integral_values(B) sage: parent(m) Full MatrixSpace of 1 by 2 dense matrices over Complex Field with 53 bits of precision - sage: (m[0,0]/m[0,1]).algdep(3).degree() # curve is CM, so the period is quadratic + sage: (m[0,0]/m[0,1]).algebraic_dependency(3).degree() # curve is CM, so the period is quadratic 2 .. NOTE:: @@ -2412,7 +2412,7 @@ def riemann_matrix(self): sage: x = polygen(QQ) sage: K. = NumberField(x^2 - x + 2) - sage: all(len(m.algdep(6).roots(K)) > 0 for m in M.list()) + sage: all(len(m.algebraic_dependency(6).roots(K)) > 0 for m in M.list()) True """ PeriodMatrix = self.period_matrix() @@ -2689,7 +2689,7 @@ def polynomialize_element(alpha): d = 1 while True: d += 1 - dep = algdep(alpha, d, height_bound=10**d) + dep = algebraic_dependency(alpha, d, height_bound=10**d) if dep and dep(alpha) < epscomp: return dep From 70126c2a323a31bbc4b7041458ce0905892314d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 20 Feb 2025 21:15:52 +0100 Subject: [PATCH 445/507] fix error --- src/sage/arith/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index c1a6d0ff7c9..4b71395a30e 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -274,7 +274,7 @@ def norm(v): return min(factors, key=lambda f: abs(f(z))) -alg_dep = algebraic_dependency +algdep = algebraic_dependency def bernoulli(n, algorithm='default', num_threads=1): From 0507796f594c5a48a41032e78edf3b800e3fa1c0 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Thu, 20 Feb 2025 15:21:19 -0700 Subject: [PATCH 446/507] Implemented 'certificate' as a keyword-only argument --- .../multi_polynomial_libsingular.pyx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index ddbd4bdbca2..da8c72183c2 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -6073,7 +6073,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): else: return self * self.denominator() - def in_subalgebra(self, J, algorithm='algebra_containment'): + def in_subalgebra(self, J, algorithm='algebra_containment', *, certificate=False): """ Return whether this polynomial is contained in the subalgebra generated by ``J`` @@ -6104,6 +6104,14 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): and only if the remainder involves only the new variables `z_i`. + - ``certificate`` -- An expression that evaluates to True or False. If + it evaluates to False (the default) then return either True or False. + If it evaluates to True and 'algorithm' is set to 'groebner', return + the polynomial generated by the algorithm if such a polynomial exists, + otherwise return None. If a string is provided which evaluates to + True then this string will be used as the name of the variables in the + returned polynomial, otherwise the name will be 'newgens' + EXAMPLES:: sage: P. = QQ[] @@ -6135,7 +6143,10 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): elif algorithm == "insubring": return singular_function('inSubring')(self, R.ideal(J))[0] == 1 elif algorithm == "groebner": - new_gens = [f"newgens{i}" for i in range(len(J))] + var_name = "newgens" + if (certificate and isinstance(certificate, str)): + var_name = certificate + new_gens = [f"{var_name}{i}" for i in range(len(J))] ngens = len(new_gens) from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing S = PolynomialRing(R.base_ring(), R.gens() + tuple(new_gens), @@ -6143,6 +6154,12 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): new_gens = S.gens()[-ngens:] I = S.ideal([g - S(j) for (g,j) in zip(new_gens, J)]) z = S(self).reduce(I) + if (certificate): + if (set(z.variables()).issubset(new_gens)): + T = PolynomialRing(R.base_ring(), tuple(new_gens)) + return T(z) + else: + return None return set(z.variables()).issubset(new_gens) else: raise ValueError("unknown algorithm") From e7d8a41e702c2294eb0f54be50855c0db6ddd92a Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Thu, 20 Feb 2025 15:26:43 -0700 Subject: [PATCH 447/507] Modified example to test new functionality --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index da8c72183c2..7cc208a219b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -6123,6 +6123,8 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): True sage: (a^2).in_subalgebra(J, algorithm='groebner') True + sage: (a^2).in_subalgebra(J, algorithm='groebner', certificate='x') + x0^2*x1^2 sage: (a + a^2).in_subalgebra(J) True """ From 5ee0e25d8040534494d0dda0f07cee2cb40f168c Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Thu, 20 Feb 2025 20:10:48 -0700 Subject: [PATCH 448/507] Added blank line after doctest. --- src/sage/misc/functional.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index cdaa44a4348..5c5e3c36e9b 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1150,6 +1150,7 @@ def log(*args, **kwds): +Infinity Check if sub-issue detailed in :issue:`38971` is fixed:: + sage: log(6, base=0) 0 sage: log(e, base=0) From 47cb1e194ad961bbafaddd91346758c78f1f940c Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Thu, 20 Feb 2025 20:32:50 -0700 Subject: [PATCH 449/507] Fixed issue where Partitions() was caching results when an error happens. --- src/sage/combinat/partition.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index dd2ef6fe63e..c1f4f06f129 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -7596,8 +7596,19 @@ def __classcall_private__(cls, n, parts): sage: P2 = Partitions(4, parts_in=(1,2)) sage: P is P2 True + + Ensure that :issue:`38640` is fixed:: + + sage: list(Partitions(4,parts_in=vector(QQ,[2,4]))) + [[4], [2, 2]] + sage: list(Partitions(4,parts_in=vector(QQ,[2,1/4]))) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + sage: list(Partitions(4,parts_in=vector(ZZ,[2,4]))) + [[4], [2, 2]] """ - parts = tuple(sorted(parts)) + parts = tuple(sorted(map(ZZ,parts))) return super().__classcall__(cls, Integer(n), parts) def __init__(self, n, parts): From e4793c3e82df0c3e353a99c53bd6d5d98dc7ff19 Mon Sep 17 00:00:00 2001 From: Noel Roemmele Date: Thu, 20 Feb 2025 22:08:27 -0700 Subject: [PATCH 450/507] Fixed issue where insert_row in dense intger matrices would not accept python integers. --- src/sage/matrix/matrix_integer_dense.pyx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 4738aa2685d..d84a3317733 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -5423,6 +5423,16 @@ cdef class Matrix_integer_dense(Matrix_dense): [ 3 4 5] [ 6 7 8] [ 1 5 -10] + + TESTS: + + Ensure that :issue:`11328` is fixed:: + + sage: m = matrix([[int(1),int(1)],[int(1),int(1)]]) + sage: m.insert_row(1,[int(1),int(1)]) + [1 1] + [1 1] + [1 1] """ cdef Matrix_integer_dense res = self._new(self._nrows + 1, self._ncols) cdef Py_ssize_t j @@ -5438,7 +5448,11 @@ cdef class Matrix_integer_dense(Matrix_dense): for i from 0 <= i < index: fmpz_init_set(fmpz_mat_entry(res._matrix,i,j), fmpz_mat_entry(self._matrix,i,j)) - z = row[j] + try: + z = row[j] + except TypeError: + z = ZZ(row[j]) + fmpz_set_mpz(zflint,z.value) fmpz_init_set(fmpz_mat_entry(res._matrix,index,j), zflint) From 9fc91d9b9b02cdf4a4b9ceb12a091846e11a1289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Feb 2025 10:07:12 +0100 Subject: [PATCH 451/507] cleaning the maximal orders in rational function fields --- .../rings/function_field/order_rational.py | 133 +++++++++--------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py index d90124c6800..cdcdfcafe31 100644 --- a/src/sage/rings/function_field/order_rational.py +++ b/src/sage/rings/function_field/order_rational.py @@ -12,7 +12,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** import sage.rings.abc @@ -43,7 +43,7 @@ class FunctionFieldMaximalOrder_rational(FunctionFieldMaximalOrder): sage: R = K.maximal_order(); R Maximal order of Rational function field in t over Finite Field of size 19 """ - def __init__(self, field): + def __init__(self, field) -> None: """ Initialize. @@ -59,8 +59,8 @@ def __init__(self, field): self._populate_coercion_lists_(coerce_list=[field._ring]) self._ring = field._ring - self._gen = self(self._ring.gen()) - self._basis = (self.one(),) + self._gen = self(self._ring.gen()) # generator as a ring + self._basis = (self.one(),) # basis as a module over itself def _element_constructor_(self, f): """ @@ -72,6 +72,7 @@ def _element_constructor_(self, f): sage: O = K.maximal_order() sage: O._element_constructor_(y) y + sage: O._element_constructor_(1/y) Traceback (most recent call last): ... @@ -84,7 +85,7 @@ def _element_constructor_(self, f): raise TypeError("unable to convert to an element of {}".format(F)) if f.denominator() not in self.function_field().constant_base_field(): - raise TypeError("%r is not an element of %r" % (f,self)) + raise TypeError("%r is not an element of %r" % (f, self)) return f @@ -98,10 +99,11 @@ def ideal_with_gens_over_base(self, gens): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # needs sage.rings.function_field - sage: O = L.equation_order() # needs sage.rings.function_field - sage: O.ideal_with_gens_over_base([x^3 + 1, -y]) # needs sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: O.ideal_with_gens_over_base([x^3 + 1, -y]) Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ideal(gens) @@ -299,7 +301,7 @@ def _residue_field_global(self, q, name=None): # extend the base field to a field of degree r*s over the # prime field s = q.degree() - K,sigma = k.extension(s, map=True, name=name) + K, sigma = k.extension(s, map=True, name=name) # find a root beta in K satisfying the irreducible q S = K['X'] @@ -328,20 +330,22 @@ def to_K(f): coeffs = (f % q).list() return sum((sigma(c) * beta_pow[i] for i, c in enumerate(coeffs)), K.zero()) - if r == 1: # take care of the prime field case + if r == 1: # take care of the prime field case def fr_K(g): co = W.coordinates(V(g), check=False) return R([k(co[j]) for j in range(s)]) else: def fr_K(g): co = W.coordinates(V(g), check=False) - return R([k(co[i:i+r]) for i in range(0, r*s, r)]) + return R([k(co[i:i + r]) for i in range(0, r * s, r)]) return K, fr_K, to_K - def basis(self): + def basis(self) -> tuple: """ - Return the basis (=1) of the order as a module over the polynomial ring. + Return the basis of the order as a module over the polynomial ring. + + This is the tuple (1,). EXAMPLES:: @@ -354,13 +358,18 @@ def basis(self): def gen(self, n=0): """ - Return the ``n``-th generator of the order. Since there is only one generator ``n`` must be 0. + Return the ``n``-th generator of the order. + + Since there is only one generator ``n`` must be 0. EXAMPLES:: sage: O = FunctionField(QQ,'y').maximal_order() sage: O.gen() y + + TESTS:: + sage: O.gen(1) Traceback (most recent call last): ... @@ -370,9 +379,11 @@ def gen(self, n=0): raise IndexError("there is only one generator") return self._gen - def ngens(self): + def ngens(self) -> int: """ - Return 1 the number of generators of the order. + Return the number of generators of the order. + + This is 1. EXAMPLES:: @@ -413,12 +424,12 @@ def ideal(self, *gens): gens = (gens,) K = self.function_field() gens = [K(e) for e in gens if e != 0] - if len(gens) == 0: - gen = K(0) + if not gens: + gen = K.zero() else: d = lcm([c.denominator() for c in gens]).monic() - g = gcd([(d*c).numerator() for c in gens]).monic() - gen = K(g/d) + g = gcd([(d * c).numerator() for c in gens]).monic() + gen = K(g / d) return self.ideal_monoid().element_class(self, gen) @@ -427,6 +438,8 @@ class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfini """ Maximal infinite orders of rational function fields. + These are not finitely-generated rings in general. + INPUT: - ``field`` -- a rational function field @@ -438,7 +451,7 @@ class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfini sage: R = K.maximal_order_infinite(); R Maximal infinite order of Rational function field in t over Finite Field of size 19 """ - def __init__(self, field, category=None): + def __init__(self, field, category=None) -> None: """ Initialize. @@ -447,6 +460,10 @@ def __init__(self, field, category=None): sage: K. = FunctionField(GF(19)) sage: O = K.maximal_order_infinite() sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order_infinite() + sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') """ FunctionFieldMaximalOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_rational, category=PrincipalIdealDomains().or_subcategory(category)) @@ -458,14 +475,16 @@ def _element_constructor_(self, f): EXAMPLES:: - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O._element_constructor_(y) - y + sage: K. = FunctionField(GF(7)) + sage: O = K.maximal_order_infinite() sage: O._element_constructor_(1/y) + 1/y + + sage: O._element_constructor_(y) Traceback (most recent call last): ... - TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field + TypeError: y is not an element of Maximal infinite order of + Rational function field in y over Finite Field of size 7 """ F = self.function_field() try: @@ -478,48 +497,23 @@ def _element_constructor_(self, f): return f - def basis(self): + def basis(self) -> tuple: """ - Return the basis (=1) of the order as a module over the polynomial ring. + Return the basis of the maximal infinite order as a module over itself. EXAMPLES:: sage: K. = FunctionField(GF(19)) - sage: O = K.maximal_order() + sage: O = K.maximal_order_infinite() sage: O.basis() (1,) - """ - return 1/self.function_field().gen() - - def gen(self, n=0): - """ - Return the `n`-th generator of ``self``. Since there is only one - generator `n` must be `0`. - EXAMPLES:: - - sage: O = FunctionField(QQ,'y').maximal_order() - sage: O.gen() - y - sage: O.gen(1) - Traceback (most recent call last): - ... - IndexError: there is only one generator - """ - if n != 0: - raise IndexError("there is only one generator") - return self._gen - - def ngens(self): - """ - Return 1 the number of generators of the order. - - EXAMPLES:: - - sage: FunctionField(QQ,'y').maximal_order().ngens() - 1 + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order_infinite() + sage: O.basis() + (1,) """ - return 1 + return (1,) def prime_ideal(self): """ @@ -533,7 +527,7 @@ def prime_ideal(self): Ideal (1/t) of Maximal infinite order of Rational function field in t over Finite Field of size 19 """ - return self.ideal( 1/self.function_field().gen() ) + return self.ideal(1 / self.function_field().gen()) def ideal(self, *gens): """ @@ -548,15 +542,19 @@ def ideal(self, *gens): sage: K. = FunctionField(QQ) sage: O = K.maximal_order_infinite() sage: O.ideal(x) - Ideal (x) of Maximal infinite order of Rational function field in x over Rational Field + Ideal (x) of Maximal infinite order of Rational function field in x + over Rational Field sage: O.ideal([x, 1/x]) == O.ideal(x ,1/x) # multiple generators may be given as a list True sage: O.ideal(x^3 + 1, x^3 + 6) - Ideal (x^3) of Maximal infinite order of Rational function field in x over Rational Field + Ideal (x^3) of Maximal infinite order of Rational function field in x + over Rational Field sage: I = O.ideal((x^2+1)*(x^3+1), (x^3+6)*(x^2+1)); I - Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field + Ideal (x^5) of Maximal infinite order of Rational function field in x + over Rational Field sage: O.ideal(I) - Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field + Ideal (x^5) of Maximal infinite order of Rational function field in x + over Rational Field """ if len(gens) == 1: gens = gens[0] @@ -568,9 +566,10 @@ def ideal(self, *gens): K = self.function_field() gens = [K(g) for g in gens] try: - d = max(g.numerator().degree() - g.denominator().degree() for g in gens if g != 0) + d = max(g.numerator().degree() - g.denominator().degree() + for g in gens if g != 0) gen = K.gen() ** d - except ValueError: # all gens are zero - gen = K(0) + except ValueError: # all gens are zero + gen = K.zero() return self.ideal_monoid().element_class(self, gen) From 626f7c93e7c2ce486f22e0e048422529459069f4 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:39:12 +0700 Subject: [PATCH 452/507] Revert unintended change, add a test for it --- src/sage/categories/rings.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index e4cf8ed94cb..a7147178ad6 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1018,6 +1018,22 @@ def ideal(self, *args, **kwds): Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field sage: R.ideal() Principal ideal (0) of Univariate Polynomial Ring in x over Rational Field + + Check ``ideal_class=`` keyword argument when input is empty:: + + sage: from sage.rings.ideal import Ideal_pid + sage: class CustomIdealClass(Ideal_pid): + ....: pass + sage: type(ZZ.ideal(6)) + + sage: type(ZZ.ideal(6, ideal_class=CustomIdealClass)) + + sage: type(ZZ.ideal()) + + sage: type(ZZ.ideal(ideal_class=CustomIdealClass)) + + sage: type(ZZ.ideal((), ideal_class=CustomIdealClass)) + """ if 'coerce' in kwds: coerce = kwds['coerce'] @@ -1027,7 +1043,7 @@ def ideal(self, *args, **kwds): from sage.rings.ideal import Ideal_generic if not args: - return self.zero_ideal() + gens = [self(0)] else: gens = args while isinstance(gens, (list, tuple, GeneratorType)) and len(gens) == 1: @@ -1051,7 +1067,7 @@ def ideal(self, *args, **kwds): break if not gens: - return self.zero_ideal() + gens = [self.zero()] elif coerce: gens = [self(g) for g in gens] From d44a769dc5bd7393a948a9564863c1df62c3f866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Feb 2025 14:14:22 +0100 Subject: [PATCH 453/507] fixes --- src/sage/combinat/designs/resolvable_bibd.py | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 9ebe55cd577..d314d771d75 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -113,7 +113,7 @@ def resolvable_balanced_incomplete_block_design(v, k, existence=False): # (15,5,2) (k == 6 and v == 36) or # Fisher's inequality - (v*(v-1))/(k*(k-1)) < v): + (v*(v-1))/(k*(k-1)) < v): if existence: return False raise EmptySetError("There exists no ({},{},{})-RBIBD".format(v, k, 1)) @@ -140,7 +140,7 @@ def resolvable_balanced_incomplete_block_design(v, k, existence=False): if existence: return Unknown - raise NotImplementedError(f"I don't know how to build a ({v},{3},1)-RBIBD!") + raise NotImplementedError(f"I don't know how to build a ({v},3,1)-RBIBD!") def kirkman_triple_system(v, existence=False): @@ -417,7 +417,7 @@ def v_4_1_rbibd(v, existence=False): if v % 3 != 1 or not is_prime_power((v-1)//3): if existence: return Unknown - raise NotImplementedError(f"I don't know how to build a ({v},{4},1)-RBIBD!") + raise NotImplementedError(f"I don't know how to build a ({v},4,1)-RBIBD!") from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//3 nn = (q-1)//4 @@ -489,7 +489,7 @@ def PBD_4_7(v, check=True, existence=False): # Beth/Jungnickel/Lenz: take KTS(15) and extend each of the 7 classes # with a new point. Make those new points a 7-set. KTS15 = kirkman_triple_system(15) - blocks = [S+[i+15] for i, classs in enumerate(KTS15._classes) for S in classs]+[list(range(15, 22))] + blocks = [S+[i+15] for i, classs in enumerate(KTS15._classes) for S in classs] + [list(range(15, 22))] elif v == 34: # [BJL99] (p527,vol1), but originally Brouwer @@ -500,11 +500,11 @@ def PBD_4_7(v, check=True, existence=False): A = [[(x+i, y+j) for x, y in A] for i in range(9) for j in range(3)] - B = [[(x+i, y+i+j) for x, y in B]+[27+j] + B = [[(x+i, y+i+j) for x, y in B] + [27+j] for i in range(9) for j in range(3)] - C = [[(x+i+j, y+2*i+j) for x, y in C]+[30+j] + C = [[(x+i+j, y+2*i+j) for x, y in C] + [30+j] for i in range(9) for j in range(3)] - D = [[(x+i, y+i) for x, y in D]+[33] + D = [[(x+i, y+i) for x, y in D] + [33] for i in range(9)] blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*9+(x[0] % 9) @@ -522,11 +522,11 @@ def PBD_4_7(v, check=True, existence=False): for i in range(13) for j in range(3)] B = [[(x+i, y+j) for x, y in B] for i in range(13) for j in range(3)] - C = [[(x+i, y+j) for x, y in C]+[39+j] + C = [[(x+i, y+j) for x, y in C] + [39+j] for i in range(13) for j in range(3)] - D = [[(x+i, y+j) for x, y in D]+[42+j] + D = [[(x+i, y+j) for x, y in D] + [42+j] for i in range(13) for j in range(3)] - E = [[(x+i, y+i) for x, y in E]+[45] + E = [[(x+i, y+i) for x, y in E] + [45] for i in range(13)] blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*13+(x[0] % 13) @@ -548,11 +548,11 @@ def PBD_4_7(v, check=True, existence=False): for i in range(17) for j in range(3)] C = [[(x+i, y+j) for x, y in C] for i in range(17) for j in range(3)] - D = [[(x+i, y+j) for x, y in D]+[51+j] + D = [[(x+i, y+j) for x, y in D] + [51+j] for i in range(17) for j in range(3)] - E = [[(x+i, y+j) for x, y in E]+[54+j] + E = [[(x+i, y+j) for x, y in E] + [54+j] for i in range(17) for j in range(3)] - F = [[(x+i, y+i) for x, y in F]+[57] + F = [[(x+i, y+i) for x, y in F] + [57] for i in range(17)] blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*17+(x[0] % 17) @@ -577,11 +577,11 @@ def PBD_4_7(v, check=True, existence=False): for i in range(21) for j in range(3)] D = [[(x+i, y+j) for x, y in D] for i in range(21) for j in range(3)] - E = [[(x+i, y+j) for x, y in E]+[63+j] + E = [[(x+i, y+j) for x, y in E] + [63+j] for i in range(21) for j in range(3)] - F = [[(x+3*i+j, y+ii+j) for x, y in F]+[66+j] + F = [[(x+3*i+j, y+ii+j) for x, y in F] + [66+j] for i in range(7) for j in range(3) for ii in range(3)] - H = [[(x+i, y+i) for x, y in H]+[69] + H = [[(x+i, y+i) for x, y in H] + [69] for i in range(21)] blocks = [[int(x) if not isinstance(x, tuple) else (x[1] % 3)*21+(x[0] % 21) @@ -701,7 +701,7 @@ def PBD_4_7(v, check=True, existence=False): u = vv-4*g if (orthogonal_array(5, g, existence=True) is True and PBD_4_7(3*g+1, existence=True) is True and - PBD_4_7(3*u+1, existence=True) is True): + PBD_4_7(3*u+1, existence=True) is True): from .orthogonal_arrays import transversal_design domain = set(range(vv)) GDD = transversal_design(5, g) @@ -788,7 +788,7 @@ def PBD_4_7_from_Y(gdd, check=True): GDD[7] = PBD_4_7(22) x = set(range(22)).difference(*[S for S in GDD[7] if len(S) != 4]).pop() relabel = sum((S for S in GDD[7] if x in S), []) # the groups must be 012,345,... - relabel = [xx for xx in relabel if xx != x]+[x] + relabel = [xx for xx in relabel if xx != x] + [x] GDD[7].relabel({v: i for i, v in enumerate(relabel)}) GDD[7] = [S for S in GDD[7] if 21 not in S] From 9b0603a289fff1253775e201d2fff52f45fd1651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Feb 2025 15:58:16 +0100 Subject: [PATCH 454/507] trying to use latex \cfrac --- src/sage/rings/power_series_ring_element.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 72853f7969b..e0fb7af1429 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1388,15 +1388,13 @@ cdef class PowerSeries(AlgebraElement): r""" Return the super delta continued fraction of ``self``. - This is a continued fraction of the following shape:: + This is a continued fraction of the following shape: - v0 x^k0 - -------------------------------------------------------- - v1 x^(k0 + k1 + delta) - U1(x) - ------------------------------------------- - v2 x^(k1 +k2 + delta) - U2(x) - ------------------------- - U3(x) - ... + .. MATH:: + + \cfrac{v_0 x^{k_0}}{U_1(x) - + \cfrac{v_1 x^{k_0 + k_1 + \delta}{U_2(x) - + \cfrac{v_2 x^{k_0 + k_1 + k_2 + \delta}{U_3(x) - \cdots}}}}} where each `U_j(x) = 1 + u_j(x) x`. @@ -1429,6 +1427,8 @@ cdef class PowerSeries(AlgebraElement): (1, 1, 3*q^2 + 2*q + 1), (-4, 0, -q + 1)] + A Jacobi continued fraction:: + sage: t = PowerSeriesRing(QQ, 't').gen() sage: s = sum(factorial(k) * t**k for k in range(12)).O(12) sage: s.super_delta_fraction(2) From 49b97584e8e22e7a3d8e58f2f0515508aa3671ca Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Fri, 21 Feb 2025 17:53:37 +0100 Subject: [PATCH 455/507] Update src/sage/matrix/matrix_polynomial_dense.pyx Co-authored-by: Hugo Passe <107032003+HugoPasse@users.noreply.github.com> --- src/sage/matrix/matrix_polynomial_dense.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 17a85cc696b..cea4f5ed6a6 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3824,7 +3824,7 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): INPUT: - ``points`` -- list of elements from the base field (or coercible into - it), of list of such lists + it), or list of such lists - ``shifts`` -- (default: ``None``) list of integers; ``None`` is interpreted as ``shifts=[0,...,0]`` From 5b8250d5feceef037312d8e289dc8fb17870cdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Feb 2025 21:28:37 +0100 Subject: [PATCH 456/507] fix latex code --- src/sage/rings/power_series_ring_element.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index e0fb7af1429..7496a0ebaff 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1392,9 +1392,9 @@ cdef class PowerSeries(AlgebraElement): .. MATH:: - \cfrac{v_0 x^{k_0}}{U_1(x) - - \cfrac{v_1 x^{k_0 + k_1 + \delta}{U_2(x) - - \cfrac{v_2 x^{k_0 + k_1 + k_2 + \delta}{U_3(x) - \cdots}}}}} + \cfrac{v_0 x^{k_0}} {U_1(x) - + \cfrac{v_1 x^{k_0 + k_1 + \delta}} {U_2(x) - + \cfrac{v_2 x^{k_0 + k_1 + k_2 + \delta}} {U_3(x) - \cdots} } } where each `U_j(x) = 1 + u_j(x) x`. From 9cd86e9596a6d996611cd7cc9281ac5a95fda89c Mon Sep 17 00:00:00 2001 From: Release Manager Date: Fri, 21 Feb 2025 21:59:24 +0100 Subject: [PATCH 457/507] Updated SageMath version to 10.6.beta7 --- CITATION.cff | 4 ++-- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 4 ++-- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/version_requirements.txt | 2 +- build/pkgs/sage_docbuild/version_requirements.txt | 2 +- build/pkgs/sage_setup/version_requirements.txt | 2 +- build/pkgs/sage_sws2rst/version_requirements.txt | 2 +- build/pkgs/sagelib/version_requirements.txt | 2 +- build/pkgs/sagemath_bliss/version_requirements.txt | 2 +- build/pkgs/sagemath_categories/version_requirements.txt | 2 +- build/pkgs/sagemath_coxeter3/version_requirements.txt | 2 +- build/pkgs/sagemath_environment/version_requirements.txt | 2 +- build/pkgs/sagemath_mcqd/version_requirements.txt | 2 +- build/pkgs/sagemath_meataxe/version_requirements.txt | 2 +- build/pkgs/sagemath_objects/version_requirements.txt | 2 +- build/pkgs/sagemath_repl/version_requirements.txt | 2 +- build/pkgs/sagemath_sirocco/version_requirements.txt | 2 +- build/pkgs/sagemath_tdlib/version_requirements.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_conda/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-bliss/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-coxeter3/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-mcqd/VERSION.txt | 2 +- pkgs/sagemath-meataxe/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- pkgs/sagemath-sirocco/VERSION.txt | 2 +- pkgs/sagemath-tdlib/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 38 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 305ef37cbd5..bf07776a356 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.6.beta6 +version: 10.6.beta7 doi: 10.5281/zenodo.8042260 -date-released: 2025-02-10 +date-released: 2025-02-21 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index 0dec762feaa..c20b0627cda 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.6.beta6, Release Date: 2025-02-10 +SageMath version 10.6.beta7, Release Date: 2025-02-21 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ad88cab35b5..67bb44a82c3 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=6aea4942af8ac8278125e205c7408aa98f327cd6 -sha256=d088d2ad67c8381bc8eaee6f46ce88f3db07fe557dfe2c50ad2f37e3300c24ff +sha1=d2332c79700273e351ce2b63347e4599e4e2ce43 +sha256=7aa9a2c919f5afc679372a632ad93494563e6ff2218ec76a3422b267cf684030 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 5589a99c8ee..a3e4b7e2ebc 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -950a954e4dc44a1b1fe746e052ac1501643ca507 +c2766d1f6250032244ca3880f14c5b424f1bdd83 diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 77d6c0cb0c5..07a61ab735e 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.6b6 +sage-conf ~= 10.6b7 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index 50eb65e2172..f0421af21af 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.6b6 +sage-docbuild ~= 10.6b7 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index 06adbdc6df6..48fc6cd26a5 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.6b6 +sage-setup ~= 10.6b7 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index dd05f21376e..22e0bd981fc 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.6b6 +sage-sws2rst ~= 10.6b7 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index e64a5d0c206..92e731e689a 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.6b6 +sagemath-standard ~= 10.6b7 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index d88731f06e9..1dcf8a9322a 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.6b6 +sagemath-bliss ~= 10.6b7 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 198cb911336..639f01d78ee 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.6b6 +sagemath-categories ~= 10.6b7 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index 7415f70536b..f715f9357e4 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.6b6 +sagemath-coxeter3 ~= 10.6b7 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 09106be9ba3..93ecd3e5a14 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.6b6 +sagemath-environment ~= 10.6b7 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index bff6125dcaf..70273aad838 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.6b6 +sagemath-mcqd ~= 10.6b7 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index 662a7b7e7f6..8036474a775 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.6b6 +sagemath-meataxe ~= 10.6b7 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index df4dc506909..66992114227 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.6b6 +sagemath-objects ~= 10.6b7 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index 5f906d7000a..22b16b93978 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.6b6 +sagemath-repl ~= 10.6b7 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index 9077fb7fff0..3dd38fd94ac 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.6b6 +sagemath-sirocco ~= 10.6b7 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 37d47b62c5f..15ac9d50e66 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.6b6 +sagemath-tdlib ~= 10.6b7 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/src/VERSION.txt b/src/VERSION.txt index 8b3698135db..51a36a91c8a 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.6.beta6 +10.6.beta7 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index d1e2aa14731..2dea11b2d1e 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.6.beta6' -SAGE_RELEASE_DATE='2025-02-10' -SAGE_VERSION_BANNER='SageMath version 10.6.beta6, Release Date: 2025-02-10' +SAGE_VERSION='10.6.beta7' +SAGE_RELEASE_DATE='2025-02-21' +SAGE_VERSION_BANNER='SageMath version 10.6.beta7, Release Date: 2025-02-21' diff --git a/src/sage/version.py b/src/sage/version.py index 6291f1b503f..37a2f5f5fba 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.6.beta6' -date = '2025-02-10' -banner = 'SageMath version 10.6.beta6, Release Date: 2025-02-10' +version = '10.6.beta7' +date = '2025-02-21' +banner = 'SageMath version 10.6.beta7, Release Date: 2025-02-21' From 9978c73c2be35a9461eb2740a405d337af72426f Mon Sep 17 00:00:00 2001 From: Caleb Van't Land <120340570+pyrusbrawler64@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:06:51 -0700 Subject: [PATCH 458/507] Clarified Documentation Co-authored-by: Travis Scrimshaw --- .../polynomial/multi_polynomial_libsingular.pyx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 7cc208a219b..c3b7f9aabdc 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -6104,13 +6104,11 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): and only if the remainder involves only the new variables `z_i`. - - ``certificate`` -- An expression that evaluates to True or False. If - it evaluates to False (the default) then return either True or False. - If it evaluates to True and 'algorithm' is set to 'groebner', return - the polynomial generated by the algorithm if such a polynomial exists, - otherwise return None. If a string is provided which evaluates to - True then this string will be used as the name of the variables in the - returned polynomial, otherwise the name will be 'newgens' + - ``certificate`` -- boolean (default: ``False``) or string; if``True`` + and ``algorithm='groebner'``, return the polynomial generated by + the algorithm if such a polynomial exists, otherwise return ``None``; + if a nonempty string, then this is used as the name of the variables + in the returned polynomial, otherwise the name will be ``'newgens'`` EXAMPLES:: From cfa2989cf0440e0f237cac4f1df0eb27497ede46 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land <120340570+pyrusbrawler64@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:08:53 -0700 Subject: [PATCH 459/507] Code Formatting Improvement Co-authored-by: Travis Scrimshaw --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index c3b7f9aabdc..54cb6af843a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -6154,12 +6154,11 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): new_gens = S.gens()[-ngens:] I = S.ideal([g - S(j) for (g,j) in zip(new_gens, J)]) z = S(self).reduce(I) - if (certificate): - if (set(z.variables()).issubset(new_gens)): + if certificate: + if set(z.variables()).issubset(new_gens): T = PolynomialRing(R.base_ring(), tuple(new_gens)) return T(z) - else: - return None + return None return set(z.variables()).issubset(new_gens) else: raise ValueError("unknown algorithm") From 6367f3a6ffafec0eb44118c2dd89cf2b6d60c74c Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Fri, 21 Feb 2025 14:22:30 -0700 Subject: [PATCH 460/507] Corrected Documentation of monsky_washnitzer.SpecialCubicQuotientRing.gens() --- src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 36d629888b5..23766b59996 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -572,7 +572,7 @@ def poly_ring(self): def gens(self): """ - Return a list [x, T] where x and T are the generators of the ring + Return a tuple (x, T) where x and T are the generators of the ring (as element *of this ring*). .. NOTE:: From 0ac1e7ea633fa1f1e448f1debca95c752a329c17 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Fri, 21 Feb 2025 14:41:15 -0700 Subject: [PATCH 461/507] Modified EllipticCurve_number_field.gens() to return tuple, changed doctests accordingly --- .../elliptic_curves/ell_number_field.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 518fda03481..cafa2eaee3e 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -158,7 +158,7 @@ def base_extend(self, R): [(52 : 111 : 1)] sage: EK = E.base_extend(K) sage: EK.gens() - [(52 : 111 : 1)] + ((52 : 111 : 1),) """ E = super().base_extend(R) if isinstance(E, EllipticCurve_number_field): @@ -2328,9 +2328,9 @@ def gens(self, **kwds): sage: K. = NumberField(x^2 + 23, 'a') sage: E = EllipticCurve(K,[0,0,0,101,0]) sage: E.gens() - [(23831509/8669448*a - 2867471/8669448 : 76507317707/18049790736*a - 424166479633/18049790736 : 1), + ((23831509/8669448*a - 2867471/8669448 : 76507317707/18049790736*a - 424166479633/18049790736 : 1), (-2031032029/969232392*a + 58813561/969232392 : -15575984630401/21336681877488*a + 451041199309/21336681877488 : 1), - (-186948623/4656964 : 549438861195/10049728312*a : 1)] + (-186948623/4656964 : 549438861195/10049728312*a : 1)) It can happen that no points are found if the height bounds used in the search are too small (see :issue:`10745`):: @@ -2338,11 +2338,11 @@ def gens(self, **kwds): sage: K. = NumberField(x^4 + x^2 - 7) sage: E = EllipticCurve(K, [1, 0, 5*t^2 + 16, 0, 0]) sage: E.gens(lim1=1, lim3=1) - [] + () sage: E.rank() 1 sage: gg=E.gens(lim3=13); gg # long time (about 4s) - [(... : 1)] + ((... : 1),) Check that the point found has infinite order, and that it is on the curve:: @@ -2356,7 +2356,7 @@ def gens(self, **kwds): sage: K. = NumberField(x^2 - 17) sage: E = EllipticCurve(K, [-4, 0]) sage: E.gens() - [(-1/2*t + 1/2 : -1/2*t + 1/2 : 1), (-t + 3 : -2*t + 10 : 1)] + ((-1/2*t + 1/2 : -1/2*t + 1/2 : 1), (-t + 3 : -2*t + 10 : 1)) sage: E.rank() 2 @@ -2368,7 +2368,7 @@ def gens(self, **kwds): sage: EK.rank() 0 sage: EK.gens() - [] + () IMPLEMENTATION: @@ -2378,10 +2378,10 @@ def gens(self, **kwds): PARI/GP scripts from http://www.math.unicaen.fr/~simon/. """ try: - return self.gens_quadratic(**kwds) + return tuple(self.gens_quadratic(**kwds)) except ValueError: self.simon_two_descent(**kwds) - return self._known_points + return tuple(self._known_points) def period_lattice(self, embedding): r""" From 721a15284d762e205d146fffd6849d05dd2377c2 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Fri, 21 Feb 2025 14:45:12 -0700 Subject: [PATCH 462/507] Changed RealField_class.gens() to return a tuple, modified documentation and examples accordingly --- src/sage/rings/real_mpfr.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 31d0bb96aed..0c92c716988 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -909,14 +909,14 @@ cdef class RealField_class(sage.rings.abc.RealField): def gens(self): """ - Return a list of generators. + Return a tuple of generators. EXAMPLES:: sage: RR.gens() - [1.00000000000000] + (1.00000000000000,) """ - return [self.gen()] + return (self.gen(),) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ From 2480b6111a6640df3648be33de1f7dea872b4987 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Fri, 21 Feb 2025 14:47:11 -0700 Subject: [PATCH 463/507] Changed RealIntervalField_class.gens() to return a tuple, modified documentation and examples accordingly --- src/sage/rings/real_mpfi.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index bbe72c26ba2..e5e8f7df114 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -952,14 +952,14 @@ cdef class RealIntervalField_class(sage.rings.abc.RealIntervalField): def gens(self): """ - Return a list of generators. + Return a tuple of generators. EXAMPLES:: sage: RIF.gens() - [1] + (1,) """ - return [self.gen()] + return (self.gen(),) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ From 027875c76464008df7eae04fc1e894edc5492def Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Fri, 21 Feb 2025 14:50:41 -0700 Subject: [PATCH 464/507] Changed SymmetricReductionStrategy.gens() to return a tuple, modified documentation and examples accordingly --- src/sage/rings/polynomial/symmetric_reduction.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/symmetric_reduction.pyx b/src/sage/rings/polynomial/symmetric_reduction.pyx index 0794ca8a941..a57c52a9ad0 100644 --- a/src/sage/rings/polynomial/symmetric_reduction.pyx +++ b/src/sage/rings/polynomial/symmetric_reduction.pyx @@ -256,7 +256,7 @@ cdef class SymmetricReductionStrategy: def gens(self): """ - Return the list of Infinite Polynomials modulo which ``self`` reduces. + Return the tuple of Infinite Polynomials modulo which ``self`` reduces. EXAMPLES:: @@ -269,9 +269,9 @@ cdef class SymmetricReductionStrategy: y_2*y_1^2, y_2^2*y_1 sage: S.gens() - [y_2*y_1^2, y_2^2*y_1] + (y_2*y_1^2, y_2^2*y_1) """ - return self._lm + return tuple(self._lm) def setgens(self, L): """ From 739b9cd56e15863fcace6e1ed022722e63f89d23 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land <120340570+pyrusbrawler64@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:02:12 -0700 Subject: [PATCH 465/507] Fix Typo --- src/sage/combinat/designs/latin_squares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 937976b9206..41f5a1ae6b9 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -383,7 +383,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): matrices = construction()[:k] - # Implements the construction from Theorem 5.2.4 of [KD2015]_ for primw powers. + # Implements the construction from Theorem 5.2.4 of [KD2015]_ for prime powers. # This was implemented to fix :issue:`26107`, which pointed out that this # function was unacceptably slow when n was a large prime power elif is_prime_power(n): From cc680ff9aa63367be5414fd3f9dc6e7aa92e78d5 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land <120340570+pyrusbrawler64@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:06:27 -0700 Subject: [PATCH 466/507] Added blank line --- src/sage/groups/generic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index 6af3b1c863b..ed9f95f9a91 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -891,6 +891,7 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i ....: assert res == sol Verify that :issue:`38316` is fixed:: + sage: F = GF(5) sage: base = F(3) sage: a = F(1) From 9cb2a07b0650d38eef6796d5196b003f5b8a63ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 08:44:21 +0100 Subject: [PATCH 467/507] remove sentence, break some long lines --- src/sage/rings/function_field/order_rational.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py index cdcdfcafe31..eff5dd4613d 100644 --- a/src/sage/rings/function_field/order_rational.py +++ b/src/sage/rings/function_field/order_rational.py @@ -76,7 +76,8 @@ def _element_constructor_(self, f): sage: O._element_constructor_(1/y) Traceback (most recent call last): ... - TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field + TypeError: 1/y is not an element of Maximal order of + Rational function field in y over Rational Field """ F = self.function_field() try: @@ -112,8 +113,9 @@ def _residue_field(self, ideal, name=None): """ Return a field isomorphic to the residue field at the prime ideal. - The residue field is by definition `k[x]/q` where `q` is the irreducible - polynomial generating the prime ideal and `k` is the constant base field. + The residue field is by definition `k[x]/q` where `q` is the + irreducible polynomial generating the prime ideal and `k` is + the constant base field. INPUT: @@ -438,8 +440,6 @@ class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfini """ Maximal infinite orders of rational function fields. - These are not finitely-generated rings in general. - INPUT: - ``field`` -- a rational function field From b2b07ce15aae40b1b18b3a9a6ecd0229ffaa1559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 08:50:51 +0100 Subject: [PATCH 468/507] suggested details --- src/sage/rings/power_series_ring_element.pyx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 7496a0ebaff..9ffe31712e8 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1400,11 +1400,9 @@ cdef class PowerSeries(AlgebraElement): INPUT: - - ``delta`` -- positive integer, most usually 2 + - ``delta`` -- positive integer, usually 2 - OUTPUT: - - list of `(v_j, k_j, U_{j+1}(x))_{j \geq 0}` + OUTPUT: list of `(v_j, k_j, U_{j+1}(x))_{j \geq 0}` REFERENCES: From 387f823c5d8bc54e6bdb84fd3b757fcde933505b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 09:36:27 +0100 Subject: [PATCH 469/507] some fixes for E114 --- .../dynamics/arithmetic_dynamics/wehlerK3.py | 4 +- src/sage/game_theory/matching_game.py | 2 +- src/sage/groups/free_group.py | 10 +- src/sage/interfaces/maxima.py | 17 +-- .../modular/pollack_stevens/fund_domain.py | 28 ++--- src/sage/plot/plot.py | 100 ++++++++++-------- .../rings/algebraic_closure_finite_field.py | 25 +++-- src/sage/rings/padics/lattice_precision.py | 17 +-- src/sage/schemes/generic/algebraic_scheme.py | 9 +- 9 files changed, 115 insertions(+), 97 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index fab391ececf..8fa12d51c17 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -2066,8 +2066,8 @@ def fiber(self, p, component): Points = [] if (self.Gpoly(component,0)(P0) != 0): - #We are using the quadratic formula, we need this check to ensure that the points - #will be rational + # We are using the quadratic formula, we need this check + # to ensure that the points will be rational T0 = (self.Hpoly(component, 0, 1)(P0)**2 - 4*self.Gpoly(component, 0)(P0)*self.Gpoly(component, 1)(P0)) T1 = (self.Hpoly(component, 0, 2)(P0)**2 - 4*self.Gpoly(component, 0)(P0)*self.Gpoly(component, 2)(P0)) if (T0.is_square() and T1.is_square()): diff --git a/src/sage/game_theory/matching_game.py b/src/sage/game_theory/matching_game.py index 7a51988baec..dd1db3cd388 100644 --- a/src/sage/game_theory/matching_game.py +++ b/src/sage/game_theory/matching_game.py @@ -493,7 +493,7 @@ def __eq__(self, other): zip(set(self._suitors), set(other._suitors)))) __hash__ = None - # not hashable because this is mutable. + # not hashable because this is mutable. def plot(self): r""" diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index bc952896e67..2324d81538e 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -457,8 +457,9 @@ def fox_derivative(self, gen, im_gens=None, ring=None): R = ring symb = list(im_gens) symb += reversed([a**(-1) for a in im_gens]) - i = gen.Tietze()[0] # So ``gen`` is the `i`-th - # generator of the free group. + i = gen.Tietze()[0] + # so gen is the i-th generator of the free group + a = R.zero() coef = R.one() while l: @@ -506,7 +507,7 @@ def syllables(self): for i in range(k): exponent = exponent_syllable(g, i+1).sage() generator = gen(generator_syllable(g, i+1).sage() - 1) - result.append( (generator, exponent) ) + result.append((generator, exponent)) return tuple(result) def __call__(self, *values): @@ -936,6 +937,7 @@ def quotient(self, relations, **kwds): Finitely presented group < a, b, c, d | a*b*a^-1 > """ from sage.groups.finitely_presented import FinitelyPresentedGroup - return FinitelyPresentedGroup(self, tuple(map(self, relations) ), **kwds) + return FinitelyPresentedGroup(self, + tuple(map(self, relations)), **kwds) __truediv__ = quotient diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index 08a8f37f751..ba31f56aa4f 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -504,9 +504,9 @@ from .expect import (Expect, ExpectElement, gc_disabled) from .maxima_abstract import (MaximaAbstract, MaximaAbstractFunction, - MaximaAbstractElement, - MaximaAbstractFunctionElement, - MaximaAbstractElementFunction) + MaximaAbstractElement, + MaximaAbstractFunctionElement, + MaximaAbstractElementFunction) from sage.misc.instancedoc import instancedoc @@ -586,10 +586,13 @@ def __init__(self, script_subdirectory=None, logfile=None, server=None, self._ask = [b'zero or nonzero\\?', b'an integer\\?', b'positive, negative or zero\\?', b'positive or negative\\?', b'positive or zero\\?', b'equal to .*\\?'] + self._prompt_wait = ([self._prompt] + [re.compile(x) for x in self._ask] + - [b'Break [0-9]+']) # note that you might need to change _expect_expr if you - # change this + [b'Break [0-9]+']) + # note that you might need to change _expect_expr if you + # change this _prompt_wait + self._error_re = re.compile('(Principal Value|debugmode|incorrect syntax|Maxima encountered a Lisp error)') self._display2d = False @@ -811,7 +814,7 @@ def _eval_line(self, line, allow_use_file=False, self._expect_expr() assert len(self._before()) == 0, \ - 'Maxima expect interface is confused!' + 'Maxima expect interface is confused!' r = self._output_prompt_re m = r.search(out) if m is not None: @@ -984,7 +987,7 @@ def lisp(self, cmd): ( """ self._eval_line(':lisp %s\n""' % cmd, allow_use_file=False, - wait_for_prompt=False, reformat=False, error_check=False) + wait_for_prompt=False, reformat=False, error_check=False) self._expect_expr('(%i)') return self._before() diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index 806b08709c3..f1647996153 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -1151,7 +1151,7 @@ def form_list_of_cusps(self): full_domain = False # Says that we are not done yet! - v = [False for r in range(sP)] + v = [False for _ in range(sP)] # This initializes a list indexed by P^1(Z/NZ) which keeps track of # which right coset representatives we've found for Gamma_0(N)/SL_2(Z) # thru the construction of a fundamental domain @@ -1171,8 +1171,8 @@ def form_list_of_cusps(self): # This loop runs through the current set of cusps # and checks to see if more cusps should be added # ----------------------------------------------- - for s in range(1, len(C), 2): # range over odd indices in the - # final list C + for s in range(1, len(C), 2): + # range over odd indices in the final list C if C[s] == "?": # Single out our two cusps (path from cusp2 to cusp1) @@ -1188,9 +1188,9 @@ def form_list_of_cusps(self): # This is the point where it is determined whether # or not the adjacent triangle should be added # ------------------------------------------------ - pos = P.index(b2, b1) # The Sage index of the bottom - # row of our unimodular - # transformation gam + pos = P.index(b2, b1) + # The Sage index of the bottom row of our + # unimodular transformation gam # Check if we need to flip (since this P1 element has not # yet been accounted for!) @@ -1214,16 +1214,18 @@ def form_list_of_cusps(self): # where gam is the matrix corresponding to the # unimodular path connecting cusp1 to cusp2 - C[s] = "i" # The '?' is changed to an 'i' - # indicating that a new cusp needs to - # be inserted here + C[s] = "i" + # The '?' is changed to an 'i' indicating + # that a new cusp needs to be inserted here full_domain = False else: - C[s] = "x" # The '?' is changed to an 'x' and no - # more checking below is needed! =) + C[s] = "x" + # The '?' is changed to an 'x' and no + # more checking below is needed! =) else: - C[s] = "x" # The '?' is changed to an 'x' and no more - # checking below is needed! =) + C[s] = "x" + # The '?' is changed to an 'x' and no more + # checking below is needed! =) # Now insert the missing cusps (where there is an 'i' in # the final list) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 4c6ad2000b7..4cdead2ccd4 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -2017,16 +2017,16 @@ def f(x): return (floor(x)+0.5) / (1-(x-0.5)**2) if 'color' in kwds and 'rgbcolor' in kwds: raise ValueError('only one of color or rgbcolor should be specified') elif 'color' in kwds: - kwds['rgbcolor'] = kwds.pop('color', (0,0,1)) # take blue as default ``rgbcolor`` + kwds['rgbcolor'] = kwds.pop('color', (0, 0, 1)) # take blue as default ``rgbcolor`` G_kwds = Graphics._extract_kwds_for_show(kwds, ignore=['xmin', 'xmax']) if 'scale' in G_kwds: - kwds['scale'] = G_kwds['scale'] # pass scaling information to _plot too + kwds['scale'] = G_kwds['scale'] # pass scaling information to _plot too original_opts = kwds.pop('__original_opts', {}) - do_show = kwds.pop('show',False) + do_show = kwds.pop('show', False) from sage.structure.element import Vector - if kwds.get('parametric',False) and isinstance(funcs, Vector): + if kwds.get('parametric', False) and isinstance(funcs, Vector): funcs = tuple(funcs) if hasattr(funcs, 'plot'): @@ -2171,7 +2171,7 @@ def _plot(funcs, xrange, parametric=False, from sage.plot.misc import setup_for_eval_on_grid if funcs == []: return Graphics() - orig_funcs = funcs # keep the original functions (for use in legend labels) + orig_funcs = funcs # keep the original functions (for use in legend labels) excluded_points = [] imag_tol = options["imaginary_tolerance"] funcs, ranges = setup_for_eval_on_grid(funcs, @@ -2192,7 +2192,7 @@ def _plot(funcs, xrange, parametric=False, # Otherwise, let the plot color be 'blue'. if parametric or not isinstance(funcs, (list, tuple)): if 'rgbcolor' in options.keys() and options['rgbcolor'] == 'automatic': - options['rgbcolor'] = (0, 0, 1) # default color for a single curve. + options['rgbcolor'] = (0, 0, 1) # default color for a single curve. # Check to see if funcs is a list of functions that will be all plotted together. if isinstance(funcs, (list, tuple)) and not parametric: @@ -2208,7 +2208,7 @@ def golden_rainbow(i, lightness=0.4): options_temp = options.copy() color_temp = options_temp.pop('rgbcolor', 'automatic') fill_temp = options_temp.pop('fill', fill) - fillcolor_temp = options_temp.pop('fillcolor', 'automatic') # perhaps the 2nd argument should be ``options_temp['color']`` + fillcolor_temp = options_temp.pop('fillcolor', 'automatic') # perhaps the 2nd argument should be ``options_temp['color']`` linestyle_temp = options_temp.pop('linestyle', None) legend_label_temp = options_temp.pop('legend_label', None) legend_color_temp = options_temp.pop('legend_color', None) @@ -2261,21 +2261,22 @@ def golden_rainbow(i, lightness=0.4): fill_entry = fill_temp # passed more than one fillcolor directive? - fillcolor_entry = 'gray' # the default choice + fillcolor_entry = 'gray' # the default choice if isinstance(fillcolor_temp, dict): if i in fillcolor_temp: fillcolor_entry = fillcolor_temp[i] else: - fillcolor_entry = golden_rainbow(i,0.85) + fillcolor_entry = golden_rainbow(i, 0.85) elif isinstance(fillcolor_temp, (list, tuple)): if i < len(fillcolor_temp): fillcolor_entry = fillcolor_temp[i] else: - fillcolor_entry = golden_rainbow(i,0.85) + fillcolor_entry = golden_rainbow(i, 0.85) elif fillcolor_temp == 'automatic': - # check that we haven't overwritten automatic multi-colors in color_temp + # check that we haven't overwritten automatic + # multi-colors in color_temp if len(funcs) > 1 and not one_plot_color: - fillcolor_entry = golden_rainbow(i,0.85) + fillcolor_entry = golden_rainbow(i, 0.85) elif fillcolor_temp is not None: fillcolor_entry = fillcolor_temp @@ -2298,7 +2299,7 @@ def golden_rainbow(i, lightness=0.4): linestyle_entry = linestyle_temp # passed more than one legend_label directive? - legend_label_entry = orig_funcs[i].__repr__() # the 'automatic' choice + legend_label_entry = orig_funcs[i].__repr__() # the 'automatic' choice if isinstance(legend_label_temp, dict): if i in legend_label_temp: legend_label_entry = legend_label_temp[i] @@ -2357,7 +2358,7 @@ def golden_rainbow(i, lightness=0.4): # We make sure that points plot points close to the excluded points are computed epsilon = 0.001*(xmax - xmin) - initial_points = reduce(lambda a,b: a+b, + initial_points = reduce(lambda a, b: a+b, [[x - epsilon, x + epsilon] for x in excluded_points], []) else: @@ -2369,7 +2370,10 @@ def golden_rainbow(i, lightness=0.4): not parametric and options['scale'] in ['loglog', 'semilogx']) if is_log_scale: - f_exp = lambda x: f(exp(x)) + + def f_exp(x): + return f(exp(x)) + log_xrange = (log(xrange[0]), log(xrange[1])) if initial_points is None: log_initial_points = None @@ -2391,20 +2395,21 @@ def golden_rainbow(i, lightness=0.4): # If we did a change in variables, undo it now if is_log_scale: - for i,(a,fa) in enumerate(data): + for i, (a, fa) in enumerate(data): data[i] = (exp(a), fa) - for i,p in enumerate(excluded_points): + for i, p in enumerate(excluded_points): excluded_points[i] = exp(p) if parametric: - # We need the original x-values to be able to exclude points in parametric plots + # We need the original x-values to be able to exclude points + # in parametric plots exclude_data = data newdata = [] - for x,fdata in data: + for x, fdata in data: try: newdata.append((fdata, g(x))) except (ValueError, TypeError): - newdata.append((fdata, 0)) # append a dummy value 0 + newdata.append((fdata, 0)) # append a dummy value 0 excluded_points.append(x) data = newdata @@ -2549,7 +2554,7 @@ def golden_rainbow(i, lightness=0.4): return G -########## misc functions ################### +# ######### misc functions ################# @options(aspect_ratio=1.0) def parametric_plot(funcs, *args, **kwargs): @@ -3134,8 +3139,8 @@ def list_plot(data, plotjoined=False, **kwargs): pass if not isinstance(plotjoined, bool): raise TypeError("The second argument 'plotjoined' should be boolean " - "(True or False). If you meant to plot two lists 'x' " - "and 'y' against each other, use 'list_plot(list(zip(x,y)))'.") + "(True or False). If you meant to plot two lists 'x' " + "and 'y' against each other, use 'list_plot(list(zip(x,y)))'.") if isinstance(data, dict): if plotjoined: list_data = sorted(data.items()) @@ -3148,8 +3153,10 @@ def list_plot(data, plotjoined=False, **kwargs): RDF(data[0]) data = list(enumerate(data)) list_enumerated = True - except TypeError: # we can get this TypeError if the element is a list - # or tuple or numpy array, or an element of CC, CDF + except TypeError: + # we can get this TypeError if the element is a list + # or tuple or numpy array, or an element of CC, CDF + # We also want to avoid doing CC(data[0]) here since it will go # through if data[0] is really a tuple and every element of the # data will be converted to a complex and later converted back to @@ -3181,7 +3188,7 @@ def list_plot(data, plotjoined=False, **kwargs): else: return point(data, **kwargs) -#------------------------ Graphs on log scale ---------------------------# +# ------------------------ Graphs on log scale --------------------------- @options(base=10) @@ -3829,11 +3836,11 @@ def multi_graphics(graphics_list): return MultiGraphics(graphics_list) -def minmax_data(xdata, ydata, dict=False): +def minmax_data(xdata, ydata, dict=False) -> tuple | dict: """ Return the minimums and maximums of ``xdata`` and ``ydata``. - If dict is False, then minmax_data returns the tuple (xmin, xmax, + If dict is ``False``, then minmax_data returns the tuple (xmin, xmax, ymin, ymax); otherwise, it returns a dictionary whose keys are 'xmin', 'xmax', 'ymin', and 'ymax' and whose values are the corresponding values. @@ -3861,8 +3868,8 @@ def minmax_data(xdata, ydata, dict=False): if dict: return {'xmin': xmin, 'xmax': xmax, 'ymin': ymin, 'ymax': ymax} - else: - return xmin, xmax, ymin, ymax + + return xmin, xmax, ymin, ymax def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, @@ -3946,7 +3953,7 @@ def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, try: y = float(f(x)) if str(y) in ['nan', 'NaN', 'inf', '-inf']: - sage.misc.verbose.verbose(f"{msg}\nUnable to compute f({x})",1) + sage.misc.verbose.verbose(f"{msg}\nUnable to compute f({x})", 1) # give up for this branch if excluded: return [(x, 'NaN')] @@ -3961,19 +3968,20 @@ def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, # this distance calculation is not perfect. if abs((p1[1] + p2[1])/2.0 - y) > adaptive_tolerance: - return adaptive_refinement(f, p1, (x, y), - adaptive_tolerance=adaptive_tolerance, - adaptive_recursion=adaptive_recursion, - level=level+1, - excluded=excluded) \ - + [(x, y)] + \ - adaptive_refinement(f, (x, y), p2, - adaptive_tolerance=adaptive_tolerance, - adaptive_recursion=adaptive_recursion, - level=level+1, - excluded=excluded) - else: - return [] + ref = adaptive_refinement(f, p1, (x, y), + adaptive_tolerance=adaptive_tolerance, + adaptive_recursion=adaptive_recursion, + level=level+1, + excluded=excluded) + ref += [(x, y)] + ref += adaptive_refinement(f, (x, y), p2, + adaptive_tolerance=adaptive_tolerance, + adaptive_recursion=adaptive_recursion, + level=level+1, + excluded=excluded) + return ref + + return [] def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, @@ -4109,7 +4117,7 @@ def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, except (ArithmeticError, TypeError, ValueError) as m: sage.misc.verbose.verbose(f"{m}\nUnable to compute f({xi})", 1) - if i == 0: # Given an error for left endpoint, try to move it in slightly + if i == 0: # Given an error for left endpoint, try to move it in slightly for j in range(1, 99): xj = xi + delta*j/100.0 try: @@ -4125,7 +4133,7 @@ def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, exceptions += 1 exception_indices.append(i) - elif i == plot_points-1: # Given an error for right endpoint, try to move it in slightly + elif i == plot_points-1: # Given an error for right endpoint, try to move it in slightly for j in range(1, 99): xj = xi - delta*j/100.0 try: diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index da9737adf38..c57ef57cc38 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -942,28 +942,27 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=None, algori new_coeffs = [self.inclusion(c[0].degree(), l)(c[1]) for c in coeffs] - polys = [(g,m,l,phi) for g,m in P(new_coeffs).factor()] + polys = [(g, m, l, phi) for g, m in P(new_coeffs).factor()] roots = [] # a list of pair (root,multiplicity) while polys: - g,m,l,phi = polys.pop() + g, m, l, phi = polys.pop() - if g.degree() == 1: # found a root + if g.degree() == 1: # found a root r = phi(-g.constant_coefficient()) - roots.append((r,m)) - else: # look at the extension of degree g.degree() which contains at - # least one root of g + roots.append((r, m)) + else: + # look at the extension of degree g.degree() which + # contains at least one root of g ll = l * g.degree() psi = self.inclusion(l, ll) FF, pphi = self.subfield(ll) - # note: there is no coercion from the l-th subfield to the ll-th - # subfield. The line below does the conversion manually. + # note: there is no coercion from the l-th subfield to + # the ll-th subfield. The line below does the + # conversion manually. g = PolynomialRing(FF, 'x')([psi(_) for _ in g]) - polys.extend((gg,m,ll,pphi) for gg,_ in g.factor()) + polys.extend((gg, m, ll, pphi) for gg, _ in g.factor()) - if multiplicities: - return roots - else: - return [r[0] for r in roots] + return roots if multiplicities else [r[0] for r in roots] def _factor_univariate_polynomial(self, p, **kwds): r""" diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index f5a5ce09a76..d3c927c8119 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -636,13 +636,16 @@ def __init__(self, p, label): """ self._p = p self._label = label - self._elements = [ ] - self._matrix = { } # A dictionary whose keys are weak references to tracked elements - # and values corresponding columns in the matrix - # representing the precision lattice - self._collected_references = [ ] - self._marked_for_deletion = [ ] - self._approx_zero = pRational(p, ZZ(0)) + self._elements = [] + + self._matrix = {} + # A dictionary whose keys are weak references to tracked + # elements and values corresponding columns in the matrix + # representing the precision lattice + + self._collected_references = [] + self._marked_for_deletion = [] + self._approx_zero = pRational(p, ZZ.zero()) self._threshold_deletion = DEFAULT_THRESHOLD_DELETION self._history_init = None self._history = None diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 3b135e39975..eff41741a4b 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -976,13 +976,14 @@ def _check_satisfies_equations(self, v): """ coords = list(v) for f in self.defining_polynomials(): - if f(coords) != 0: # it must be "!=0" instead of "if f(v)", e.g., - # because of p-adic base rings. - raise TypeError("Coordinates %s do not define a point on %s" % (coords,self)) + if f(coords) != 0: + # it must be "!=0" instead of "if f(v)", e.g., + # because of p-adic base rings. + raise TypeError("Coordinates %s do not define a point on %s" % (coords, self)) try: return self.ambient_space()._check_satisfies_equations(coords) except TypeError: - raise TypeError("Coordinates %s do not define a point on %s" % (coords,self)) + raise TypeError("Coordinates %s do not define a point on %s" % (coords, self)) def base_extend(self, R): """ From e3c870560dc0eca8596250db90f60970d477aff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 10:34:44 +0100 Subject: [PATCH 470/507] remove many unused imports --- src/sage/algebras/jordan_algebra.py | 1 - .../algebras/lie_algebras/verma_module.py | 2 -- src/sage/categories/kahler_algebras.py | 6 ++--- src/sage/coding/guruswami_sudan/utils.py | 8 +++---- src/sage/combinat/combinat.py | 3 --- .../root_system/reflection_group_complex.py | 23 +++++++++---------- src/sage/combinat/specht_module.py | 1 - .../crypto/key_exchange/diffie_hellman.py | 13 ++++------- src/sage/crypto/util.py | 8 +++---- src/sage/env.py | 1 - src/sage/features/__init__.py | 2 +- src/sage/features/fricas.py | 1 - src/sage/features/latex.py | 2 +- src/sage/features/meson_editable.py | 5 ++-- src/sage/features/pkg_systems.py | 2 +- src/sage/functions/transcendental.py | 1 - src/sage/geometry/cone_critical_angles.py | 3 +-- src/sage/geometry/lattice_polytope.py | 18 +++++++-------- .../geometry/polyhedron/backend_polymake.py | 1 - src/sage/geometry/polyhedron/base.py | 1 - .../triangulation/point_configuration.py | 16 +++++-------- src/sage/graphs/graph_database.py | 3 +-- src/sage/graphs/orientations.py | 2 -- src/sage/groups/class_function.py | 1 - src/sage/homology/chain_complex.py | 2 -- src/sage/interfaces/jmoldata.py | 1 - src/sage/manifolds/chart_func.py | 1 - src/sage/matrix/operation_table.py | 2 -- src/sage/misc/compat.py | 1 - src/sage/misc/cython.py | 5 ++-- src/sage/misc/misc.py | 7 +++--- src/sage/modules/with_basis/representation.py | 1 - src/sage/repl/rich_output/backend_ipython.py | 1 - src/sage/rings/number_field/morphism.py | 7 +++--- src/sage/rings/number_field/order_ideal.py | 4 +--- src/sage/schemes/affine/affine_morphism.py | 4 +--- src/sage/schemes/affine/affine_point.py | 1 - src/sage/symbolic/expression_conversions.py | 2 -- src/sage/symbolic/function_factory.py | 1 - .../tensor/modules/free_module_morphism.py | 1 - src/sage/topology/cubical_complex.py | 5 ++-- src/sage/topology/simplicial_complex.py | 1 - 42 files changed, 57 insertions(+), 114 deletions(-) diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index f5881fbba88..18316b05085 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -1298,7 +1298,6 @@ def basis(self): sage: len(B) 27 """ - import itertools R = self.base_ring() OB = self._O.basis() base = [R.zero()] * 3 + [self._O.zero()] * 3 diff --git a/src/sage/algebras/lie_algebras/verma_module.py b/src/sage/algebras/lie_algebras/verma_module.py index 09962f11b6d..0e4cdff3732 100644 --- a/src/sage/algebras/lie_algebras/verma_module.py +++ b/src/sage/algebras/lie_algebras/verma_module.py @@ -28,11 +28,9 @@ from sage.categories.homset import Hom, Homset from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid from sage.combinat.free_module import CombinatorialFreeModule -from sage.modules.free_module_element import vector from sage.sets.family import Family from sage.structure.richcmp import richcmp from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ class ModulePrinting: diff --git a/src/sage/categories/kahler_algebras.py b/src/sage/categories/kahler_algebras.py index d8791ae78fb..4a065d64f2a 100644 --- a/src/sage/categories/kahler_algebras.py +++ b/src/sage/categories/kahler_algebras.py @@ -17,16 +17,14 @@ from sage.categories.category_types import Category_over_base_ring from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis -from sage.categories.finite_dimensional_algebras_with_basis import FiniteDimensionalAlgebrasWithBasis -from sage.categories.filtered_modules_with_basis import FilteredModulesWithBasis from sage.misc.abstract_method import abstract_method from sage.quadratic_forms.quadratic_form import QuadraticForm -from sage.misc.cachefunc import cached_method class KahlerAlgebras(Category_over_base_ring): r""" The category of graded algebras satisfying the Kähler package. + A finite-dimensional graded algebra `\bigoplus_{k=1}^{r}A^k` satisfies the *Kähler package* if the following properties hold: @@ -197,4 +195,4 @@ def hodge_riemann_relations(self, k): for i,el in enumerate(basis_k): for j in range(i, len(basis_k)): coeff.append((el * (lefschetz_el ** (r-(2*k)) * basis_k[j])).degree()) - return QuadraticForm(self.base_ring(), len(basis_k), coeff) \ No newline at end of file + return QuadraticForm(self.base_ring(), len(basis_k), coeff) diff --git a/src/sage/coding/guruswami_sudan/utils.py b/src/sage/coding/guruswami_sudan/utils.py index 31bfdf85e64..320c07c6c52 100644 --- a/src/sage/coding/guruswami_sudan/utils.py +++ b/src/sage/coding/guruswami_sudan/utils.py @@ -8,7 +8,7 @@ - David Lucas, ported the original implementation in Sage """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2015 David Lucas # 2015 Johan S. R. Nielsen # @@ -16,12 +16,10 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.arith.misc import integer_floor as floor -from sage.misc.lazy_import import lazy_import from sage.misc.functional import sqrt from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index e0217176636..50149b3473b 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -170,15 +170,12 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer -from sage.rings.infinity import infinity from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.misc.misc_c import prod from sage.misc.cachefunc import cached_function from sage.structure.sage_object import SageObject -from sage.structure.parent import Parent from sage.misc.lazy_import import lazy_import -from sage.misc.lazy_attribute import lazy_attribute from .combinat_cython import _stirling_number2 from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.element import Element diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 1d2641d0c65..c213a2dcf6c 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -196,26 +196,25 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.cachefunc import cached_method, cached_function -from sage.misc.misc_c import prod from sage.categories.category import Category -from sage.categories.permutation_groups import PermutationGroups from sage.categories.complex_reflection_groups import ComplexReflectionGroups from sage.categories.coxeter_groups import CoxeterGroups +from sage.categories.permutation_groups import PermutationGroups +from sage.combinat.root_system.cartan_matrix import CartanMatrix from sage.combinat.root_system.reflection_group_element import ComplexReflectionGroupElement, _gap_return -from sage.sets.family import Family -from sage.structure.unique_representation import UniqueRepresentation from sage.groups.perm_gps.permgroup import PermutationGroup_generic -from sage.combinat.permutation import Permutation -from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ +from sage.interfaces.gap3 import gap3 from sage.matrix.constructor import matrix from sage.matrix.special import identity_matrix -from sage.structure.element import Matrix -from sage.interfaces.gap3 import gap3 -from sage.modules.free_module_element import vector -from sage.combinat.root_system.cartan_matrix import CartanMatrix +from sage.misc.cachefunc import cached_method, cached_function +from sage.misc.misc_c import prod from sage.misc.sage_eval import sage_eval +from sage.modules.free_module_element import vector +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.sets.family import Family +from sage.structure.element import Matrix +from sage.structure.unique_representation import UniqueRepresentation class ComplexReflectionGroup(UniqueRepresentation, PermutationGroup_generic): diff --git a/src/sage/combinat/specht_module.py b/src/sage/combinat/specht_module.py index 1f7f15c90d6..8d442af53be 100644 --- a/src/sage/combinat/specht_module.py +++ b/src/sage/combinat/specht_module.py @@ -34,7 +34,6 @@ from sage.matrix.constructor import matrix from sage.rings.rational_field import QQ from sage.modules.with_basis.subquotient import SubmoduleWithBasis, QuotientModuleWithBasis -from sage.modules.free_module_element import vector from sage.categories.modules_with_basis import ModulesWithBasis diff --git a/src/sage/crypto/key_exchange/diffie_hellman.py b/src/sage/crypto/key_exchange/diffie_hellman.py index 8dee1010a19..43c4f544db7 100644 --- a/src/sage/crypto/key_exchange/diffie_hellman.py +++ b/src/sage/crypto/key_exchange/diffie_hellman.py @@ -17,22 +17,17 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - -from sage.misc.superseded import experimental +from typing import Union from sage.crypto.key_exchange.key_exchange_scheme import KeyExchangeScheme - -from sage.arith.misc import is_prime from sage.misc.prandom import randint -from sage.rings.integer import Integer +from sage.misc.superseded import experimental from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.finite_rings.finite_field_prime_modn import \ - FiniteField_prime_modn +from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn from sage.rings.finite_rings.integer_mod import IntegerMod_abstract +from sage.rings.integer import Integer from sage.structure.proof.proof import WithProof -from typing import Union - class DiffieHellman(KeyExchangeScheme): diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index 66ffb78ef2b..819daade08d 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -11,21 +11,19 @@ ``is_blum_prime``, ``least_significant_bits``, ``random_blum_prime``. """ -#***************************************************************************** +# *************************************************************************** # Copyright (c) 2009, 2010 Minh Van Nguyen # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** -from sage.arith.functions import lcm from sage.arith.misc import is_prime, primes, random_prime from sage.misc.lazy_import import lazy_import from sage.rings.finite_rings.integer_mod import Mod as mod -from sage.rings.integer import Integer lazy_import('sage.arith.misc', ('carmichael_lambda'), deprecation=34719) lazy_import('sage.monoids.string_monoid', 'BinaryStrings') diff --git a/src/sage/env.py b/src/sage/env.py index 826c0343ac8..310f24f3301 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -47,7 +47,6 @@ import sys import sysconfig from . import version -from pathlib import Path import subprocess diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index 8d3f67efae7..cf4959b5b1a 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -565,7 +565,7 @@ def package_systems(): [Feature('homebrew'), Feature('sage_spkg'), Feature('pip')] """ # The current implementation never returns more than one system. - from subprocess import run, CalledProcessError, PIPE + from subprocess import run, CalledProcessError global _cache_package_systems if _cache_package_systems is None: from .pkg_systems import PackageSystem, SagePackageSystem, PipPackageSystem diff --git a/src/sage/features/fricas.py b/src/sage/features/fricas.py index 15c346248ae..8c3c32c0edd 100644 --- a/src/sage/features/fricas.py +++ b/src/sage/features/fricas.py @@ -12,7 +12,6 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** -import os import subprocess from . import Executable, FeatureTestResult diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index 46173f7484b..e2114df9b7c 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -242,7 +242,7 @@ def absolute_filename(self) -> str: sage: feature.absolute_filename() # optional - latex '.../latex/base/article.cls' """ - from subprocess import run, CalledProcessError, PIPE + from subprocess import run, CalledProcessError try: proc = run(['kpsewhich', self.filename], capture_output=True, text=True, check=True) diff --git a/src/sage/features/meson_editable.py b/src/sage/features/meson_editable.py index a110648b873..2fdd59ad420 100644 --- a/src/sage/features/meson_editable.py +++ b/src/sage/features/meson_editable.py @@ -1,14 +1,13 @@ r""" Feature for testing if Meson editable install is used. """ - -import sys from . import Feature, FeatureTestResult class MesonEditable(Feature): r""" - A :class:`~sage.features.Feature` describing if Meson editable install is used. + A :class:`~sage.features.Feature` describing if Meson editable install + is used. EXAMPLES:: diff --git a/src/sage/features/pkg_systems.py b/src/sage/features/pkg_systems.py index 4f6db21b735..869bdf3e1e0 100644 --- a/src/sage/features/pkg_systems.py +++ b/src/sage/features/pkg_systems.py @@ -69,7 +69,7 @@ def _spkg_installation_hint(self, spkgs, prompt, feature): sage: fedora.spkg_installation_hint('openblas') # optional - SAGE_ROOT 'To install openblas using the fedora package manager, you can try to run:\n!sudo yum install openblas-devel' """ - from subprocess import run, CalledProcessError, PIPE + from subprocess import run, CalledProcessError lines = [] system = self.name try: diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index 55e24a44cbd..b655282486c 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -16,7 +16,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import math -import sys from sage.misc.lazy_import import lazy_import from sage.misc.misc import increase_recursion_limit diff --git a/src/sage/geometry/cone_critical_angles.py b/src/sage/geometry/cone_critical_angles.py index 964280e6205..11ed0c3c4ab 100644 --- a/src/sage/geometry/cone_critical_angles.py +++ b/src/sage/geometry/cone_critical_angles.py @@ -31,9 +31,8 @@ methods have been prefixed with an underscore. """ -from sage.functions.trig import arccos, cos +from sage.functions.trig import arccos from sage.matrix.constructor import matrix -from sage.misc.misc import powerset from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA from sage.rings.rational_field import QQ diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index da030a17966..dfba9f78a3b 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -120,6 +120,14 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** +from collections.abc import Hashable +from copyreg import constructor as copyreg_constructor +import os +import shlex +from subprocess import Popen, PIPE +from warnings import warn +from functools import reduce +from io import IOBase, StringIO from sage.misc.lazy_import import lazy_import lazy_import('sage.combinat.posets.posets', 'FinitePoset') @@ -159,16 +167,6 @@ from sage.geometry.convex_set import ConvexSet_compact import sage.geometry.abc -from copy import copy -from collections.abc import Hashable -from copyreg import constructor as copyreg_constructor -import os -import shlex -from subprocess import Popen, PIPE -from warnings import warn -from functools import reduce -from io import IOBase, StringIO - class SetOfAllLatticePolytopesClass(Set_generic): def _repr_(self): diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 84fdd48177b..b42e53398c6 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -553,7 +553,6 @@ def _from_polymake_polytope(cls, parent, polymake_polytope): if parent is None: from .parent import Polyhedra from sage.rings.rational_field import QQ - from sage.rings.qqbar import AA if polymake_polytope.typeof()[0] == 'Polymake::polytope::Polytope__Rational': base_ring = QQ else: diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7110f74a202..f1cd9a9461e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -32,7 +32,6 @@ from sage.misc.cachefunc import cached_method -import sage.rings.abc from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.matrix.constructor import matrix diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 312da9db9a0..7c4b9afbe31 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -179,22 +179,18 @@ ######################################################################## import itertools +from copy import copy +import sys +import pexpect from sage.features import FeatureNotPresentError from sage.features.topcom import TOPCOMExecutable -from sage.structure.unique_representation import UniqueRepresentation +from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method -from sage.misc.lazy_import import lazy_import - +from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.matrix.constructor import matrix -from sage.modules.free_module_element import vector - -from copy import copy -import sys -import pexpect - +from sage.structure.unique_representation import UniqueRepresentation from sage.geometry.triangulation.base import \ PointConfiguration_base, Point, ConnectedTriangulationsIterator diff --git a/src/sage/graphs/graph_database.py b/src/sage/graphs/graph_database.py index b0fa83f55d4..f5a4bc228d3 100644 --- a/src/sage/graphs/graph_database.py +++ b/src/sage/graphs/graph_database.py @@ -43,10 +43,9 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # ############################################################################## +import re from . import graph -import os -import re from sage.rings.integer import Integer from sage.databases.sql_db import SQLDatabase, SQLQuery from sage.features.databases import DatabaseGraphs diff --git a/src/sage/graphs/orientations.py b/src/sage/graphs/orientations.py index 842d3e5c4f7..30b36954ac5 100644 --- a/src/sage/graphs/orientations.py +++ b/src/sage/graphs/orientations.py @@ -557,13 +557,11 @@ def acyclic_orientations(G): # A graph without edge cannot be oriented return - from sage.rings.infinity import Infinity from sage.combinat.subset import Subsets def reorder_vertices(G): n = G.order() ko = n - k = n G_copy = G.copy() vertex_labels = {v: None for v in G_copy.vertices()} diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index 1e69c3d6080..ec63b04def0 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -67,7 +67,6 @@ class function on the conjugacy classes, in that order sage: chi = ClassFunction(G, values); chi Character of Cyclic group of order 4 as a permutation group """ - from sage.misc.superseded import deprecation try: return group.class_function(values) except AttributeError: diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 2f050801f25..8be7c9864be 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -54,13 +54,11 @@ from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ from sage.modules.free_module import FreeModule from sage.modules.free_module_element import vector from sage.matrix.matrix0 import Matrix from sage.matrix.constructor import matrix from sage.misc.latex import latex -from sage.misc.superseded import deprecation from sage.rings.fast_arith import prime_range from sage.homology.homology_group import HomologyGroup from sage.misc.persist import register_unpickle_override diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index f8f497172b9..0733a046df4 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -28,7 +28,6 @@ import os import re import subprocess -import sys from pathlib import Path diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index 8e9ac906059..09a25cd0ba8 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -36,7 +36,6 @@ from sage.categories.commutative_algebras import CommutativeAlgebras from sage.manifolds.utilities import ExpressionNice from sage.misc.cachefunc import cached_method -from sage.misc.lazy_import import lazy_import from sage.structure.element import AlgebraElement, ModuleElementWithMutability from sage.structure.mutability import Mutability from sage.structure.parent import Parent diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 58c53cfa1c7..78e7f08e61a 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -14,8 +14,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from copy import copy - from sage.structure.sage_object import SageObject from sage.matrix.constructor import Matrix diff --git a/src/sage/misc/compat.py b/src/sage/misc/compat.py index 5f27aea06a0..6f441f40ee5 100644 --- a/src/sage/misc/compat.py +++ b/src/sage/misc/compat.py @@ -11,7 +11,6 @@ # ***************************************************************************** import os -import subprocess import sys from sage.env import SAGE_LOCAL diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index c85be325c3d..9111f25c72b 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -29,11 +29,10 @@ from sage.env import (SAGE_LOCAL, cython_aliases, sage_include_directories) +from sage.misc.cachefunc import cached_function +from sage.misc.sage_ostools import restore_cwd, redirection from sage.misc.temporary_file import spyx_tmp, tmp_filename from sage.repl.user_globals import get_globals -from sage.misc.sage_ostools import restore_cwd, redirection -from sage.cpython.string import str_to_bytes -from sage.misc.cachefunc import cached_function @cached_function diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 8005a14ee5a..2eec7d9e3e0 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -46,14 +46,15 @@ import sys import warnings -from sage.misc.lazy_string import lazy_string from sage.env import DOT_SAGE, HOSTNAME from sage.misc.lazy_import import lazy_import -lazy_import("sage.combinat.subset", ["powerset", "subsets", "uniq"], deprecation=35564) +lazy_import("sage.combinat.subset", ["powerset", "subsets", "uniq"], + deprecation=35564) lazy_import( - "sage.misc.timing", ["cputime", "GlobalCputime", "walltime"], deprecation=35816 + "sage.misc.timing", ["cputime", "GlobalCputime", "walltime"], + deprecation=35816 ) LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 61e65409e21..457ed5dd92a 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -2878,7 +2878,6 @@ def __init__(self, V, shape): from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.categories.tensor import tensor - from sage.matrix.matrix_space import MatrixSpace R = V.base_ring() self._shape = shape diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 5b013574507..ee7dd4b848d 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -16,7 +16,6 @@ # **************************************************************************** import os -import sys import html from IPython.display import publish_display_data from sage.repl.rich_output.backend_base import BackendBase diff --git a/src/sage/rings/number_field/morphism.py b/src/sage/rings/number_field/morphism.py index 579c925ed01..6b8f70c8dff 100644 --- a/src/sage/rings/number_field/morphism.py +++ b/src/sage/rings/number_field/morphism.py @@ -5,18 +5,17 @@ fields (i.e. field embeddings). """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2007 William Stein # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.misc.cachefunc import cached_method -from sage.misc.lazy_import import lazy_import from sage.rings.morphism import RingHomomorphism_im_gens, RingHomomorphism from sage.structure.sequence import Sequence diff --git a/src/sage/rings/number_field/order_ideal.py b/src/sage/rings/number_field/order_ideal.py index 3e0ecce3653..93ad7997896 100644 --- a/src/sage/rings/number_field/order_ideal.py +++ b/src/sage/rings/number_field/order_ideal.py @@ -62,15 +62,13 @@ from sage.structure.sequence import Sequence from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.arith.misc import gcd -from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector from sage.rings.polynomial.polynomial_ring import polygens from sage.rings.ideal import Ideal_generic import sage.rings.number_field.order -#TODO I*u works when u lies in I.ring().number_field(), but u*I doesn't +# TODO I*u works when u lies in I.ring().number_field(), but u*I doesn't def NumberFieldOrderIdeal(O, *args, **kwds): diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index baa58aeb639..ee0bf419c2c 100644 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -42,13 +42,11 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** import sys -import sage.rings.abc - from sage.categories.homset import Hom, End from sage.categories.fields import Fields diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py index eb13ba7c2cd..c5fa388a5ac 100644 --- a/src/sage/schemes/affine/affine_point.py +++ b/src/sage/schemes/affine/affine_point.py @@ -22,7 +22,6 @@ # **************************************************************************** from sage.categories.number_fields import NumberFields -from sage.misc.lazy_import import lazy_import from sage.rings.integer_ring import ZZ from sage.schemes.generic.morphism import SchemeMorphism_point, SchemeMorphism from sage.structure.sequence import Sequence diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 0a2b25d4a8a..ad8e8be604c 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -18,8 +18,6 @@ from operator import eq, ne, gt, lt, ge, le, mul, pow, neg, add, truediv from functools import reduce -import sage.rings.abc - from sage.misc.lazy_import import lazy_import from sage.symbolic.ring import SR from sage.structure.element import Expression diff --git a/src/sage/symbolic/function_factory.py b/src/sage/symbolic/function_factory.py index 1918e166443..070b221c6ce 100644 --- a/src/sage/symbolic/function_factory.py +++ b/src/sage/symbolic/function_factory.py @@ -10,7 +10,6 @@ # https://www.gnu.org/licenses/ ############################################################################### from __future__ import annotations -from typing import Union from sage.symbolic.function import (SymbolicFunction, sfunctions_funcs, unpickle_wrapper) diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py index cb119b3eecb..47811d7ca18 100644 --- a/src/sage/tensor/modules/free_module_morphism.py +++ b/src/sage/tensor/modules/free_module_morphism.py @@ -1358,7 +1358,6 @@ def __init__(self, parent, matrix_rep, bases=None, name=None, sage: latex(phi) \mathrm{Id} """ - from sage.matrix.special import identity_matrix from sage.misc.constant_function import ConstantFunction fmodule = parent.domain() diff --git a/src/sage/topology/cubical_complex.py b/src/sage/topology/cubical_complex.py index 5d95c8f7522..2832bedc84e 100644 --- a/src/sage/topology/cubical_complex.py +++ b/src/sage/topology/cubical_complex.py @@ -67,8 +67,9 @@ see the :mod:`Generic Cell Complex ` page instead. """ - from copy import copy +from functools import total_ordering + from .cell_complex import GenericCellComplex from sage.structure.sage_object import SageObject from sage.rings.integer import Integer @@ -77,8 +78,6 @@ from sage.rings.rational_field import QQ from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import -from sage.misc.superseded import deprecation -from functools import total_ordering lazy_import('sage.matrix.constructor', 'matrix') diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 3204373e271..b6b58d72e98 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -167,7 +167,6 @@ from sage.misc.cachefunc import cached_method from sage.misc.latex import latex from sage.misc.lazy_import import lazy_import -from sage.misc.superseded import deprecation from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring import polygens From 5cc6577fd7da81be9f2ce460b14c18df1faeb357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 12:43:01 +0100 Subject: [PATCH 471/507] some fixes for E272 in pyx files --- src/sage/coding/codecan/codecan.pyx | 3 ++- src/sage/combinat/designs/designs_pyx.pyx | 4 ++-- .../orthogonal_arrays_find_recursive.pyx | 24 +++++++++---------- .../combinatorial_polyhedron/base.pyx | 18 +++++++------- .../slice_decomposition.pyx | 7 +++--- src/sage/graphs/strongly_regular_db.pyx | 8 +++---- .../automorphism_group_canonical_label.pyx | 2 +- .../perm_gps/partn_ref/data_structures.pyx | 14 +++++------ .../perm_gps/partn_ref/double_coset.pyx | 20 ++++++++-------- .../perm_gps/partn_ref/refinement_sets.pyx | 4 ++-- src/sage/libs/mpmath/ext_main.pyx | 8 +++---- src/sage/matrix/matrix_cyclo_dense.pyx | 4 ++-- src/sage/modules/vector_rational_dense.pyx | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 8 +++---- src/sage/numerical/mip.pyx | 4 ++-- src/sage/stats/hmm/chmm.pyx | 4 ++-- src/sage/stats/hmm/util.pyx | 7 +++--- 17 files changed, 72 insertions(+), 69 deletions(-) diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index ceb197f7890..07d2e861f93 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -636,7 +636,8 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): else: P = remaining_inner_group.column_blocks(self._best_candidate) for p in P: - x = S(v=[ F.primitive_element() if i in p else F.one() for i in range(self._n) ]) + x = S(v=[F.primitive_element() if i in p else F.one() + for i in range(self._n)]) self._autom_group_generators.append(transp_inv * x * self._transporter) self._inner_group_stabilizer_order = (len(F) - 1) ** len(P) diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index 571d7e27c35..0afb3465fdc 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -948,12 +948,12 @@ cpdef _OA_cache_set(int k, int n, truth_value): _OA_cache_size = new_cache_size if truth_value is True: - _OA_cache[n].max_true = k if k>_OA_cache[n].max_true else _OA_cache[n].max_true + _OA_cache[n].max_true = k if k>_OA_cache[n].max_true else _OA_cache[n].max_true elif truth_value is Unknown: _OA_cache[n].min_unknown = k if k<_OA_cache[n].min_unknown else _OA_cache[n].min_unknown _OA_cache[n].max_unknown = k if k>_OA_cache[n].max_unknown else _OA_cache[n].max_unknown else: - _OA_cache[n].min_false = k if k<_OA_cache[n].min_false else _OA_cache[n].min_false + _OA_cache[n].min_false = k if k<_OA_cache[n].min_false else _OA_cache[n].min_false cpdef _OA_cache_get(int k, int n): r""" diff --git a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx index dc9c278cd72..defa8bd4258 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +++ b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx @@ -391,10 +391,10 @@ cpdef find_construction_3_5(int k, int n): for s in range(min(i+1,nn)): for r in range(max(0,i-nn-s), min(s+1,i-s+1,nn)): t = i - r - s - if ((nn-r-1)*(nn-s) < t and - (r==0 or is_available(k,r)) and - (s==0 or is_available(k,s)) and - (t==0 or is_available(k,t))): + if ((nn-r-1)*(nn-s) < t and + (r==0 or is_available(k, r)) and + (s==0 or is_available(k, s)) and + (t==0 or is_available(k, t))): from sage.combinat.designs.orthogonal_arrays_build_recursive import construction_3_5 return construction_3_5, (k,nn,mm,r,s,t) @@ -481,15 +481,15 @@ cpdef find_q_x(int k, int n): x = (n-q**2+q-2)/(2-q) if (x < q and 0 < x and - n == (q-1)*(q-x)+x+2 and - is_available(k+1,q-x-1) and - is_available(k+1,q-x+1) and + n == (q-1)*(q-x)+x+2 and + is_available(k+1, q-x-1) and + is_available(k+1, q-x+1) and # The next is always True, because q is a prime power # is_available(k+1,q) and - is_available(k, x+2 ) and + is_available(k, x+2 ) and smallInteger(q).is_prime_power()): from sage.combinat.designs.orthogonal_arrays_build_recursive import construction_q_x - return construction_q_x, (k,q,x) + return construction_q_x, (k, q, x) return False cpdef find_thwart_lemma_3_5(int k, int N): @@ -865,11 +865,11 @@ def int_as_sum(int value, list S, int k_max): vv = v-i if vv == 0: return D[v] + (i,) - if (vv > 0 and # The new integer i is too big + if (vv > 0 and # The new integer i is too big vv <= j*max_value and # The new integer i is too small - vv not in D and # We had it in D already + vv not in D and # We had it in D already vv not in new_D): # We had it in new_D already - new_D[vv] = D[v]+(i,) + new_D[vv] = D[v] + (i,) if not new_D: break D.update(new_D) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index cc150ba8e8d..957f9307498 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -86,22 +86,22 @@ import numbers from memory_allocator cimport MemoryAllocator from cysignals.memory cimport check_calloc, sig_free -from sage.graphs.graph import Graph -from sage.geometry.polyhedron.base import Polyhedron_base +from sage.graphs.graph import Graph +from sage.geometry.polyhedron.base import Polyhedron_base from sage.geometry.lattice_polytope import LatticePolytopeClass -from sage.geometry.cone import ConvexRationalPolyhedralCone -from sage.structure.element import Matrix -from sage.matrix.matrix_dense cimport Matrix_dense -from sage.misc.misc import is_iterator +from sage.geometry.cone import ConvexRationalPolyhedralCone +from sage.structure.element import Matrix +from sage.matrix.matrix_dense cimport Matrix_dense +from sage.misc.misc import is_iterator from .conversions import (incidence_matrix_to_bit_rep_of_facets, incidence_matrix_to_bit_rep_of_Vrep, facets_tuple_to_bit_rep_of_facets, facets_tuple_to_bit_rep_of_Vrep) from sage.geometry.polyhedron.combinatorial_polyhedron.conversions cimport Vrep_list_to_bit_rep -from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_method -from sage.rings.integer cimport smallInteger -from cysignals.signals cimport sig_check +from sage.rings.integer cimport smallInteger +from cysignals.signals cimport sig_check from sage.geometry.polyhedron.combinatorial_polyhedron.face_data_structure cimport face_len_atoms, face_init, face_free from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator cimport iter_t, parallel_f_vector diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 39a85485dfa..e4e586a3ab7 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -414,9 +414,10 @@ cdef class SliceDecomposition(SageObject): # Translate the results with the actual vertices of the graph self.sigma = tuple(Gbackend.vertex_label(v_int) for v_int in sigma) - self.sigma_inv = {v: i for i, v in enumerate(self.sigma)} - self.lex_label = {i: tuple(Gbackend.vertex_label(v_int) for v_int in lli) - for i, lli in enumerate(lex_label)} + self.sigma_inv = {v: i for i, v in enumerate(self.sigma)} + self.lex_label = {i: tuple(Gbackend.vertex_label(v_int) + for v_int in lli) + for i, lli in enumerate(lex_label)} def __eq__(self, other): """ diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 2f6f8339aa5..3749aa42661 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -3295,11 +3295,11 @@ def _check_database(): if _brouwer_database[params]['status'] != "impossible": raise RuntimeError("Brouwer's db does not seem to know that {} in unfeasible".format(params)) comment = _brouwer_database[params]['comments'] - if ('Krein' in comment or + if ('Krein' in comment or 'Absolute' in comment or - 'Conf' in comment or - 'mu=1' in comment or - 'μ=2' in comment): + 'Conf' in comment or + 'mu=1' in comment or + 'μ=2' in comment): continue raise RuntimeError("We detected that {} was unfeasible, but maybe we should not have".format(params)) diff --git a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx index 6aa7b262712..b799de5f6be 100644 --- a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +++ b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx @@ -308,7 +308,7 @@ cdef aut_gp_and_can_lab *allocate_agcl_output(int n) noexcept: output.relabeling = sig_malloc(n*sizeof(int)) output.generators = sig_malloc(2*n*n*sizeof(int)) output.size_of_generator_array = 2*n*n - if output.group is NULL or \ + if output.group is NULL or \ output.relabeling is NULL or \ output.generators is NULL: deallocate_agcl_output(output) diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx index cd9bcee07d3..6ce3185f883 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx @@ -617,17 +617,17 @@ cdef StabilizerChain *SC_new(int n, bint init_gens=True) noexcept: SC.OP_scratch = OP_new(n) # bitset_init without the MemoryError: cdef long limbs = (default_num_bits - 1)/(8*sizeof(unsigned long)) + 1 - SC.gen_used.size = default_num_bits - SC.gen_is_id.size = default_num_bits - SC.gen_used.limbs = limbs + SC.gen_used.size = default_num_bits + SC.gen_is_id.size = default_num_bits + SC.gen_used.limbs = limbs SC.gen_is_id.limbs = limbs - SC.gen_used.bits = sig_malloc(limbs * sizeof(mp_limb_t)) - SC.gen_is_id.bits = sig_malloc(limbs * sizeof(mp_limb_t)) + SC.gen_used.bits = sig_malloc(limbs * sizeof(mp_limb_t)) + SC.gen_is_id.bits = sig_malloc(limbs * sizeof(mp_limb_t)) # check for allocation failures - if int_array is NULL or int_ptrs is NULL or \ + if int_array is NULL or int_ptrs is NULL or \ SC.gen_used.bits is NULL or SC.gen_is_id.bits is NULL or \ - SC.OP_scratch is NULL: + SC.OP_scratch is NULL: sig_free(int_array) sig_free(int_ptrs) SC_dealloc(SC) diff --git a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx index 0ee7cf4b486..b72901775a3 100644 --- a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx +++ b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx @@ -213,24 +213,24 @@ cdef dc_work_space *allocate_dc_work_space(int n) noexcept: return NULL work_space.degree = n - int_array = sig_malloc((n*n + # for perm_stack + int_array = sig_malloc((n*n + # for perm_stack 5*n # for int_array )*sizeof(int)) work_space.group1 = SC_new(n) work_space.group2 = SC_new(n) - work_space.current_ps = PS_new(n,0) - work_space.first_ps = PS_new(n,0) + work_space.current_ps = PS_new(n, 0) + work_space.first_ps = PS_new(n, 0) work_space.bitset_array = sig_calloc((n + 2*len_of_fp_and_mcr + 1), sizeof(bitset_t)) work_space.orbits_of_subgroup = OP_new(n) work_space.perm_stack = NULL - if int_array is NULL or \ - work_space.group1 is NULL or \ - work_space.group2 is NULL or \ - work_space.current_ps is NULL or \ - work_space.first_ps is NULL or \ - work_space.bitset_array is NULL or \ - work_space.orbits_of_subgroup is NULL: + if int_array is NULL or \ + work_space.group1 is NULL or \ + work_space.group2 is NULL or \ + work_space.current_ps is NULL or \ + work_space.first_ps is NULL or \ + work_space.bitset_array is NULL or \ + work_space.orbits_of_subgroup is NULL: sig_free(int_array) deallocate_dc_work_space(work_space) return NULL diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx index 669e149ff09..2139c548364 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx @@ -643,8 +643,8 @@ cdef int allocate_subset_gen_2(int degree, int max_size, iterator *it) noexcept: cgd.iterator_stack[i].data = allocate_sgd(degree) cgd.iterator_stack[i].next = &subset_generator_next if cgd.iterator_stack[i].data is NULL or \ - cgd.object_stack[i] is NULL or \ - cgd.parent_stack[i] is NULL: + cgd.object_stack[i] is NULL or \ + cgd.parent_stack[i] is NULL: for j from 0 <= j <= i: deallocate_sgd(cgd.iterator_stack[i].data) free_subset(cgd.object_stack[i]) diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 03840b3d8df..d5c002fe238 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -1178,7 +1178,7 @@ cdef class Context: if kwargs: if 'prec' in kwargs: opts.prec = int(kwargs['prec']) - if 'dps' in kwargs: + if 'dps' in kwargs: opts.prec = libmp.dps_to_prec(int(kwargs['dps'])) if 'rounding' in kwargs: opts.rounding = rndmode_from_python(kwargs['rounding']) @@ -1350,7 +1350,7 @@ cdef class Context: opts.rounding = global_opts.rounding if kwargs: if 'prec' in kwargs: opts.prec = int(kwargs['prec']) - if 'dps' in kwargs: opts.prec = libmp.dps_to_prec(int(kwargs['dps'])) + if 'dps' in kwargs: opts.prec = libmp.dps_to_prec(int(kwargs['dps'])) if 'rounding' in kwargs: opts.rounding = rndmode_from_python(kwargs['rounding']) if typx == 1: if MPF_sgn(&tmp_opx_re) < 0: @@ -1435,7 +1435,7 @@ cdef class wrapped_libmp_function: rounding = rndmode_to_python(global_opts.rounding) if kwargs: if 'prec' in kwargs: prec = int(kwargs['prec']) - if 'dps' in kwargs: prec = libmp.dps_to_prec(int(kwargs['dps'])) + if 'dps' in kwargs: prec = libmp.dps_to_prec(int(kwargs['dps'])) if 'rounding' in kwargs: rounding = kwargs['rounding'] typx = MPF_set_any(&tmp_opx_re, &tmp_opx_im, x, global_opts, 1) if typx == 1: @@ -1846,7 +1846,7 @@ cdef class mpf(mpf_base): opts = global_opts if kwargs: if 'prec' in kwargs: opts.prec = int(kwargs['prec']) - if 'dps' in kwargs: opts.prec = libmp.dps_to_prec(int(kwargs['dps'])) + if 'dps' in kwargs: opts.prec = libmp.dps_to_prec(int(kwargs['dps'])) if 'rounding' in kwargs: opts.rounding = rndmode_from_python(kwargs['rounding']) if MPF_set_any(&self.value, &self.value, x, opts, 1) != 1: raise TypeError diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index f32d738df0d..c49a22aa210 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1383,14 +1383,14 @@ cdef class Matrix_cyclo_dense(Matrix_dense): p = previous_prime(MAX_MODULUS) prod = 1 v = [] - #A, denom = self._matrix._clear_denom() + # A, denom = self._matrix._clear_denom() # TODO: this might be stupidly slow denom = self._matrix.denominator() A._matrix = (denom*self._matrix) bound = A._charpoly_bound() L_last = 0 while prod <= bound: - while (n >= 2 and p % n != 1) or denom % p == 0: + while (n >= 2 and p % n != 1) or denom % p == 0: if p == 2: raise RuntimeError("we ran out of primes in multimodular charpoly algorithm.") p = previous_prime(p) diff --git a/src/sage/modules/vector_rational_dense.pyx b/src/sage/modules/vector_rational_dense.pyx index 22e3c67fcc3..e843b67677a 100644 --- a/src/sage/modules/vector_rational_dense.pyx +++ b/src/sage/modules/vector_rational_dense.pyx @@ -308,7 +308,7 @@ cdef class Vector_rational_dense(free_module_element.FreeModuleElement): r = right z = self._new_c() cdef Py_ssize_t i - for i in range(self._degree): + for i in range(self._degree): mpq_mul(z._entries[i], self._entries[i], r._entries[i]) return z diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 55c63a16353..4ac65e15f40 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -2981,8 +2981,8 @@ cdef class GLPKBackend(GenericBackend): i = glp_eval_tab_row(self.lp, k + 1, c_indices, c_values) - indices = [c_indices[j+1] - 1 for j in range(i)] - values = [c_values[j+1] for j in range(i)] + indices = [c_indices[j + 1] - 1 for j in range(i)] + values = [c_values[j + 1] for j in range(i)] return (indices, values) cpdef eval_tab_col(self, int k): @@ -3079,8 +3079,8 @@ cdef class GLPKBackend(GenericBackend): i = glp_eval_tab_col(self.lp, k + 1, c_indices, c_values) - indices = [c_indices[j+1] - 1 for j in range(i)] - values = [c_values[j+1] for j in range(i)] + indices = [c_indices[j + 1] - 1 for j in range(i)] + values = [c_values[j + 1] for j in range(i)] return (indices, values) def __dealloc__(self): diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 36ec9bfed0d..63e88886ec5 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -2894,8 +2894,8 @@ cdef class MixedIntegerLinearProgram(SageObject): """ d = {} for v in L: - for id,coeff in v.iteritems(): - d[id] = coeff + d.get(id,0) + for id, coeff in v.iteritems(): + d[id] = coeff + d.get(id, 0) return self.linear_functions_parent()(d) def get_backend(self): diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index f345f47f09b..796e158a2ca 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -14,7 +14,7 @@ AUTHOR: # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from cpython.object cimport PyObject_RichCompare @@ -22,7 +22,7 @@ from libc.math cimport log, sqrt, exp, isnormal, isfinite, M_PI cdef double sqrt2pi = sqrt(2*M_PI) from cysignals.signals cimport sig_on, sig_off -from sage.misc.flatten import flatten +from sage.misc.flatten import flatten from sage.structure.element import Matrix from sage.stats.time_series cimport TimeSeries diff --git a/src/sage/stats/hmm/util.pyx b/src/sage/stats/hmm/util.pyx index c43abc73f75..ef5feb4ce37 100644 --- a/src/sage/stats/hmm/util.pyx +++ b/src/sage/stats/hmm/util.pyx @@ -4,19 +4,20 @@ Hidden Markov Models -- Utility functions AUTHOR: - - William Stein, 2010-03 +- William Stein, 2010-03 """ ############################################################################# # Copyright (C) 2010 William Stein # Distributed under the terms of the GNU General Public License (GPL) v2+. # The full text of the GPL is available at: -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################# from sage.structure.element import Matrix -from sage.misc.flatten import flatten +from sage.misc.flatten import flatten + cdef class HMM_Util: """ From e0ffbf3e7e198f4316b317d495cabb6210734f89 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:44:27 +0700 Subject: [PATCH 472/507] Make test deterministic to fix CI --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 2a5ee28b5d6..23bec9a5238 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2354,12 +2354,14 @@ def gens(self, proof=None, **kwds): sage: set(E.gens()) <= set([P,-P]) True - Check that :issue:`38813` has been fixed: + Check that :issue:`38813` has been fixed:: - sage: set_random_seed(91390048253425197917505296851335255685) + sage: # long time sage: E = EllipticCurve([-127^2,0]) - sage: E.gens(use_database=False, algorithm='pari', pari_effort=4) # long time + sage: l = E.gens(use_database=False, algorithm='pari', pari_effort=4); l # random [(611429153205013185025/9492121848205441 : 15118836457596902442737698070880/924793900700594415341761 : 1)] + sage: a = E(611429153205013185025/9492121848205441, 15118836457596902442737698070880/924793900700594415341761) + sage: assert len(l) == 1 and ((l[0] - a).is_finite_order() or (l[0] + a).is_finite_order()) """ if proof is None: from sage.structure.proof.proof import get_flag From ab7ee7d2894f2ef8302a9b406c0fffcca3f667e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 13:05:32 +0100 Subject: [PATCH 473/507] simplify empty sets --- src/sage/algebras/orlik_solomon.py | 8 +++--- src/sage/algebras/orlik_terao.py | 8 +++--- src/sage/calculus/desolvers.py | 5 ++-- .../combinat/root_system/pieri_factors.py | 2 +- src/sage/doctest/util.py | 2 +- src/sage/geometry/fan.py | 2 +- src/sage/geometry/hasse_diagram.py | 2 +- .../hyperplane_arrangement/arrangement.py | 2 +- src/sage/geometry/lattice_polytope.py | 5 ++-- src/sage/geometry/polyhedral_complex.py | 18 ++++++------- src/sage/graphs/generators/families.py | 2 +- src/sage/graphs/generators/smallgraphs.py | 2 +- src/sage/matrix/matrix_modn_sparse.pyx | 2 +- src/sage/matrix/matrix_rational_sparse.pyx | 2 +- src/sage/matroids/dual_matroid.py | 4 +-- src/sage/matroids/linear_matroid.pyx | 18 ++++++------- src/sage/matroids/matroid.pyx | 26 +++++++++---------- src/sage/matroids/minor_matroid.py | 4 +-- src/sage/matroids/utilities.py | 4 +-- .../rings/polynomial/pbori/specialsets.py | 2 +- src/sage/symbolic/relation.py | 2 +- 21 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 113bb29418f..f1b46ca4c65 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -192,7 +192,7 @@ def one_basis(self): sage: M = matroids.Wheel(3) sage: OS = M.orlik_solomon_algebra(QQ) - sage: OS.one_basis() == frozenset([]) + sage: OS.one_basis() == frozenset() True """ return frozenset({}) @@ -347,7 +347,7 @@ def subset_image(self, S): [[(1, 2), (1, 4), (2, 3), (3, 4)], [(3, 5), (3, 6), (5, 6)]] sage: OSMG = MG.orlik_solomon_algebra(QQ, ordering=s) - sage: OSMG.subset_image(frozenset([])) + sage: OSMG.subset_image(frozenset()) OS{} sage: OSMG.subset_image(frozenset([(1,2),(3,4),(1,4),(2,3)])) 0 @@ -375,7 +375,7 @@ def subset_image(self, S): [0, 3, 5], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [2, 3], [4, 5]] sage: OSMG = MG.orlik_solomon_algebra(QQ) - sage: OSMG.subset_image(frozenset([])) + sage: OSMG.subset_image(frozenset()) OS{} sage: OSMG.subset_image(frozenset([1, 2, 3])) 0 @@ -394,7 +394,7 @@ def subset_image(self, S): sage: sorted([sorted(c) for c in MG.circuits()]) [[0, 1], [2, 3, 4]] sage: OSMG = MG.orlik_solomon_algebra(QQ) - sage: OSMG.subset_image(frozenset([])) + sage: OSMG.subset_image(frozenset()) OS{} sage: OSMG.subset_image(frozenset([1, 3, 4])) -OS{0, 2, 3} + OS{0, 2, 4} diff --git a/src/sage/algebras/orlik_terao.py b/src/sage/algebras/orlik_terao.py index 26b3c49b443..b039e7bef3f 100644 --- a/src/sage/algebras/orlik_terao.py +++ b/src/sage/algebras/orlik_terao.py @@ -242,7 +242,7 @@ def one_basis(self): sage: M = matroids.Wheel(3) sage: OT = M.orlik_terao_algebra(QQ) - sage: OT.one_basis() == frozenset([]) + sage: OT.one_basis() == frozenset() True """ return frozenset({}) @@ -391,7 +391,7 @@ def subset_image(self, S): [[(1, 2), (1, 4), (2, 3), (3, 4)], [(3, 5), (3, 6), (5, 6)]] sage: OT = M.orlik_terao_algebra(QQ, ordering=s) - sage: OT.subset_image(frozenset([])) + sage: OT.subset_image(frozenset()) OT{} sage: OT.subset_image(frozenset([(1,2),(3,4),(1,4),(2,3)])) 0 @@ -419,7 +419,7 @@ def subset_image(self, S): [0, 3, 5], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [2, 3], [4, 5]] sage: OT = M.orlik_terao_algebra() - sage: OT.subset_image(frozenset([])) + sage: OT.subset_image(frozenset()) OT{} sage: OT.subset_image(frozenset([1, 2, 3])) 0 @@ -438,7 +438,7 @@ def subset_image(self, S): sage: sorted([sorted(c) for c in M.circuits()]) [[0, 1], [2, 3, 4]] sage: OT = M.orlik_terao_algebra(QQ) - sage: OT.subset_image(frozenset([])) + sage: OT.subset_image(frozenset()) OT{} sage: OT.subset_image(frozenset([1, 3, 4])) -OT{0, 2, 3} + OT{0, 2, 4} diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index 6a8d8fbe2bb..e959c22f234 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -913,7 +913,7 @@ def desolve_system(des, vars, ics=None, ivar=None, algorithm='maxima'): if len(des) == 1 and algorithm == "maxima": return desolve_laplace(des[0], vars[0], ics=ics, ivar=ivar) - ivars = set([]) + ivars = set() for i, de in enumerate(des): if not (isinstance(de, Expression) and de.is_relational()): des[i] = de == 0 @@ -1440,11 +1440,10 @@ def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1 - Robert Marik (10-2009) """ - if ics is None: raise ValueError("No initial conditions, specify with ics=[x0,y01,y02,...].") - ivars = set([]) + ivars = set() for de in des: ivars = ivars.union(set(de.variables())) diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index c1f2ba8afd3..e903042dc63 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -519,7 +519,7 @@ class PieriFactors_type_A_affine(PieriFactors_affine_type): @staticmethod def __classcall__(cls, W, min_length=0, max_length=infinity, - min_support=frozenset([]), max_support=None): + min_support=frozenset(), max_support=None): r""" TESTS:: diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index a62d48cfa19..b78dd838e18 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -606,7 +606,7 @@ def make_recording_dict(D, st, gt): EXAMPLES:: sage: from sage.doctest.util import make_recording_dict - sage: D = make_recording_dict({'a':4,'d':42},set([]),set(['not_here'])) + sage: D = make_recording_dict({'a':4,'d':42},set(),set(['not_here'])) sage: sorted(D.items()) [('a', 4), ('d', 42)] sage: D.got diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index fc004dd88ac..7af1fa122ee 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -636,7 +636,7 @@ def result(): (len(cones), len(generating_cones))) elif discard_faces: cones = _discard_faces(cones) - ray_set = set([]) + ray_set = set() for cone in cones: ray_set.update(cone.rays()) if rays: # Preserve the initial order of rays, if they were given diff --git a/src/sage/geometry/hasse_diagram.py b/src/sage/geometry/hasse_diagram.py index 74a30abed6d..1b95deb7382 100644 --- a/src/sage/geometry/hasse_diagram.py +++ b/src/sage/geometry/hasse_diagram.py @@ -146,7 +146,7 @@ def default_face_constructor(atoms, coatoms, **kwds): atoms = atoms.intersection(coatom_to_atoms[coatom]) H[atom] = (atoms, coatoms) # 8: compute the set G of minimal sets in H - minimals = set([]) + minimals = set() while candidates: candidate = candidates.pop() atoms = H[candidate][0] diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 8517bb36671..854c412fc83 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -2199,7 +2199,7 @@ def poset_of_regions(self, B=None, numbered_labels=True): while R: # Transfer the "next step" to the "current step" curTest = list(nextTest) - nextTest = set([]) + nextTest = set() # we want to test each region that we haven't hit yet for r in R: # Since it's graded, it suffices to look at the regions of the previous rank diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index da030a17966..eb386e3f9a9 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -3947,11 +3947,10 @@ def skeleton_points(self, k=1): """ if k >= self.dim(): return list(range(self.npoints())) - skeleton = set([]) + skeleton = set() for face in self.faces(dim=k): skeleton.update(face.ambient_point_indices()) - skeleton = sorted(skeleton) - return skeleton + return sorted(skeleton) def skeleton_show(self, normal=None): r"""Show the graph of one-skeleton of this polytope. diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 768c0cfe82a..9d6b4c84d6e 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -370,7 +370,7 @@ def cells(self, subcomplex=None) -> dict: for k in range(self._dim, -1, -1): if k in maximal_cells: if k not in cells: - cells[k] = set([]) + cells[k] = set() cells[k].update(maximal_cells[k]) if k in cells: for cell in cells[k]: @@ -382,7 +382,7 @@ def cells(self, subcomplex=None) -> dict: covers[p] = [] covers[p].append(cell) if (k-1) not in cells: - cells[k-1] = set([]) + cells[k-1] = set() cells[k-1].add(p) self._face_poset = Poset(covers) self._cells = cells @@ -1055,7 +1055,7 @@ def is_subcomplex(self, other) -> bool: """ other_cells = other.cells() for (d, stratum) in self.maximal_cells().items(): - if not stratum.issubset(other_cells.get(d, set([]))): + if not stratum.issubset(other_cells.get(d, set())): return False return True @@ -1594,9 +1594,9 @@ def is_convex(self) -> bool: # After making sure that the affine hulls of the cells are the same, # it does not matter that is not full dimensional. boundaries = self.relative_boundary_cells() - vertices = set([]) - rays = set([]) - lines = set([]) + vertices = set() + rays = set() + lines = set() for cell in boundaries: # it suffices to consider only vertices on the boundaries # Note that a line (as polyhedron) has vertex too @@ -2055,7 +2055,7 @@ def add_cell(self, cell): for facet in c.facets(): p = facet.as_polyhedron() if d not in cells: - cells[d] = set([]) + cells[d] = set() if p not in cells[d]: cells[d].add(p) covers[p] = [c] @@ -2358,7 +2358,7 @@ def subdivide(self, make_simplicial=False, if new_rays: raise ValueError("rays/lines cannot be used for subdivision") # bounded version of `fan.subdivide`; not require rational. - vertices = set([]) + vertices = set() if make_simplicial and not self.is_simplicial_complex(): for p in self.maximal_cell_iterator(): for v in p.vertices_list(): @@ -2390,7 +2390,7 @@ def subdivide(self, make_simplicial=False, raise ValueError("new vertices cannot be used for subdivision") # mimic :meth:`~sage.geometry.fan .subdivide` # but here we allow for non-pointed cones, and we subdivide them. - rays_normalized = set([]) + rays_normalized = set() self_rays = [] cones = [] for p in self.maximal_cell_iterator(): diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 74e7dd01f28..71a146f6b49 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -3135,7 +3135,7 @@ def YDeltaTrans(G, v): # for as long as we generate new graphs. P = PetersenGraph() - l = set([]) + l = set() l_new = [P.canonical_label().graph6_string()] while l_new: diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 4fc69e689b6..916b1eb1a1f 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -5642,7 +5642,7 @@ def pi_vec(x): # Associate a matrix to every entry of W int_to_matrix = {0: matrix.zero(45)} for i in range(15): - vec = [frozenset([]), L[0, 0], L[1, 0], L[2, 0], L[3, 0]] + vec = [frozenset(), L[0, 0], L[1, 0], L[2, 0], L[3, 0]] vec = f_pow(pi_vec, i % 3, vec) vec = f_pow(sigma2, i % 5, vec) int_to_matrix[i + 1] = N(vec) diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 33056c221ce..8f5d657a464 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -361,7 +361,7 @@ cdef class Matrix_modn_sparse(Matrix_sparse): cdef c_vector_modint* v # Build a table that gives the nonzero positions in each column of right - nonzero_positions_in_columns = [set([]) for _ in range(right._ncols)] + nonzero_positions_in_columns = [set() for _ in range(right._ncols)] cdef Py_ssize_t i, j, k for i from 0 <= i < right._nrows: v = &(right.rows[i]) diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index 8e9898642c3..ffe11128dd4 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -236,7 +236,7 @@ cdef class Matrix_rational_sparse(Matrix_sparse): cdef mpq_vector* v # Build a table that gives the nonzero positions in each column of right - nonzero_positions_in_columns = [set([]) for _ in range(right._ncols)] + nonzero_positions_in_columns = [set() for _ in range(right._ncols)] cdef Py_ssize_t i, j, k for i in range(right._nrows): v = &(right._matrix[i]) diff --git a/src/sage/matroids/dual_matroid.py b/src/sage/matroids/dual_matroid.py index 59dcfa73cc9..f930cafd5c7 100644 --- a/src/sage/matroids/dual_matroid.py +++ b/src/sage/matroids/dual_matroid.py @@ -332,8 +332,8 @@ def _minor(self, contractions=None, deletions=None): EXAMPLES:: sage: M = matroids.catalog.Vamos().dual() - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) Dual of 'M / {'b', 'c'} \ {'a'}, where M is Vamos: Matroid of rank 4 on 8 elements with circuit-closures {3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'}, diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 7259f9f1dc0..58ff8b8def1 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -1332,8 +1332,8 @@ cdef class LinearMatroid(BasisExchangeMatroid): sage: M = Matroid(groundset='abcdefgh', ring=GF(5), ....: reduced_matrix=[[2, 1, 1, 0], ....: [1, 1, 0, 1], [1, 0, 1, 1], [0, 1, 1, 2]]) - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) Linear matroid of rank 3 on 5 elements represented over the Finite Field of size 5 """ @@ -2847,7 +2847,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): if sol: if certificate: (certX, certY) = cert_pair - cert = set([]) + cert = set() for x in certX: cert.add(dX[x]) for y in certY: @@ -3783,8 +3783,8 @@ cdef class BinaryMatroid(LinearMatroid): EXAMPLES:: sage: M = matroids.catalog.Fano() - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) Binary matroid of rank 2 on 4 elements, type (0, 6) """ self._move_current_basis(contractions, deletions) @@ -4720,8 +4720,8 @@ cdef class TernaryMatroid(LinearMatroid): EXAMPLES:: sage: M = matroids.catalog.P8() - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) Ternary matroid of rank 3 on 5 elements, type 0- """ self._move_current_basis(contractions, deletions) @@ -5488,8 +5488,8 @@ cdef class QuaternaryMatroid(LinearMatroid): EXAMPLES:: sage: M = matroids.catalog.Q10() # needs sage.rings.finite_rings - sage: N = M._minor(contractions=set(['a']), deletions=set([])) # needs sage.rings.finite_rings - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) # needs sage.rings.finite_rings + sage: N = M._minor(contractions=set(['a']), deletions=set()) # needs sage.rings.finite_rings + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) # needs sage.rings.finite_rings Quaternary matroid of rank 4 on 7 elements """ self._move_current_basis(contractions, deletions) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index d4a71e8a38e..c308c492022 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -894,7 +894,7 @@ cdef class Matroid(SageObject): True """ cdef set XX = set(X) - cdef set res = set([]) + cdef set res = set() cdef int r = self._rank(frozenset(X)) for e in Y: XX.add(e) @@ -1155,8 +1155,8 @@ cdef class Matroid(SageObject): EXAMPLES:: sage: M = matroids.catalog.Vamos() - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['b', 'c'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['b', 'c'])) M / {'a'} \ {'b', 'c'}, where M is Vamos: Matroid of rank 4 on 8 elements with circuit-closures {3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'}, @@ -2603,7 +2603,7 @@ cdef class Matroid(SageObject): sage: [sorted(X) for X in CC[3]] [['a', 'b', 'c', 'd', 'e', 'f', 'g']] """ - CC = [set([]) for r in range(self.rank() + 1)] + CC = [set() for r in range(self.rank() + 1)] for C in self.circuits_iterator(): CC[len(C) - 1].add(self.closure(C)) return {r: CC[r] for r in range(self.rank() + 1) if CC[r]} @@ -2634,7 +2634,7 @@ cdef class Matroid(SageObject): ... KeyError: 3 """ - CC = [set([]) for r in range(self.rank() + 1)] + CC = [set() for r in range(self.rank() + 1)] for C in self.nonspanning_circuits_iterator(): CC[len(C) - 1].add(self.closure(C)) return {r: CC[r] for r in range(self.rank() + 1) if CC[r]} @@ -4944,7 +4944,7 @@ cdef class Matroid(SageObject): """ E = set(self.groundset()) E.difference_update(self._closure(frozenset())) # groundset minus loops - res = set([]) + res = set() while E: e = E.pop() @@ -4980,7 +4980,7 @@ cdef class Matroid(SageObject): """ E = set(self.groundset()) E.difference_update(self._coclosure(frozenset())) # groundset minus coloops - res = set([]) + res = set() while E: e = E.pop() @@ -5412,7 +5412,7 @@ cdef class Matroid(SageObject): for R1 in map(set, combinations(R, r2)): R2 = R - R1 # F is the set of elements cannot be in the extension of Q1 - F = set([]) + F = set() U = E - R # if Q1|R1 is full if m-len(Q1)-len(R1) == 0: @@ -5925,14 +5925,14 @@ cdef class Matroid(SageObject): ....: [0,0,0,0,1,1,1,1,0,0,1,1], ....: [0,0,0,0,0,0,0,0,1,1,1,1]]) sage: M._shifting_all(M.basis(), - ....: set([0,1]), set([0,1]), set([]), set([]), 3) + ....: set([0,1]), set([0,1]), set(), set(), 3) (False, None) sage: M = Matroid(field=GF(2), reduced_matrix=[[1,0,1,1,1], ....: [1,1,1,1,0], ....: [0,1,1,1,0], ....: [0,0,0,1,1]]) sage: M._shifting_all(M.basis(), - ....: set([0,1]), set([5,8]), set([]), set([]), 3)[0] + ....: set([0,1]), set([5,8]), set(), set(), 3)[0] True """ Y = self.groundset()-X @@ -5986,14 +5986,14 @@ cdef class Matroid(SageObject): ....: [0,0,0,0,1,1,1,1,0,0,1,1], ....: [0,0,0,0,0,0,0,0,1,1,1,1]]) sage: M._shifting(M.basis(), - ....: set([0,1]), set([0,1]), set([]), set([]), 3) + ....: set([0,1]), set([0,1]), set(), set(), 3) (False, None) sage: M = Matroid(field=GF(2), reduced_matrix=[[1,0,1,1,1], ....: [1,1,1,1,0], ....: [0,1,1,1,0], ....: [0,0,0,1,1]]) sage: M._shifting(M.basis(), - ....: set([0,1]), set([5,8]), set([]), set([4]), 3)[0] + ....: set([0,1]), set([5,8]), set(), set([4]), 3)[0] True """ @@ -6954,7 +6954,7 @@ cdef class Matroid(SageObject): raise ValueError("nonnegative weights were expected.") wt = sorted(wt, reverse=True) Y = [e for (w, e) in wt] - res = set([]) + res = set() r = 0 for e in Y: res.add(e) diff --git a/src/sage/matroids/minor_matroid.py b/src/sage/matroids/minor_matroid.py index 3bfec4bf2a4..871a8892579 100644 --- a/src/sage/matroids/minor_matroid.py +++ b/src/sage/matroids/minor_matroid.py @@ -324,8 +324,8 @@ def _minor(self, contractions, deletions): sage: from sage.matroids.advanced import * sage: M = MinorMatroid(matroids.catalog.Vamos(), contractions=set('c'), deletions={'b', 'f'}) - sage: N = M._minor(contractions=set(['a']), deletions=set([])) - sage: N._minor(contractions=set([]), deletions=set(['d'])) + sage: N = M._minor(contractions=set(['a']), deletions=set()) + sage: N._minor(contractions=set(), deletions=set(['d'])) M / {'a', 'c'} \ {'b', 'd', 'f'}, where M is Vamos: Matroid of rank 4 on 8 elements with circuit-closures {3: {{'a', 'b', 'c', 'd'}, {'a', 'b', 'e', 'f'}, diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py index eb8b493c2e0..cb462793050 100644 --- a/src/sage/matroids/utilities.py +++ b/src/sage/matroids/utilities.py @@ -429,7 +429,7 @@ def spanning_stars(M): # remove low degree vertices H = [] # candidate vertices - V_0 = set([]) + V_0 = set() d = 0 while G.order(): x, d = min(G.degree_iterator(labels=True), key=itemgetter(1)) @@ -444,7 +444,7 @@ def spanning_stars(M): # greedily remove vertices G2 = G.copy() # set of picked vertices - V_1 = set([]) + V_1 = set() while G2.order(): # choose vertex with maximum degree in G2 x, d = max(G2.degree_iterator(labels=True), key=itemgetter(1)) diff --git a/src/sage/rings/polynomial/pbori/specialsets.py b/src/sage/rings/polynomial/pbori/specialsets.py index 9c5b4bdefc8..8ad680857ba 100644 --- a/src/sage/rings/polynomial/pbori/specialsets.py +++ b/src/sage/rings/polynomial/pbori/specialsets.py @@ -87,7 +87,7 @@ def power_set(variables): print(list(all_monomials_of_degree_d(1, []))) print(list(power_set([Variable(i) for i in range(2)]))) print(list(power_set([Variable(i) for i in range(4)]))) - print(list(power_set([]))) + print(list(power_set())) # every monomial in the first 8 var, which is at most linear in the first 5 print(list(mod_mon_set( power_set([Variable(i) for i in range(8)]), diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index d0098871834..01f5bd147e9 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -1807,7 +1807,7 @@ def solve_ineq_fourier(ineq, vars=None): - Robert Marik (01-2010) """ if vars is None: - setvars = set([]) + setvars = set() for i in (ineq): setvars = setvars.union(set(i.variables())) vars = list(setvars) From 710acc3b1b7083721ce2671c5e90d207542ca2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 16:51:48 +0100 Subject: [PATCH 474/507] suggested detail --- src/sage/modular/pollack_stevens/fund_domain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index f1647996153..c478f1650fe 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -1143,7 +1143,7 @@ def form_list_of_cusps(self): # Initialize some lists - C = [QQ(-1), "?", QQ(0)] + C = [QQ(-1), "?", QQ.zero()] # Initialize the list of cusps at the bottom of the fund. domain. # The ? denotes that it has not yet been checked if more cusps need @@ -1151,7 +1151,7 @@ def form_list_of_cusps(self): full_domain = False # Says that we are not done yet! - v = [False for _ in range(sP)] + v = [False] * sP # This initializes a list indexed by P^1(Z/NZ) which keeps track of # which right coset representatives we've found for Gamma_0(N)/SL_2(Z) # thru the construction of a fundamental domain From 7be6aa11843077aa27f499ad932bbcc7551979df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Feb 2025 19:55:45 +0100 Subject: [PATCH 475/507] suggested details --- src/sage/stats/hmm/chmm.pyx | 4 ++-- src/sage/stats/hmm/util.pyx | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index 796e158a2ca..7d1e2399b54 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -7,7 +7,7 @@ AUTHOR: - William Stein, 2010-03 """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2010 William Stein # # This program is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ AUTHOR: # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# *************************************************************************** from cpython.object cimport PyObject_RichCompare from libc.math cimport log, sqrt, exp, isnormal, isfinite, M_PI diff --git a/src/sage/stats/hmm/util.pyx b/src/sage/stats/hmm/util.pyx index ef5feb4ce37..d2eb4ff515c 100644 --- a/src/sage/stats/hmm/util.pyx +++ b/src/sage/stats/hmm/util.pyx @@ -7,12 +7,12 @@ AUTHOR: - William Stein, 2010-03 """ -############################################################################# +# ########################################################################## # Copyright (C) 2010 William Stein # Distributed under the terms of the GNU General Public License (GPL) v2+. # The full text of the GPL is available at: # https://www.gnu.org/licenses/ -############################################################################# +# ########################################################################## from sage.structure.element import Matrix @@ -124,7 +124,6 @@ cdef class HMM_Util: This function is used internally by the ``__init__`` methods of Hidden Markov Models to make a transition matrix from ``A``. - INPUT: - ``A`` -- matrix, list, list of lists, or :class:`TimeSeries` From 2a57f75a72ccc19cc8245e7d1a06bce4e41c2550 Mon Sep 17 00:00:00 2001 From: Noel-Roemmele <94943596+Noel-Roemmele@users.noreply.github.com> Date: Sat, 22 Feb 2025 20:16:54 -0700 Subject: [PATCH 476/507] Set added to help in removing duplicates. Change suggested by tscrim. Co-authored-by: Travis Scrimshaw --- src/sage/combinat/partition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index c1f4f06f129..f4f28740e11 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -7608,7 +7608,7 @@ def __classcall_private__(cls, n, parts): sage: list(Partitions(4,parts_in=vector(ZZ,[2,4]))) [[4], [2, 2]] """ - parts = tuple(sorted(map(ZZ,parts))) + parts = tuple(sorted(set(map(ZZ,parts)))) return super().__classcall__(cls, Integer(n), parts) def __init__(self, n, parts): From 94fc518e209f5a8e979e5ea41ae825cece2c5257 Mon Sep 17 00:00:00 2001 From: Noel-Roemmele <94943596+Noel-Roemmele@users.noreply.github.com> Date: Sat, 22 Feb 2025 20:19:41 -0700 Subject: [PATCH 477/507] Removed try catch block for a simpler solution. Change suggested by tscrim. Co-authored-by: Travis Scrimshaw --- src/sage/matrix/matrix_integer_dense.pyx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index d84a3317733..b133bdaebca 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -5448,10 +5448,7 @@ cdef class Matrix_integer_dense(Matrix_dense): for i from 0 <= i < index: fmpz_init_set(fmpz_mat_entry(res._matrix,i,j), fmpz_mat_entry(self._matrix,i,j)) - try: - z = row[j] - except TypeError: - z = ZZ(row[j]) + z = ZZ(row[j]) fmpz_set_mpz(zflint,z.value) fmpz_init_set(fmpz_mat_entry(res._matrix,index,j), zflint) From 1e4c37412c2ff77185b7da7afa4cf5dc4227a19d Mon Sep 17 00:00:00 2001 From: Noel-Roemmele <94943596+Noel-Roemmele@users.noreply.github.com> Date: Sat, 22 Feb 2025 20:20:39 -0700 Subject: [PATCH 478/507] Updated doctest to be more robust. Change suggested by tscrim. Co-authored-by: Travis Scrimshaw --- src/sage/matrix/matrix_integer_dense.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index b133bdaebca..ef4159bbc65 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -5429,9 +5429,9 @@ cdef class Matrix_integer_dense(Matrix_dense): Ensure that :issue:`11328` is fixed:: sage: m = matrix([[int(1),int(1)],[int(1),int(1)]]) - sage: m.insert_row(1,[int(1),int(1)]) - [1 1] + sage: m.insert_row(1,[int(2),int(3)]) [1 1] + [2 3] [1 1] """ cdef Matrix_integer_dense res = self._new(self._nrows + 1, self._ncols) From b9275d72eaa71dc3f1084d86eb6df88d720b41cb Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sat, 22 Feb 2025 20:41:51 -0700 Subject: [PATCH 479/507] Change function to expect tuple instead of list --- src/sage/rings/polynomial/symmetric_reduction.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/symmetric_reduction.pyx b/src/sage/rings/polynomial/symmetric_reduction.pyx index 98133d617df..0da8d656830 100644 --- a/src/sage/rings/polynomial/symmetric_reduction.pyx +++ b/src/sage/rings/polynomial/symmetric_reduction.pyx @@ -254,7 +254,7 @@ cdef class SymmetricReductionStrategy: return richcmp((left._parent, left._lm, left._tail), (right._parent, right._lm, right._tail), op) - def gens(self) -> list: + def gens(self) -> tuple: """ Return the tuple of Infinite Polynomials modulo which ``self`` reduces. From 4549c4c954d37e825b61b32db540682d550e7321 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:46:01 -0500 Subject: [PATCH 480/507] src/sage/libs/giac: remove This package has been split off into sagemath-giac. --- src/sage/libs/giac/__init__.py | 348 - src/sage/libs/giac/auto-methods.pxi | 16981 -------------------------- src/sage/libs/giac/giac.pxd | 204 - src/sage/libs/giac/giac.pyx | 2070 ---- src/sage/libs/giac/keywords.pxi | 9 - src/sage/libs/giac/meson.build | 17 - src/sage/libs/giac/misc.h | 115 - 7 files changed, 19744 deletions(-) delete mode 100644 src/sage/libs/giac/__init__.py delete mode 100644 src/sage/libs/giac/auto-methods.pxi delete mode 100644 src/sage/libs/giac/giac.pxd delete mode 100644 src/sage/libs/giac/giac.pyx delete mode 100644 src/sage/libs/giac/keywords.pxi delete mode 100644 src/sage/libs/giac/meson.build delete mode 100644 src/sage/libs/giac/misc.h diff --git a/src/sage/libs/giac/__init__.py b/src/sage/libs/giac/__init__.py deleted file mode 100644 index 49bbe254947..00000000000 --- a/src/sage/libs/giac/__init__.py +++ /dev/null @@ -1,348 +0,0 @@ -# sage.doctest: needs sage.libs.giac -""" -Wrappers for Giac functions - -We provide a python function to compute and convert to sage a Groebner -basis using the ``giacpy_sage`` module. - -AUTHORS: - -- Martin Albrecht (2015-07-01): initial version -- Han Frederic (2015-07-01): initial version - -EXAMPLES:: - - sage: from sage.libs.giac import groebner_basis as gb_giac # random - sage: P = PolynomialRing(QQ, 6, 'x') - sage: I = sage.rings.ideal.Cyclic(P) - sage: B = gb_giac(I.gens()) # random - sage: B - Polynomial Sequence with 45 Polynomials in 6 Variables -""" - -# ***************************************************************************** -# Copyright (C) 2013 Frederic Han -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# ***************************************************************************** - -from sage.structure.proof.all import polynomial as proof_polynomial -from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence -from .giac import giacsettings, libgiac - -# Remarks for doctests: -# 1) The first time that the c++ library giac is loaded a message appears. -# This message is version and arch dependent. -# 2) When proba_epsilon is too bad (>1e-6?) setting it to a better value -# will give an additional message like the following one: -# Restoring proba epsilon to 1e-6 from 1e-12 -# (it looks like in internal giac changes this also to not work with a too bad probability) - - -class GiacSettingsDefaultContext: - """ - Context preserve libgiac settings. - """ - - def __enter__(self): - """ - EXAMPLES:: - - sage: from sage.libs.giac import GiacSettingsDefaultContext - sage: from sage.libs.giac.giac import giacsettings - sage: giacsettings.proba_epsilon = 1e-16 - sage: with GiacSettingsDefaultContext(): giacsettings.proba_epsilon = 1e-12 - sage: giacsettings.proba_epsilon < 1e-14 - True - """ - self.proba_epsilon = giacsettings.proba_epsilon - self.threads = giacsettings.threads - # Change the debug level at the end to not have messages at each modification - self.debuginfolevel = libgiac('debug_infolevel()') - - def __exit__(self, typ, value, tb): - """ - EXAMPLES:: - - sage: from sage.libs.giac import GiacSettingsDefaultContext - sage: from sage.libs.giac.giac import giacsettings - sage: giacsettings.proba_epsilon = 1e-16 - sage: with GiacSettingsDefaultContext(): giacsettings.proba_epsilon = 1e-30 - sage: giacsettings.proba_epsilon < 1e-20 - False - """ - # Restore the debug level first to not have messages at each modification - libgiac('debug_infolevel')(self.debuginfolevel) - # NB: giacsettings.epsilon has a different meaning that giacsettings.proba_epsilon. - giacsettings.proba_epsilon = self.proba_epsilon - giacsettings.threads = self.threads - - -def local_giacsettings(func): - """ - Decorator to preserve Giac's proba_epsilon and threads settings. - - EXAMPLES:: - - sage: def testf(a, b): - ....: giacsettings.proba_epsilon = a/100 - ....: giacsettings.threads = b+2 - ....: return (giacsettings.proba_epsilon, giacsettings.threads) - - sage: from sage.libs.giac.giac import giacsettings - sage: from sage.libs.giac import local_giacsettings - sage: gporig, gtorig = (giacsettings.proba_epsilon,giacsettings.threads) - sage: gp, gt = local_giacsettings(testf)(giacsettings.proba_epsilon,giacsettings.threads) - sage: gporig == giacsettings.proba_epsilon - True - sage: gtorig == giacsettings.threads - True - sage: gp0, Gamma(a)=int(e^{-t}*t^{a-1},t=0..inf)) and Gamma(a)=Gamma(a+1)/a and Gamma(a,b)=ugamma(a,b). - See also: 1/ Psi 2/ Beta 3/ ugamma 4/ igamma - Ex1:Gamma(5) - Ex2:Gamma(1/2) - Ex3:Gamma(gamma(-5.1)) - Ex4:Gamma(-5.1,2.1) - ''' - return GiacMethods['Gamma'](self, *args) - - def Heaviside(self, *args): - r'''From Giac's documentation: - Help for Heaviside: - Heaviside(Real) - Function equal to 0 if x<0 and 1 if x>=0. - See also: 1/ Dirac 2/ laplace - Ex1:Heaviside(1) - Ex2:Heaviside(-1) - Ex3:Heaviside(0) - ''' - return GiacMethods['Heaviside'](self, *args) - - def JordanBlock(self, *args): - r'''From Giac's documentation: - Help for JordanBlock: - JordanBlock(Expr(a),Intg(n)) - Returns an n*n matrix with a on the diagonal, 1 above and 0 everywhere else. - See also: 1/ jordan - Ex1:JordanBlock(7,3) - ''' - return GiacMethods['JordanBlock'](self, *args) - - def LU(self, *args): - r'''From Giac's documentation: - Help for LU: - LU(Mtrx(A),Var(L),Var(U),Var(P)) - For a numerical matrix A, stores in L a lower triangular matrix, in U an upper triangular matrix and in P a permutation matrix such that P*A=L*U. - See also: 1/ lu 2/ QR - Ex1:LU([[1,2],[3,4]],L,U,P) - Ex2:LU([[6,12,18],[5,14,31],[3,8,18]],L,U,P) - ''' - return GiacMethods['LU'](self, *args) - - def LambertW(self, *args): - r'''From Giac's documentation: - Help for LambertW: - LambertW(Real(x),[Intg(n)]) - Returns a solution for t of t*exp(t)=x - Ex1:LambertW(1.0) - Ex2:LambertW(ln(4)) - Ex3:LambertW(-0.1,-1) - ''' - return GiacMethods['LambertW'](self, *args) - - def Li(self, *args): - r'''From Giac's documentation: - Help for Li: - Li(Expr) - Logarithm integral Li(x)=Ei(ln(x)) primitive of 1/ln(x). - See also: 1/ Si 2/ Ci 3/ Ei - Ex1:Li(2.0) - ''' - return GiacMethods['Li'](self, *args) - - def Line(self, *args): - r'''From Giac's documentation: - Help for Line: - Line(Expr(a),Expr(b),Expr(c),Expr(d)) - Draws the segment [a+i*b,c+i*d]. - See also: 1/ segment - Ex1:Line(-1,-2,1,2) - ''' - return GiacMethods['Line'](self, *args) - - def LineHorz(self, *args): - r'''From Giac's documentation: - Help for LineHorz: - LineHorz(Expr(a)) - Draws the horizontal line y=a. - See also: 1/ Line 2/ LineVert - Ex1:LineHorz(-1) - ''' - return GiacMethods['LineHorz'](self, *args) - - def LineTan(self, *args): - r'''From Giac's documentation: - Help for LineTan: - LineTan(Expr(f(x)),[Var],Expr(a)) - Draws the tangent to y=f(x) at x=a. Do not use parentheses or put the parenthesis around the entire expression. - See also: 1/ tangent 2/ droite_tangente - Ex1: LineTan sin(x),pi/4 - Ex2: LineTan sin(t),t=pi/4) - Ex3: LineTan sin(t),t,pi/4 - Ex4: LineTan x^2-x,1 - Ex5: (LineTan sin(t),t,pi/4) - ''' - return GiacMethods['LineTan'](self, *args) - - def LineVert(self, *args): - r'''From Giac's documentation: - Help for LineVert: - LineVert(Expr(a)) - Draws the vertical line x=a. - See also: 1/ Line 2/ LineHorz - Ex1:LineVert(2) - ''' - return GiacMethods['LineVert'](self, *args) - - def Phi(self, *args): - r'''From Giac's documentation: - Help for Phi: - Phi(Intg(n)) - Euler's function (euler(n)=card({p1 sum(1/n^a,n,1,+infinity). - See also: 1/ sum - Ex1:Zeta(2) - ''' - return GiacMethods['Zeta'](self, *args) - - def a2q(self, *args): - r'''From Giac's documentation: - Help for a2q: - a2q(Mtrx,VectVar) - a2q(A,X)=the quadratic form q associated to A, X=vector of variables. - See also: 1/ q2a - Ex1:a2q([[1,2],[4,4]],[x,y]) - Ex2:a2q([[1,3],[3,4]],[x,y]) - ''' - return GiacMethods['a2q'](self, *args) - - def abcuv(self, *args): - r'''From Giac's documentation: - Help for abcuv: - abcuv(Poly(a),Poly(b),Poly(c),[Var]) - Returns [u,v] such that au+bv=c for 3 polynomials a,b,c. - See also: 1/ egcd 2/ iabcuv - Ex1:abcuv(x^2+2*x+1,x^2-1,x+1) - Ex2:abcuv(X^2+2*X+1,X^2-1,X+1,X) - Ex3:abcuv(x^2+2*x+1,x^2-1,x^3+1) - Ex4:abcuv(X^2+2*X+1,X^2-1,X^3+1,X) - Ex5:abcuv([1,2,1],[1,0,-1],[1,0,0,1]) - ''' - return GiacMethods['abcuv'](self, *args) - - def about(self, *args): - r'''From Giac's documentation: - Help for about: - about(Var(a)) - Returns the hypothesis made with assume on the variable a. - See also: 1/ assume 2/ purge - Ex1:about(a) - Ex2:about(n) - ''' - return GiacMethods['about'](self, *args) - - def abs(self, *args): - r'''From Giac's documentation: - Help for abs: - abs(Cplx||LstCplx) - Returns the absolute value or the norm of its argument. - See also: 1/ arg - Ex1:abs(-4) - Ex2:abs(1+2*i) - Ex3:abs((1+2*i)^2) - Ex4:abs([-2,1+i,-4]) - ''' - return GiacMethods['abs'](self, *args) - - def abscissa(self, *args): - r'''From Giac's documentation: - Help for abscissa: - abscissa(Pnt or Vect) - Returns the abscissa of a point or a vector. - See also: 1/ ordinate 2/ affix 3/ cote 4/ coordinates - Ex1:abscissa(point(1+2*i)) - Ex2:abscissa(point(i)-point(1+2*i)) - Ex3:abscissa(-1-i) - Ex4:abscissa(point(1,2,3)) - ''' - return GiacMethods['abscissa'](self, *args) - - def accumulate_head_tail(self, *args): - r'''From Giac's documentation: - Help for accumulate_head_tail: - accumulate_head_tail(Lst(l),Intg(p),Intg(q)) - Returns the list where the first p and the last q elements of l are replaced by their sum. - See also: 1/ - Ex1:accumulate_head_tail([0,1,2,3,4,5,6,7,8,9],3,2) - ''' - return GiacMethods['accumulate_head_tail'](self, *args) - - def acos(self, *args): - r'''From Giac's documentation: - Help for acos: - acos(Expr) - Arccosine. - See also: 1/ cos 2/ acosh - Ex1:acos(0) - ''' - return GiacMethods['acos'](self, *args) - - def acos2asin(self, *args): - r'''From Giac's documentation: - Help for acos2asin: - acos2asin(Expr) - Replaces arccos(x) by pi/2-arcsin(x) in the argument. - See also: 1/ acos2atan - Ex1:acos2asin(acos(x)+asin(x)) - Ex2:acos2asin(2*acos(x)) - ''' - return GiacMethods['acos2asin'](self, *args) - - def acos2atan(self, *args): - r'''From Giac's documentation: - Help for acos2atan: - acos2atan(Expr) - Replaces arccos(x) by pi/2-arctan(x/sqrt(1-x^2)) in the argument. - See also: 1/ acos2asin - Ex1:acos2atan(2*acos(x)) - Ex2:acos2atan(acos(sqrt(1-x^2))+acos(x)) - ''' - return GiacMethods['acos2atan'](self, *args) - - def acosh(self, *args): - r'''From Giac's documentation: - Help for acosh: - acosh(Expr) - Hyperbolic arccosine. - See also: 1/ cosh 2/ acos - Ex1:acosh(1) - ''' - return GiacMethods['acosh'](self, *args) - - def acot(self, *args): - r'''From Giac's documentation: - Help for acot: - acot(Expr) - Arccotangent. - See also: 1/ atan 2/ arccos - Ex1:acot(0) - ''' - return GiacMethods['acot'](self, *args) - - def acsc(self, *args): - r'''From Giac's documentation: - Help for acsc: - acsc(Expr) - Arccosecant: acsc(x)=asin(1/x). - See also: 1/ asin 2/ csc - Ex1:acsc(1) - Ex2:acsc(2) - ''' - return GiacMethods['acsc'](self, *args) - - def acyclic(self, *args): - r'''From Giac's documentation: - Help for acyclic: - acyclic(Opt) - Option for the random_network command. - See also: 1/ random_network - ''' - return GiacMethods['acyclic'](self, *args) - - def add(self, *args): - r'''From Giac's documentation: - Help for add: - add(Expr,Var,VarMin(a),VarMax(b),[VarStep(p)]) - Discrete sum (with 2 or 4 arguments return then sum from a to b if a<=b or of the opposite of the sum from b+1 to a-1 if a>b+1 or 0 if a=b+1) or the discrete primitive or sum of the elements of a list or a sequence. - See also: 1/ + - Ex1:add(1/n^2,n,1,17) - Ex2:add(1/n^2,n=1..17) - Ex3:add(1/n^2,n,17,1) - Ex4:add(1/n^2,n=17..1) - Ex5:add(1/n^2,n,17,1,1) - Ex6:add(1/n^2,n,1,17,2) - Ex7:add(1,2,3,4) - Ex8:add([[1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,9]]) - Ex9:add(1/(x*(x+1)),x) - Ex10:add(cos(n*x),n) - ''' - return GiacMethods['add'](self, *args) - - def add_arc(self, *args): - r'''From Giac's documentation: - Help for add_arc: - add_arc(Graph(G),Edge(e)||Trail(T)||Lst(E)) - Returns a modified copy of digraph G with added arc e (or trail T or list of arcs E). - See also: 1/ add_edge 2/ delete_arc 3/ digraph 4/ edges 5/ has_arc 6/ trail - Ex1:add_arc(digraph(trail(1,2,3,4,5,1)),[[1,3],[2,4]]) - ''' - return GiacMethods['add_arc'](self, *args) - - def add_edge(self, *args): - r'''From Giac's documentation: - Help for add_edge: - add_edge(Graph(G),Edge(e)||Trail(T)||Lst(E)) - Returns a modified copy of undirected graph G with added edge e (or trail T or list of edges E). - See also: 1/ add_arc 2/ delete_edge 3/ edges 4/ graph 5/ has_edge 6/ trail - Ex1:add_edge(graph(trail(1,2,3,4)),[4,1]) - ''' - return GiacMethods['add_edge'](self, *args) - - def add_vertex(self, *args): - r'''From Giac's documentation: - Help for add_vertex: - add_vertex(Graph(G),Vrtx(v)||Lst(V)) - Returns a modified copy of G with added vertex v [or vertices from list V]. - See also: 1/ add_arc 2/ add_edge 3/ delete_vertex - Ex1:add_vertex(cycle_graph(5),["a","b"]) - ''' - return GiacMethods['add_vertex'](self, *args) - - def additionally(self, *args): - r'''From Giac's documentation: - Help for additionally: - additionally(Expr) - Makes an additionally assumption on a variable. - See also: 1/ purge 2/ about 3/ assume - Ex1: assume(n,integer);additionally(n>5) - Ex2: assume(n,integer);assume(n>=2,additionally) - ''' - return GiacMethods['additionally'](self, *args) - - def addtable(self, *args): - r'''From Giac's documentation: - Help for addtable: - addtable(fourier||laplace,f(x),F(s),Var(x),Var(s)) - Stores an unknown Fourier/Laplace transform pair (f,F). - See also: 1/ fourier 2/ laplace - Ex1:addtable(fourier,y(x),Y(s),x,s) - ''' - return GiacMethods['addtable'](self, *args) - - def adjacency_matrix(self, *args): - r'''From Giac's documentation: - Help for adjacency_matrix: - adjacency_matrix(Graph(G)) - Returns the adjacency matrix of G (rows and columns are indexed by the vertices). - See also: 1/ neighbors - Ex1:adjacency_matrix(graph(trail(1,2,3,4,2,5,1,3))) - ''' - return GiacMethods['adjacency_matrix'](self, *args) - - def adjoint_matrix(self, *args): - r'''From Giac's documentation: - Help for adjoint_matrix: - adjoint_matrix(Mtrx) - Returns the characteristic polynomial of A and the comatrix of A-xI. - See also: 1/ pcar - Ex1:adjoint_matrix([[1,i],[2,3]]) - ''' - return GiacMethods['adjoint_matrix'](self, *args) - - def affix(self, *args): - r'''From Giac's documentation: - Help for affix: - affix(Pnt||Vect) - Complex number equal to the affix of a point or of a vector. - See also: 1/ point 2/ vector - Ex1:affix(point(i)) - Ex2:affix(point(i)-point(1+2*i)) - Ex3:affix([1,2]) - ''' - return GiacMethods['affix'](self, *args) - - def algsubs(self, *args): - r'''From Giac's documentation: - Help for algsubs: - algsubs(Equal(Xpr1=Xpr2),Expr(Xpr)) - Substitutes in the expression Xpr, the algebraic expression Xpr1 by the algebraic expression Xpr2. - See also: 1/ subst 2/ subs - Ex1:algsubs(x^2=u,1+x^2+x^4) - Ex2:algsubs(a*b/c=d, 2*a*b^2/c) - Ex3:algsubs(2a=p^2-q^2,algsubs(2c=p^2+q^2,c^2-a^2)) - ''' - return GiacMethods['algsubs'](self, *args) - - def algvar(self, *args): - r'''From Giac's documentation: - Help for algvar: - algvar(Expr) - List of the variables by ascending algebraic extension order. - See also: 1/ lvar 2/ lname - Ex1:algvar(sqrt(x)+y) - ''' - return GiacMethods['algvar'](self, *args) - - def all_trig_solutions(self, *args): - r'''From Giac's documentation: - Help for all_trig_solutions: - all_trig_solutions(:=Intg(0 or 1)) - Pseudo-variable to return the general solution (all_trig_solutions:=1) or principal solution (all_trig_solutions:=0). - See also: 1/ cas_setup - Ex1: all_trig_solutions:=1 - Ex2: all_trig_solutions:=0 - ''' - return GiacMethods['all_trig_solutions'](self, *args) - - def allpairs_distance(self, *args): - r'''From Giac's documentation: - Help for allpairs_distance: - allpairs_distance(Graph(G)) - Returns a square matrix D of order equal to the number of vertices in G such that D(i,j) is the distance between i-th and j-th vertex of the (weighted) graph G. - See also: 1/ dijkstra 2/ graph_diameter 3/ vertex_distance - Ex1:allpairs_distance(graph(%{[1,2],[1,3],[1,4],[1,5],[2,3],[3,4],[4,5],[5,2]%})) - ''' - return GiacMethods['allpairs_distance'](self, *args) - - def alog10(self, *args): - r'''From Giac's documentation: - Help for alog10: - alog10(Expr) - Function x->10^x. - See also: 1/ log10 - Ex1:alog10(3) - ''' - return GiacMethods['alog10'](self, *args) - - def altitude(self, *args): - r'''From Giac's documentation: - Help for altitude: - altitude((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - altitude(A,B,C) draws the altitude through A of the triangle ABC. - See also: 1/ perpendicular 2/ orthogonal 3/ orthocenter 4/ common_perpendicular - Ex1:altitude(-1,1-i,i) - ''' - return GiacMethods['altitude'](self, *args) - - def angle(self, *args): - r'''From Giac's documentation: - Help for angle: - angle((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - angle(A,B,C) is the value of the measure of the angle (AB,AC). - See also: 1/ triangle 2/ bissector 3/ legend 4/ labels 5/ angleat 6/ angleatraw - Ex1:angle(point(0),point(i),point(1)) - Ex2:angle(0,1,i) - Ex3:angle(0,1,i,"") - Ex4:angle(0,1,i,"a") - Ex5:angle(i,1,1+i,"b") - ''' - return GiacMethods['angle'](self, *args) - - def angle_radian(self, *args): - r'''From Giac's documentation: - Help for angle_radian: - angle_radian(:=Intg(0 or 1)) - Pseudo-variable to work with radians (angle_radian:=1) or degrees (angle_radian:=0). - See also: 1/ cas_setup - Ex1: angle_radian:=1 - Ex2: angle_radian:=0 - ''' - return GiacMethods['angle_radian'](self, *args) - - def angleat(self, *args): - r'''From Giac's documentation: - Help for angleat: - angleat(Pnt(A),Pnt(B),Pnt(C),(Pnt or Cplx(z0))) - angleat(A,B,C,z0) displays at point(z0) with a legend, the value of the measure of the angle (AB,AC). - See also: 1/ angle 2/ angleatraw 3/ legend - Ex1: A:=point(0);B:=point(1);C:=point(i);angleat(A,B,C,-2-i) - ''' - return GiacMethods['angleat'](self, *args) - - def angleatraw(self, *args): - r'''From Giac's documentation: - Help for angleatraw: - angleatraw(Pnt(A)),Pnt(B),Pnt(C),(Pnt or Cplx(z0))) - angleatraw(A,B,C,z0) displays at point(z0), the value of the measure of the angle (AB,AC). - See also: 1/ angle 2/ angleat - Ex1: A:=point(0);B:=point(1);C:=point(i);angleatraw(A,B,C,-2-i) - ''' - return GiacMethods['angleatraw'](self, *args) - - def ans(self, *args): - r'''From Giac's documentation: - Help for ans: - ans(Intg(n)) - Returns the (n+1)th answer of the command history if n>=0 or, the (-n)th previous answer if n<0 (by default n=-1 for the previous answer). - See also: 1/ quest - Ex1:ans() - Ex2:ans(2) - Ex3:ans(-2) - ''' - return GiacMethods['ans'](self, *args) - - def antiprism_graph(self, *args): - r'''From Giac's documentation: - Help for antiprism_graph: - antiprism_graph(Intg(n)) - Returns the antiprism graph of order n. - See also: 1/ prism_graph - Ex1:antiprism_graph(5) - ''' - return GiacMethods['antiprism_graph'](self, *args) - - def append(self, *args): - r'''From Giac's documentation: - Help for append: - append((Lst||Set||Str(L),Elem)) - Append an element to a set or at the end of a list or of a string (L:=append(L,a) or L.append(a)). - See also: 1/ concat 2/ prepend - Ex1:append([1,2,4],6) - Ex2:append(%{1,2,4%},6) - Ex3: L:=[1,2,4];L:=append(L,6) - Ex4: L:=[1,2,4];L.append(6) - Ex5: S:=set[1,2,4];S:=append(S,6) - Ex6: S:=set[1,2,4];S.append(6) - ''' - return GiacMethods['append'](self, *args) - - def apply(self, *args): - r'''From Giac's documentation: - Help for apply: - apply(Fnc(f),Lst(l)) - Applies the function f at the elements of the list l (option matrix for a matrix). - See also: 1/ map 2/ unapply 3/ matrix - Ex1:apply(x->x^3,[1,2,3]) - Ex2:apply(x->x+1,[[1,2,3],[1,2,3]],matrix) - ''' - return GiacMethods['apply'](self, *args) - - def approx(self, *args): - r'''From Giac's documentation: - Help for approx: - approx(Expr,[Int]) - Numerical evaluation of the first argument (we can give the number of digits as second argument). - See also: 1/ evalb 2/ eval - Ex1:approx(2/3) - Ex2:approx(2/3,2) - Ex3:approx(2*sin(1)) - Ex4:approx(2*sin(1),40) - Ex5:approx(sqrt(2)+pi) - Ex6:approx(sqrt(2)+pi,30) - ''' - return GiacMethods['approx'](self, *args) - - def arc(self, *args): - r'''From Giac's documentation: - Help for arc: - arc(Pnt, Pnt, Real,[Var(C)],[Var(r)],[Opt(segment)]) - Draws a circular arc given by 2 vertices and the central angle [Xcas will put the center in C and the radius in r]. - See also: 1/ circle 2/ segment 3/ plotparam - Ex1:arc(0,1,pi/4) - Ex2:arc(i,1,pi/4,C,r) - Ex3:arc(i,1,pi/4,segment) - Ex4:arc(i,1,pi/4,segment,affichage=1+rempli) - ''' - return GiacMethods['arc'](self, *args) - - def arcLen(self, *args): - r'''From Giac's documentation: - Help for arcLen: - arcLen(Expr(Xpr) or Lst([Xpr1,Xpr2]),Var,Real(a),Real(b)) - Returns the length of the arc of the curve defined by y=Xpr(or by x=Xpr1,y=Xpr2) when the parameter values are between a and b. - See also: 1/ int - Ex1:arcLen(t^2,t,1,2) - Ex2:arcLen([t,t^2],t,1,2) - Ex3:arcLen([cos(t),sin(t)],t,1,2) - ''' - return GiacMethods['arcLen'](self, *args) - - def arccos(self, *args): - r'''From Giac's documentation: - Help for arccos: - arccos(Expr) - Arccosine. - See also: 1/ cos 2/ acosh - Ex1:arccos(0) - ''' - return GiacMethods['arccos'](self, *args) - - def arccosh(self, *args): - r'''From Giac's documentation: - Help for arccosh: - arccosh(Expr) - Hyperbolic arccosine. - See also: 1/ cosh 2/ acos - Ex1:arccosh(1) - ''' - return GiacMethods['arccosh'](self, *args) - - def arclen(self, *args): - r'''From Giac's documentation: - Help for arclen: - arclen(Expr(Xpr) or Lst([Xpr1,Xpr2]),Var,Real(a),Real(b)) - Returns the length of the arc of the curve defined by y=Xpr(or by x=Xpr1,y=Xpr2) when the parameter values are between a and b. - See also: 1/ int - Ex1:arclen(t^2,t,1,2) - Ex2:arclen([t,t^2],t,1,2) - Ex3:arclen([cos(t),sin(t)],t,1,2) - ''' - return GiacMethods['arclen'](self, *args) - - def arcsin(self, *args): - r'''From Giac's documentation: - Help for arcsin: - arcsin(Expr) - Arcsine. - See also: 1/ sin - Ex1:arcsin(0) - ''' - return GiacMethods['arcsin'](self, *args) - - def arcsinh(self, *args): - r'''From Giac's documentation: - Help for arcsinh: - arcsinh(Expr) - Hyperbolic arcsine. - See also: 1/ sinh 2/ asin - Ex1:arcsinh(0) - ''' - return GiacMethods['arcsinh'](self, *args) - - def arctan(self, *args): - r'''From Giac's documentation: - Help for arctan: - arctan(Expr) - Arctangent. - See also: 1/ tan 2/ atanh - Ex1:arctan(0) - ''' - return GiacMethods['arctan'](self, *args) - - def arctanh(self, *args): - r'''From Giac's documentation: - Help for arctanh: - arctanh(Expr) - Hyperbolic arctangent. - See also: 1/ atan 2/ tanh - Ex1:arctanh(0) - ''' - return GiacMethods['arctanh'](self, *args) - - def area(self, *args): - r'''From Giac's documentation: - Help for area: - area(Polygone || Expr,x=a..b,[n],[Method]) - Algebraic area of a circle, a circular arc or of a (star) polygon (e.g. triangle, square, ...) or of the area below a curve, optionally with a quadrature method (trapezoid,left_rectangle,right_rectangle,middle_point,simpson,rombergt,rombergm). - See also: 1/ trapezoid 2/ perimeter 3/ areaatraw 4/ areaat 5/ areaplot - Ex1:area(triangle(0,1,i)) - Ex2:area(square(0,2)) - Ex3:area(circle(0,2)) - Ex4:area(0,1,i) - Ex5:area(x^2,x=0..1,5,trapezoid) - Ex6:area(x^2,x=0..1,5,simpson) - Ex7:area(x^2,x=0..1,5,rombergm) - ''' - return GiacMethods['area'](self, *args) - - def areaat(self, *args): - r'''From Giac's documentation: - Help for areaat: - areaat(Polygone, Pnt||Cplx(z0)) - Displays at point(z0), with a legend, the algebraic area of a circle or of a (star) polygon (e.g. triangle, square, ...) - See also: 1/ area 2/ areaatraw 3/ polygon 4/ perimeteratraw 5/ areaplot - Ex1: t:=triangle(0,1,i);areaat(t,(1+i)/2) - Ex2: c:=square(0,2);areaat(c,1+i) - Ex3: c2:=circle(0,2);areaat(c2,1+i) - Ex4: p:=polygon(0,1,i);areaat(p,1+i) - Ex5: A:=point(0);B:=point(1+i);c:=carre(A,B);areaat(c,i) - ''' - return GiacMethods['areaat'](self, *args) - - def areaatraw(self, *args): - r'''From Giac's documentation: - Help for areaatraw: - areaatraw(Polygone, Pnt||Cplx(z0)) - Displays at point(z0), the algebraic area of a circle or of a (star-)polygon (e.g. triangle, square, ...) - See also: 1/ area 2/ areaat 3/ polygon 4/ perimeteratraw 5/ areaplot - Ex1:areaatraw(triangle(0,1,i),(1+i)/2) - Ex2:areaatraw(square(0,2),1+i) - Ex3:areaatraw(circle(0,2),1+i) - Ex4:areaatraw(polygon(0,1,i),1+i) - Ex5: A:=point(0);B:=point(1+i);c:=carre(A,B);areaatraw(c,i) - ''' - return GiacMethods['areaatraw'](self, *args) - - def areaplot(self, *args): - r'''From Giac's documentation: - Help for areaplot: - areaplot(Expr,x=a..b,[n],[Method]) - Displays the area below a curve, optionally with a quadrature method (trapezoid,left_rectangle,right_rectangle,middle_point). - See also: 1/ integrate 2/ plot 3/ area 4/ areaat 5/ areaatraw - Ex1:areaplot(sin(x),x=0..pi) - Ex2:areaplot(x^2,x=0..1,5,trapezoid) - Ex3:areaplot(x^2,x=0..1,5,middle_point) - ''' - return GiacMethods['areaplot'](self, *args) - - def arg(self, *args): - r'''From Giac's documentation: - Help for arg: - arg(Expr) - Returns the argument of a complex number. - See also: 1/ abs - Ex1:arg(1+i) - Ex2:arg(1+2*i) - Ex3:arg((1+2*i)^2) - ''' - return GiacMethods['arg'](self, *args) - - def array(self, *args): - r'''From Giac's documentation: - Help for array: - array(Opt) - Option for convert for definitions of sparse matrices. - See also: 1/ convert 2/ table - Ex1: A[0..2,0..2]:=1;A[0..1,1..2]:=2;convert(A,array) - Ex2: B[0..1,1..2]:=1;B[2,2]:=2;convert(B,array) - ''' - return GiacMethods['array'](self, *args) - - def arrivals(self, *args): - r'''From Giac's documentation: - Help for arrivals: - arrivals(Graph(G),[Vrtx(v)]) - Returns the list of vertices in digraph G which are connected by v with arcs such that heads are in v. If v is omitted, a list of arrivals is computed for every vertex in G. - See also: 1/ in_degree - Ex1:arrivals(digraph(%{[1,2],[1,3],[2,3]%}),1) - ''' - return GiacMethods['arrivals'](self, *args) - - def articulation_points(self, *args): - r'''From Giac's documentation: - Help for articulation_points: - articulation_points(Graph(G)) - Returns the list of articulation points (cut vertices) of G. - See also: 1/ biconnected_components 2/ is_biconnected 3/ is_connected 4/ is_triconnected - Ex1:articulation_points(path_graph(5)) - Ex2:articulation_points(cycle_graph(5)) - ''' - return GiacMethods['articulation_points'](self, *args) - - def asin(self, *args): - r'''From Giac's documentation: - Help for asin: - asin(Expr) - Arcsine. - See also: 1/ sin - Ex1:asin(0) - ''' - return GiacMethods['asin'](self, *args) - - def asin2acos(self, *args): - r'''From Giac's documentation: - Help for asin2acos: - asin2acos(Expr) - Replaces arcsin(x) by pi/2-arccos(x) in the argument. - See also: 1/ asin2atan - Ex1:asin2acos(acos(x)+asin(x)) - Ex2:asin2acos(2*asin(x)) - ''' - return GiacMethods['asin2acos'](self, *args) - - def asin2atan(self, *args): - r'''From Giac's documentation: - Help for asin2atan: - asin2atan(Expr) - Replaces arcsin(x) by arctan(x/sqrt(1-x^2)) in the argument. - See also: 1/ asin2acos - Ex1:asin2atan(2*asin(x)) - Ex2:asin2atan(asin(sqrt(1-x^2))+asin(x)) - ''' - return GiacMethods['asin2atan'](self, *args) - - def asinh(self, *args): - r'''From Giac's documentation: - Help for asinh: - asinh(Expr) - Hyperbolic arcsine. - See also: 1/ sinh 2/ asin - Ex1:asinh(0) - ''' - return GiacMethods['asinh'](self, *args) - - def assign_edge_weights(self, *args): - r'''From Giac's documentation: - Help for assign_edge_weights: - assign_edge_weights(Graph(G),Seq(m,n)||Intrv(a..b)) - Assigns random edge weights to the edges of G and returns a modified copy of G. If integers n and m such that n>=m are specified, weights are integers randomly chosen in [m,n]. If an interval a..b is specified, weights are uniformly distributed in the interval [a,b). - See also: 1/ set_edge_weight 2/ get_edge_weight 3/ weight_matrix 4/ random_digraph 5/ random_tournament - Ex1:assign_edge_weights(digraph(trail(1,2,3,4,1)),1,9) - Ex2:assign_edge_weights(digraph(trail(1,2,3,4,1)),0..1) - ''' - return GiacMethods['assign_edge_weights'](self, *args) - - def assume(self, *args): - r'''From Giac's documentation: - Help for assume: - assume(Expr) - Makes an assumption on a variable. - See also: 1/ purge 2/ about 3/ additionally - Ex1:assume(a>0) - Ex2:assume(a=0.3) - Ex3:assume(a:=[pi/4,0,pi/2]) - Ex4:assume(a:=[pi/4,0,pi/2,0.1]) - Ex5:assume(n,integer); - Ex6:assume(n,integer);additionally(n>6) - Ex7:assume(a>-10 and a<10) - Ex8:assume((a>=2 and a<4) or a>6) - Ex9:assume(a>=2);additionally(a<6) - Ex10:assume(a) - ''' - return GiacMethods['assume'](self, *args) - - def at(self, *args): - r'''From Giac's documentation: - Help for at: - at(Lst(l)||Mtrx(m),Index(j)||Lst([j,k])) - at(l,j) (or at(m,[j,k])) is the element of the list l (or matrix m) for index=j (or for index j,k). - See also: 1/ of - Ex1:at([10,11,12],1) - Ex2:at([[1,2],[3,4]],[1,0]) - ''' - return GiacMethods['at'](self, *args) - - def atan(self, *args): - r'''From Giac's documentation: - Help for atan: - atan(Expr) - Arctangent. - See also: 1/ tan 2/ atanh - Ex1:atan(0) - ''' - return GiacMethods['atan'](self, *args) - - def atan2acos(self, *args): - r'''From Giac's documentation: - Help for atan2acos: - atan2acos(Expr) - Replaces arctan(x) by pi/2-arccos(x/sqrt(1+x^2)) in the argument. - See also: 1/ atan2acos(atan(x)) - ''' - return GiacMethods['atan2acos'](self, *args) - - def atan2asin(self, *args): - r'''From Giac's documentation: - Help for atan2asin: - atan2asin(Expr) - Replaces arctan(x) by arcsin(x/sqrt(1+x^2)) in the argument. - See also: 1/ atan2asin(atan(x)) - ''' - return GiacMethods['atan2asin'](self, *args) - - def atanh(self, *args): - r'''From Giac's documentation: - Help for atanh: - atanh(Expr) - Hyperbolic arctangent. - See also: 1/ atan 2/ tanh - Ex1:atanh(0) - ''' - return GiacMethods['atanh'](self, *args) - - def atrig2ln(self, *args): - r'''From Giac's documentation: - Help for atrig2ln: - atrig2ln(Expr) - Rewrites the expression containing inverse trigonometric functions into logarithmic functions. - See also: 1/ trig2exp 2/ exp2trig - Ex1:atrig2ln(atan(x)) - Ex2:atrig2ln(asin(x)) - Ex3:atrig2ln(acos(x)) - ''' - return GiacMethods['atrig2ln'](self, *args) - - def augment(self, *args): - r'''From Giac's documentation: - Help for augment: - augment(Lst,Lst||Seq,Seq||Str,Str||Mtrx,Mtrx) - Concatenates two lists or two strings or two sequences or 2 matrices; L:=concat(L,L1) or L.concat(L1). - See also: 1/ append 2/ cat 3/ semi_augment 4/ border 5/ + - Ex1:augment([1,2],[3,4,5]) - Ex2:augment("bon","jour") - Ex3:augment([[1,2],[3,4]],[[4,5,6],[6,7,8]]) - Ex4: L:=[1,2];L.concat([3,4,5]) - Ex5: S:="abcd";S.concat("efghi") - ''' - return GiacMethods['augment'](self, *args) - - def auto_correlation(self, *args): - r'''From Giac's documentation: - Help for auto_correlation: - auto_correlation(Lst) - Returns the cross-correlation of the given signal with itself. - See also: 1/ cross_correlation 2/ correlation - Ex1:auto_correlation([1,-1,0,2,1]) - ''' - return GiacMethods['auto_correlation'](self, *args) - - def autosimplify(self, *args): - r'''From Giac's documentation: - Help for autosimplify: - autosimplify(Cmds) - The argument is a command that Xcas will use to rewrite answers (initial value is regroup and for no simplification it is nop or 0). - See also: 1/ simplify 2/ factor 3/ regroup - Ex1:autosimplify(nop) - Ex2:autosimplify(0) - Ex3:autosimplify(regroup) - Ex4:autosimplify(1) - Ex5:autosimplify(factor) - Ex6:autosimplify(simplify) - ''' - return GiacMethods['autosimplify'](self, *args) - - def avance(self, *args): - r'''From Giac's documentation: - Help for avance: - avance(NULL or Real(n)) - The turtle takes n steps forward (by default n=10). - See also: 1/ recule 2/ saute - Ex1: avance 30 - Ex2:avance(30) - ''' - return GiacMethods['avance'](self, *args) - - def avgRC(self, *args): - r'''From Giac's documentation: - Help for avgRC: - avgRC(Expr(Xpr),Var(Var),[Real(h)]) - Returns (Xpr(var+h)-Xpr(Var))/h (by default h=0.001). - See also: 1/ nDeriv - Ex1:avgRC(f(x),x,h) - Ex2:avgRC(x^2,x,0.1) - Ex3:avgRC(x^2,x) - ''' - return GiacMethods['avgRC'](self, *args) - - def axes(self, *args): - r'''From Giac's documentation: - Help for axes: - axes(Opt) - Global option (Maple compatibility) of a graphic command to put or not the axes. - See also: 1/ line_width 2/ gl_showaxes 3/ switch_axes - Ex1: axes=0;segment(0,point(1,1)) - Ex2: axes=1;segment(0,point(1,1),epaisseur=5) - ''' - return GiacMethods['axes'](self, *args) - - def axis(self, *args): - r'''From Giac's documentation: - Help for axis: - axis(xmin,xmax,ymin,ymax,[zmin,zmaz]) - Defines the graphic display - Ex1:axis(-2,4,-1,6) - ''' - return GiacMethods['axis'](self, *args) - - def back(self, *args): - r'''From Giac's documentation: - Help for back: - back(Vect or Seq or Str) - Returns the last element of a vector or a sequence or a string. - See also: 1/ inter 2/ head 3/ mid 4/ left 5/ right - Ex1:back(1,2,3) - Ex2:back([1,2,3]) - Ex3:back("bonjour") - ''' - return GiacMethods['back'](self, *args) - - def backward(self, *args): - r'''From Giac's documentation: - Help for backward: - backward(NULL or Real(n)) - The turtle takes n steps back (by default n=10). - See also: 1/ avance 2/ saute - Ex1: recule 30 - Ex2:backward(30) - ''' - return GiacMethods['backward'](self, *args) - - def baisse_crayon(self, *args): - r'''From Giac's documentation: - Help for baisse_crayon: - baisse_crayon(NULL) - Presses the pencil down so that the turtle move with traces. - See also: 1/ leve_crayon 2/ crayon - Ex1:baisse_crayon() - ''' - return GiacMethods['baisse_crayon'](self, *args) - - def bandwidth(self, *args): - r'''From Giac's documentation: - Help for bandwidth: - bandwidth(Opt) - Option for the kernel_density command. - See also: 1/ kernel_density 2/ bins - ''' - return GiacMethods['bandwidth'](self, *args) - - def bar_plot(self, *args): - r'''From Giac's documentation: - Help for bar_plot: - bar_plot(Mtrx) - Draws a barplot of a one variable statistical series. - See also: 1/ camembert 2/ histogram 3/ frequencies - Ex1:bar_plot([["France",6],["Allemagne",12],["Suisse",5]]) - Ex2:bar_plot([3/2,2/3,5/4,4/5,7/6,6/7,9/8,8/9,11/10]) - Ex3:bar_plot([[2,"xyz","abc"],["A",2,5],["B",5,6],["C",7,7]]) - ''' - return GiacMethods['bar_plot'](self, *args) - - def barplot(self, *args): - r'''From Giac's documentation: - Help for barplot: - barplot(Mtrx) - Draws a barplot of a one variable statistical series. - See also: 1/ camembert 2/ histogram 3/ frequencies - Ex1:barplot([["France",6],["Allemagne",12],["Suisse",5]]) - Ex2:barplot([3/2,2/3,5/4,4/5,7/6,6/7,9/8,8/9,11/10]) - Ex3:barplot([[2,"xyz","abc"],["A",2,5],["B",5,6],["C",7,7]]) - ''' - return GiacMethods['barplot'](self, *args) - - def bartlett_hann_window(self, *args): - r'''From Giac's documentation: - Help for bartlett_hann_window: - bartlett_hann_window(Lst,[Interval(n1..n2)]) - Applies the Bartlett-Hann windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(bartlett_hann_window(randvector(1000,0..1))) - ''' - return GiacMethods['bartlett_hann_window'](self, *args) - - def barycenter(self, *args): - r'''From Giac's documentation: - Help for barycenter: - barycenter([Pnt,Real],[Pnt,Real],[Pnt,Real]) - barycenter([point1,coeff1],...) draws the barycenter of point1 with weight coeff1... - See also: 1/ isobarycenter 2/ midpoint - Ex1:barycenter([point(-1),1],[point(1+i),2],[point(1-i),1]) - Ex2:barycenter([[point(-1),1],[point(1+i),2],[point(1-i),1]]) - Ex3:barycenter([point(-1),point(1+i),point(1-i)],[1,2,1]) - ''' - return GiacMethods['barycenter'](self, *args) - - def base(self, *args): - r'''From Giac's documentation: - Help for base: - base(Opt) - Option for convert : convert(p,base,b)= [a0,a1,..an] or convert([a0,a1,..an],base,b)=p with p=a0+a1*b+....an*b^(n-1). - See also: 1/ convert 2/ horner 3/ revlist - Ex1: convert(123,base,8) - Ex2: convert([3,7,1],base,8) - Ex3: horner(revlist([3,7,1]),8) - ''' - return GiacMethods['base'](self, *args) - - def basis(self, *args): - r'''From Giac's documentation: - Help for basis: - basis(Lst(vector1,..,vectorn)) - Extracts a basis from a spanning set of vectors. - See also: 1/ ker 2/ ibasis - Ex1:basis([[1,2,3],[4,5,6],[7,8,9],[10,11,12]]) - ''' - return GiacMethods['basis'](self, *args) - - def batons(self, *args): - r'''From Giac's documentation: - Help for batons: - batons(Mtrx) - Draws for k=0..nrows, the segments (xk,0)-(xk,yk) where xk=element row k column 0 and yk=element row k column j (j=1..ncols). - See also: 1/ polygonplot 2/ scatterplot 3/ listplot - Ex1:batons([1,3],[2,5],[3,2]) - Ex2:batons([[1,3],[2,5],[3,2]]) - Ex3:batons([1,2,3],[3,5,2]) - ''' - return GiacMethods['batons'](self, *args) - - def bellman_ford(self, *args): - r'''From Giac's documentation: - Help for bellman_ford: - bellman_ford(Graph(G),Vrtx(s),Vrtx(t)||Lst(T)) - Returns the length of the shortest path resp. paths from s to vertex t resp. vertices in T in weighted graph G. - See also: 1/ dijkstra 2/ shortest_path - Ex1:bellman_ford(graph(%{[[1,2],-1],[[2,3],-3],[[3,4],-7],[[4,5],-3],[[5,6],-3],[[1,6],-3]%}),1,4) - ''' - return GiacMethods['bellman_ford'](self, *args) - - def bernoulli(self, *args): - r'''From Giac's documentation: - Help for bernoulli: - bernoulli(Intg||(Intg,Var)) - bernoulli(n) is the n-th number of Bernoulli and bernoulli(n,x) is the n-th polynomial of Bernoulli and the second argument is the variable. - See also: 1/ - Ex1:bernoulli(6) - Ex2:bernoulli(6,x) - ''' - return GiacMethods['bernoulli'](self, *args) - - def besselJ(self, *args): - r'''From Giac's documentation: - Help for besselJ: - besselJ(Real(x),Int(p)) - besselJ(x,p) returns the Bessel function of the first kind Jp(x). - See also: 1/ BesselJ 2/ BesselY 3/ besselY - Ex1:besselJ(sqrt(2),2) - Ex2:besselJ(sqrt(2),-2) - ''' - return GiacMethods['besselJ'](self, *args) - - def besselY(self, *args): - r'''From Giac's documentation: - Help for besselY: - besselY(Real(x),Int(p)) - besselY(x,p) returns the Bessel function of the second kind Yp(x). - See also: 1/ BesselY 2/ BesselJ 3/ besselJ - Ex1:besselY(sqrt(2),2) - Ex2:besselY(sqrt(2),-2) - ''' - return GiacMethods['besselY'](self, *args) - - def betad(self, *args): - r'''From Giac's documentation: - Help for betad: - betad(Real(a>0),Real(b>0),Real(0<=x<=1)) - Returns the probability density of the Beta law (=Gamma(a+b)*x^(a-1)*(1-x)^(b-1)/(Gamma(a)*Gamma(b))). - See also: 1/ betad_cdf 2/ betad_icdf - Ex1:betad(2.2,1.5,0.8) - ''' - return GiacMethods['betad'](self, *args) - - def betad_cdf(self, *args): - r'''From Giac's documentation: - Help for betad_cdf: - betad_cdf(Real(a>0),Real(b>0),Real(0<=x0<=1),[Real(0<=y0<=1)]) - Returns the probability that a Beta random variable (with a and b as parameters) is less than x0 or between x0 and y0. - See also: 1/ betad 2/ betad_icdf - Ex1:betad_cdf(2,1,0.2) - Ex2:betad_cdf(2,1,0.1,0.3) - ''' - return GiacMethods['betad_cdf'](self, *args) - - def betad_icdf(self, *args): - r'''From Giac's documentation: - Help for betad_icdf: - betad_icdf(Real(a>0),Real(b>0),Real(0<=p<=1)) - Returns h such that the probability that a Beta random variable is less than h is p (0<=p<=1). - See also: 1/ betad_cdf 2/ betad - Ex1:betad_icdf(2,1,0.95) - Ex2:betad_icdf(2,1,0.5) - ''' - return GiacMethods['betad_icdf'](self, *args) - - def betavariate(self, *args): - r'''From Giac's documentation: - Help for betavariate: - betavariate(Real(a),Real(b)) - Returns a random real according to the Beta distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:betavariate(1,2) - Ex2:betavariate(1.5,4) - ''' - return GiacMethods['betavariate'](self, *args) - - def bezier(self, *args): - r'''From Giac's documentation: - Help for bezier: - bezier(Lst,[plot]) - Bezier curve defined by control points. - See also: 1/ parameq - Ex1:bezier(1,1+i,2+i,3-i,plot) - Ex2:bezier(point([0,0,0]),point([1,1,0]),point([0,1,1]),plot) - Ex3: parameq(bezier(1,1+i,2+i,3-i)) - Ex4: parameq(bezier(point([0,0,0]),point([1,1,0]),point([0,1,1]))) - ''' - return GiacMethods['bezier'](self, *args) - - def bezout_entiers(self, *args): - r'''From Giac's documentation: - Help for bezout_entiers: - bezout_entiers(Intg,Intg) - Extended greatest common divisor of 2 integers. - See also: 1/ gcd 2/ iabcuv 3/ egcd - Ex1:bezout_entiers(45,75) - Ex2:bezout_entiers(21,28) - Ex3:bezout_entiers(30,49) - ''' - return GiacMethods['bezout_entiers'](self, *args) - - def biconnected_components(self, *args): - r'''From Giac's documentation: - Help for biconnected_components: - biconnected_components(Graph(G)) - Returns the biconnected components of G as a list of lists of vertices. - See also: 1/ articulation_points 2/ is_biconnected 3/ is_connected 4/ trail - Ex1:biconnected_components(graph(trail(1,2,3,4,2),trail(4,5,6,7,5))) - ''' - return GiacMethods['biconnected_components'](self, *args) - - def binomial(self, *args): - r'''From Giac's documentation: - Help for binomial: - binomial(Intg(n),Intg(k),[Real(p in 0..1)]) - Returns comb(n,k)*p^k*(1-p)^(n-k) or comb(n,k) if no 3rd argument. - See also: 1/ binomial_cdf 2/ binomial_icdf 3/ multinomial 4/ randvector 5/ ranm - Ex1:binomial(4,2) - Ex2:binomial(4,0,0.5) - Ex3:binomial(4,2,0.5) - Ex4: assume(p>=0 and p<=1);binomial(4,2,p) - Ex5: assume(p>=0 and p<=1);binomial(4,p,2) - Ex6: randvector(6,binomial,4,0.2) - Ex7: ranm(4,6,binomial,4,0.7) - ''' - return GiacMethods['binomial'](self, *args) - - def binomial_cdf(self, *args): - r'''From Giac's documentation: - Help for binomial_cdf: - binomial_cdf(Intg(n),Real(p),Real(x),[Real(y)]) - Returns Proba(X<=x) or Proba(x<=X<=y) when X follows the B(n,p) law. - See also: 1/ binomial 2/ binomial_icdf - Ex1:binomial_cdf(4,0.5,2) - Ex2:binomial_cdf(4,0.1,2) - Ex3:binomial_cdf(4,0.5,2,3) - ''' - return GiacMethods['binomial_cdf'](self, *args) - - def binomial_icdf(self, *args): - r'''From Giac's documentation: - Help for binomial_icdf: - binomial_icdf(Intg(n),Real(p),Real(t)) - Returns h such as Proba(X<=h)=t when X follows the B(n,p) law. - See also: 1/ binomial 2/ binomial_cdf - Ex1:binomial_icdf(4,0.5,0.68) - Ex2:binomial_icdf(4,0.1,0.95) - ''' - return GiacMethods['binomial_icdf'](self, *args) - - def bins(self, *args): - r'''From Giac's documentation: - Help for bins: - bins(Opt) - Option for the kernel_density command. - See also: 1/ kernel_density 2/ bandwidth - ''' - return GiacMethods['bins'](self, *args) - - def bipartite(self, *args): - r'''From Giac's documentation: - Help for bipartite: - bipartite(Opt) - Option for the draw_graph command - See also: 1/ draw_graph - ''' - return GiacMethods['bipartite'](self, *args) - - def bipartite_matching(self, *args): - r'''From Giac's documentation: - Help for bipartite_matching: - bipartite_matching(Graph(G)) - Returns the list of edges in a maximum matching of the undirected unweighted bipartite graph G. - See also: 1/ is_bipartite 2/ maximum_matching - Ex1:bipartite_matching(graph("desargues")) - ''' - return GiacMethods['bipartite_matching'](self, *args) - - def bisection_solver(self, *args): - r'''From Giac's documentation: - Help for bisection_solver: - bisection_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['bisection_solver'](self, *args) - - def bisector(self, *args): - r'''From Giac's documentation: - Help for bisector: - bisector((Pnt(A) or Cplx),(Pnt(B) or Cplx),(Pnt(C) or Cplx)) - Draws the bisector of the angle (AB,AC) given by 3 points A,B,C. - See also: 1/ angle 2/ exbisector - Ex1:bisector(0,1,i) - ''' - return GiacMethods['bisector'](self, *args) - - def bit_depth(self, *args): - r'''From Giac's documentation: - Help for bit_depth: - bit_depth(Lst(clip)) - Returns the bit depth of an audio clip. - See also: 1/ channels 2/ channel_data 3/ duration 4/ samplerate - Ex1:bit_depth(readwav("/some/file")) - ''' - return GiacMethods['bit_depth'](self, *args) - - def bitand(self, *args): - r'''From Giac's documentation: - Help for bitand: - bitand(Intg,Intg) - Logical bit and. - See also: 1/ bitxor 2/ bitor - Ex1:bitand(0x12,0x38) - ''' - return GiacMethods['bitand'](self, *args) - - def bitor(self, *args): - r'''From Giac's documentation: - Help for bitor: - bitor(Intg,Intg) - Inclusive logical bit or. - See also: 1/ bitxor 2/ bitand - Ex1:bitor(0x12,0x38) - ''' - return GiacMethods['bitor'](self, *args) - - def bitxor(self, *args): - r'''From Giac's documentation: - Help for bitxor: - bitxor(Intg,Intg) - Exclusive logical bit or. - See also: 1/ bitor 2/ bitand - Ex1:bitxor(0x12,0x38) - ''' - return GiacMethods['bitxor'](self, *args) - - def blackman_harris_window(self, *args): - r'''From Giac's documentation: - Help for blackman_harris_window: - blackman_harris_window(Lst,[Interval(n1..n2)]) - Applies the Blackman-Harris windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ bartlett_hann_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(blackman_harris_window(randvector(1000,0..1))) - ''' - return GiacMethods['blackman_harris_window'](self, *args) - - def blackman_window(self, *args): - r'''From Giac's documentation: - Help for blackman_window: - blackman_window(Lst,[Real(a)],[Interval(n1..n2)]) - Applies the Blackman windowing function with parameter a (by default a=0.16) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ bartlett_harris_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(blackman_window(randvector(1000,0..1))) - ''' - return GiacMethods['blackman_window'](self, *args) - - def blockmatrix(self, *args): - r'''From Giac's documentation: - Help for blockmatrix: - blockmatrix(Intg(n),Intg(m),Lst) - Returns the matrix obtained from the list divided into n lists of dimension m. - See also: 1/ list2mat - Ex1:blockmatrix(2,3,[idn(2),idn(2),idn(2),idn(2),idn(2),idn(2)]) - Ex2:blockmatrix(2,2,[idn(2),newMat(2,3),newMat(3,2),idn(3)]) - ''' - return GiacMethods['blockmatrix'](self, *args) - - def bohman_window(self, *args): - r'''From Giac's documentation: - Help for bohman_window: - bohman_window(Lst,[Interval(n1..n2)]) - Applies the Bohman windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bartlett_hann_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(bohman_window(randvector(1000,0..1))) - ''' - return GiacMethods['bohman_window'](self, *args) - - def border(self, *args): - r'''From Giac's documentation: - Help for border: - border(Mtrx(A),Lst(b)) - Returns the matrix obtained by augmenting A with b as the last column, if nrows(A)=size(b), border(A,b)=tran(append(tran(A),b)). - See also: 1/ tran 2/ append 3/ augment - Ex1:border([[1,2,3,4],[4,5,6,8],[7,8,9,10]],[1,3,5]) - Ex2:border([[1,2,3],[4,5,6],[7,8,9]],[1,0,1]) - ''' - return GiacMethods['border'](self, *args) - - def boxcar(self, *args): - r'''From Giac's documentation: - Help for boxcar: - boxcar(Real(a),Real(b),Expr(x)) - Returns the value at x of the boxcar function corresponding to a and b. - See also: 1/ rect 2/ Heaviside - Ex1:boxcar(1,2,x) - ''' - return GiacMethods['boxcar'](self, *args) - - def boxwhisker(self, *args): - r'''From Giac's documentation: - Help for boxwhisker: - boxwhisker(Lst,[Lst],[x=a..b||y=a..b]) - Box and Whisker plot for a statistical series. - See also: 1/ quartiles - Ex1:boxwhisker([-1,1,2,2.2,3,4,-2,5]) - Ex2:boxwhisker([1,2,3,5,10,4],x=1..2) - Ex3:boxwhisker([1,2,3,5,10,4],[1,2,3,1,2,3]) - Ex4:boxwhisker([[6,0,1,3,4,2,5],[0,1,3,4,2,5,6],[1,3,4,2,5,6,0],[3,4,2,5,6,0,1],[4,2,5,6,0,1,3],[2,5,6,0,1,3,4]]) - ''' - return GiacMethods['boxwhisker'](self, *args) - - def brent_solver(self, *args): - r'''From Giac's documentation: - Help for brent_solver: - brent_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['brent_solver'](self, *args) - - def bvpsolve(self, *args): - r'''From Giac's documentation: - Help for bvpsolve: - bvpsolve(Expr(f(x,y,y')),Lst(x=a..b,y),Lst(y(a),y(b),[y'(1)]),[options]) - Returns an approximation of the function y (and optionally of y') on the interval a..b. - See also: 1/ odesolve - Ex1:bvpsolve((32+2x^3-y*diff(y(x),x))/8,[x=1..3,y],[17,43/3],20) - Ex2:bvpsolve((x^2*diff(y(x),x)^2-9y^2+4x^6)/x^5,[x=1..2,y],[0,ln(256),1],10,output=spline) - ''' - return GiacMethods['bvpsolve'](self, *args) - - def cFactor(self, *args): - r'''From Giac's documentation: - Help for cFactor: - cFactor(Expr) - Factorization of the expression in ℂ (on the Gaussian integers if there are more than 2 variables). - See also: 1/ factor - Ex1:cFactor(x^2*y+y) - Ex2:cFactor(x^2*y^2+y^2+4*x^2+4) - Ex3:cFactor(x^2*y^2+y^2+2*x^2+2) - ''' - return GiacMethods['cFactor'](self, *args) - - def cSolve(self, *args): - r'''From Giac's documentation: - Help for cSolve: - cSolve(LstEq,LstVar) - Returns the list of complex solutions of an equation or a matrix where the rows are ℂ-solutions of a system of polynomial equations. - See also: 1/ cZeros 2/ solve 3/ fslove - Ex1:cSolve(x^4-1,x) - Ex2:cSolve(x^4-y^4 and x+y=2,[x,y]) - Ex3:cSolve(x^4-y^4 and x+y=0 and x^2=2*x,[x,y]) - Ex4:cSolve(u*v-u=v and v^2=u,[u,v]) - ''' - return GiacMethods['cSolve'](self, *args) - - def cZeros(self, *args): - r'''From Giac's documentation: - Help for cZeros: - cZeros(Expr(Xpr)||LstExpr, [Var||LstVar]) - Returns the list of complex solutions of Xpr=0 or the matrix where the rows are the solutions of the system : Xpr1=0,Xpr2=0... - See also: 1/ solve - Ex1:cZeros(x^2-1) - Ex2:cZeros([x^2-1,x^2-y^2],[x,y]) - ''' - return GiacMethods['cZeros'](self, *args) - - def camembert(self, *args): - r'''From Giac's documentation: - Help for camembert: - camembert(Mtrx) - Draws pie chart of a one variable statistical series. - See also: 1/ bar_plot - Ex1:camembert([["France",6],["Allemagne",12],["Suisse",5]]) - Ex2:camembert([3/2,2/3,5/4,4/5,7/6,6/7,9/8,8/9,11/10]) - Ex3:camembert([[2,"xyz","abc"],["A",2,5],["B",5,6],["C",7,7]]) - ''' - return GiacMethods['camembert'](self, *args) - - def canonical_form(self, *args): - r'''From Giac's documentation: - Help for canonical_form: - canonical_form(Trinom(a*x^2+b*x+c),[Var]) - Canonical_form of a 2nd degree polynomial. - See also: 1/ - Ex1:canonical_form(2*x^2-12*x+1) - Ex2:canonical_form(2*a^2-12*a+1,a) - ''' - return GiacMethods['canonical_form'](self, *args) - - def canonical_labeling(self, *args): - r'''From Giac's documentation: - Help for canonical_labeling: - canonical_labeling(Graph(G)) - Returns the permutation representing the canonical labeling of G. - See also: 1/ isomorphic_copy 2/ relabel_vertices - Ex1:canonical_labeling(graph("petersen")) - ''' - return GiacMethods['canonical_labeling'](self, *args) - - def cartesian_product(self, *args): - r'''From Giac's documentation: - Help for cartesian_product: - cartesian_product(Seq(G1,G2,..)) - Returns the Cartesian product of graphs G1, G2, ... with vertices labelled as "u:v:..." where u, v, ... are vertices from G1, G2, ..., respectively. - See also: 1/ tensor_product - Ex1:cartesian_product(graph(trail(1,2,3,4,5,2)),star_graph(3)) - ''' - return GiacMethods['cartesian_product'](self, *args) - - def cauchy(self, *args): - r'''From Giac's documentation: - Help for cauchy: - cauchy(Real(x0),Real(a),Real(x)) - Returns the density of probability at x of the Cauchy law with parameters x0 and a (by default x0=0 and a=1). - See also: 1/ cauchy_cdf 2/ cauchy_icdf - Ex1:cauchy(0.0,2.0,1.0) - ''' - return GiacMethods['cauchy'](self, *args) - - def cauchy_cdf(self, *args): - r'''From Giac's documentation: - Help for cauchy_cdf: - cauchy_cdf(Real(x0),Real(a),Real(x),[Real(y)]) - Returns the probability that a Cauchy random variable is less than x. - See also: 1/ cauchyd 2/ cauchy_icdf - Ex1:cauchy_cdf(0.0,2.0,2.1) - Ex2:cauchy_cdf(2,3,-1.9,1.4) - ''' - return GiacMethods['cauchy_cdf'](self, *args) - - def cauchy_icdf(self, *args): - r'''From Giac's documentation: - Help for cauchy_icdf: - cauchy_icdf(Real(x0),Real(a),Real(p)) - Returns h such that the probability that a Cauchy random variable is less than h is p (0<=p<=1). - See also: 1/ cauchy_cdf 2/ cauchy - Ex1:cauchy_icdf(0.0,2.0,0.95) - ''' - return GiacMethods['cauchy_icdf'](self, *args) - - def cauchyd(self, *args): - r'''From Giac's documentation: - Help for cauchyd: - cauchyd(Real(x0),Real(a),Real(x)) - Returns the density of probability at x of the Cauchy law with parameters x0 and a (by default x0=0 and a=1). - See also: 1/ cauchy_cdf 2/ cauchy_icdf - Ex1:cauchyd(0.0,2.0,1.0) - ''' - return GiacMethods['cauchyd'](self, *args) - - def cauchyd_cdf(self, *args): - r'''From Giac's documentation: - Help for cauchyd_cdf: - cauchyd_cdf(Real(x0),Real(a),Real(x),[Real(y)]) - Returns the probability that a Cauchy random variable is less than x. - See also: 1/ cauchyd 2/ cauchy_icdf - Ex1:cauchyd_cdf(0.0,2.0,2.1) - Ex2:cauchyd_cdf(2,3,-1.9,1.4) - ''' - return GiacMethods['cauchyd_cdf'](self, *args) - - def cauchyd_icdf(self, *args): - r'''From Giac's documentation: - Help for cauchyd_icdf: - cauchyd_icdf(Real(x0),Real(a),Real(p)) - Returns h such that the probability that a Cauchy random variable is less than h is p (0<=p<=1). - See also: 1/ cauchy_cdf 2/ cauchy - Ex1:cauchyd_icdf(0.0,2.0,0.95) - ''' - return GiacMethods['cauchyd_icdf'](self, *args) - - def cdf(self, *args): - r'''From Giac's documentation: - Help for cdf: - cdf(Func,FuncParams) - Cumulative distribution function. - See also: 1/ icdf 2/ binomial_cdf 3/ normald_cdf 4/ plotcdf - Ex1:cdf(binomial,10,0.5,4) - Ex2:cdf(normald,0.0,1.0,2.0) - Ex3:cdf([1,3,4,3,5,6],4) - Ex4:cdf([1,3,4,3,5,6],plot) - ''' - return GiacMethods['cdf'](self, *args) - - def ceil(self, *args): - r'''From Giac's documentation: - Help for ceil: - ceil(Real or Cplx) - Returns the smallest integer >= to the argument. - See also: 1/ floor 2/ round - Ex1:ceil(-4.2) - Ex2:ceil(4.3+2.4*i) - ''' - return GiacMethods['ceil'](self, *args) - - def ceiling(self, *args): - r'''From Giac's documentation: - Help for ceiling: - ceiling(Real or Cplx) - Returns the smallest integer >= to the argument. - See also: 1/ floor 2/ round - Ex1:ceiling(-4.2) - Ex2:ceiling(4.3+2.4*i) - ''' - return GiacMethods['ceiling'](self, *args) - - def center(self, *args): - r'''From Giac's documentation: - Help for center: - center(Crcle) - Shows the center of a circle. - See also: 1/ circle 2/ radius - Ex1:center(circle(1+i,2)) - Ex2:center(circumcircle(0,1,1+i)) - ''' - return GiacMethods['center'](self, *args) - - def center2interval(self, *args): - r'''From Giac's documentation: - Help for center2interval: - center2interval(LstVal(l),[Real(a0)]) - Returns the list of intervals beginning with a0 and with l as centers. - See also: 1/ interval2center - Ex1:center2interval([2,5,9],1) - Ex2:center2interval([2,5,8]) - ''' - return GiacMethods['center2interval'](self, *args) - - def centered_cube(self, *args): - r'''From Giac's documentation: - Help for centered_cube: - centered_cube(Pnt(A),Pnt(B),Pnt(C)) - Draws the direct cube with center A, vertex B and such that the plane ABC contains an axis of symmetry of the cube. - See also: 1/ parallelepiped 2/ cube 3/ icosahedron 4/ dodecahedron 5/ octahedron 6/ centered_tetrahedron - Ex1:centered_cube([0,0,0],[3,0,0],[0,0,1]) - Ex2:centered_cube(evalf([0,0,0],[3,2,4],[1,1,0])) - ''' - return GiacMethods['centered_cube'](self, *args) - - def centered_tetrahedron(self, *args): - r'''From Giac's documentation: - Help for centered_tetrahedron: - centered_tetrahedron(Pnt(A),Pnt(B),Pnt(C)) - Draws the regular direct pyramid with center A, vertex B and a vertex in the plane (A,B,C). - See also: 1/ cube 2/ tetrahedron 3/ icosahedron 4/ dodecahedron 5/ octahedron - Ex1:centered_tetrahedron([0,0,0],[3,0,0],[0,1,0]) - Ex2:centered_tetrahedron(evalf([0,0,0],[3,2,4],[1,1,0])) - ''' - return GiacMethods['centered_tetrahedron'](self, *args) - - def cfactor(self, *args): - r'''From Giac's documentation: - Help for cfactor: - cfactor(Expr) - Factorization of the expression in ℂ (on the Gaussian integers if there are more than 2 variables). - See also: 1/ factor - Ex1:cfactor(x^2*y+y) - Ex2:cfactor(x^2*y^2+y^2+4*x^2+4) - Ex3:cfactor(x^2*y^2+y^2+2*x^2+2) - ''' - return GiacMethods['cfactor'](self, *args) - - def cfsolve(self, *args): - r'''From Giac's documentation: - Help for cfsolve: - cfsolve(Expr,Var,[Guess or Interval],[Method]) - Numerical solution of an equation or a system of equations on ℂ. - See also: 1/ fsolve 2/ nSolve 3/ csolve 4/ solve - Ex1:cfsolve(cos(x)=2) - Ex2:cfsolve([x^2+y+2,x+y^2+2],[x,y]) - ''' - return GiacMethods['cfsolve'](self, *args) - - def changebase(self, *args): - r'''From Giac's documentation: - Help for changebase: - changebase(Mtrx(A),Mtrx(P)) - Returns the matrix B=inv(P)*A*P. - See also: 1/ - Ex1:changebase([[1,2],[1,3]],[[1,1],[0,1]]) - Ex2:changebase([[1,2],[1,3]],[[1,0],[1,1]]) - ''' - return GiacMethods['changebase'](self, *args) - - def channel_data(self, *args): - r'''From Giac's documentation: - Help for channel_data: - channel_data(Lst(clip),[Intg(chn) or matrix],[range=a..b]) - Extracts the data from an audio clip (optionally specifying the channel and range). - See also: 1/ bit_depth 2/ channels 3/ duration 4/ samplerate - Ex1:channel_data(readwav("/some/file")) - Ex2:channel_data(readwav("/some/file"),matrix) - Ex3:channel_data(readwav("/some/file"),2) - Ex4:channel_data(readwav("/some/file"),matrix,range=1.0..2.5) - ''' - return GiacMethods['channel_data'](self, *args) - - def channels(self, *args): - r'''From Giac's documentation: - Help for channels: - channels(Lst(clip)) - Returns the number of channels in audio clip. - See also: 1/ bit_depth 2/ channel_data 3/ duration 4/ samplerate - Ex1:channels(readwav("/some/file")) - ''' - return GiacMethods['channels'](self, *args) - - def char(self, *args): - r'''From Giac's documentation: - Help for char: - char(Intg or Lst(Intg)) - Returns the string corresponding to the character code of the argument. - See also: 1/ asc 2/ ord - Ex1:char(65) - Ex2:char([65,66,67]) - ''' - return GiacMethods['char'](self, *args) - - def charpoly(self, *args): - r'''From Giac's documentation: - Help for charpoly: - charpoly(Mtrx,[Var]) - List of the coefficients of the characteristic polynomial of a matrix or characteristic polynomial of a matrix with the second argument as variable. - See also: 1/ jordan 2/ egv 3/ egvl 4/ companion 5/ rat_jordan 6/ pmin 7/ adjoint_matrix - Ex1:charpoly([[1,2],[3,4]]) - Ex2:charpoly([[1,2],[3,4]],x) - Ex3:charpoly([[1,2,3],[1,3,6],[2,5,7]]) - Ex4:charpoly([[1,2,3],[1,3,6],[2,5,7]],z) - ''' - return GiacMethods['charpoly'](self, *args) - - def chinrem(self, *args): - r'''From Giac's documentation: - Help for chinrem: - chinrem([Lst||Expr,Lst||Expr],[Lst||Expr,Lst||Expr]) - Chinese remainder for polynomials written as lists or no. - See also: 1/ ichinrem - Ex1:chinrem([x+2,x^2+1],[x+1,x^2+x+1]) - Ex2:chinrem([[1,2],[1,0,1]],[[1,1],[1,1,1]]) - ''' - return GiacMethods['chinrem'](self, *args) - - def chisquare(self, *args): - r'''From Giac's documentation: - Help for chisquare: - chisquare(Intg(n),Real(x0)) - Returns the probability density of the Chi^2 law at x0 (n is the number of degrees of freedom). - See also: 1/ chisquare_cdf 2/ chisquare_icdf 3/ randvector 4/ ranm - Ex1:chisquare(2,3.2) - Ex2:chisquare(4,10.5) - Ex3: randvector(3,chisquare,2) - Ex4: ranm(4,3,chisquare,2) - ''' - return GiacMethods['chisquare'](self, *args) - - def chisquare_cdf(self, *args): - r'''From Giac's documentation: - Help for chisquare_cdf: - chisquare_cdf(Intg(n),Real(x0)) - Returns the probability that a Chi^2 random variable is less than x0 (n is the number of degrees of freedom). - See also: 1/ UTPC 2/ chisquare_icdf 3/ chisquared - Ex1:chisquare_cdf(2,6.1) - Ex2:chisquare_cdf(4,6.1) - ''' - return GiacMethods['chisquare_cdf'](self, *args) - - def chisquare_icdf(self, *args): - r'''From Giac's documentation: - Help for chisquare_icdf: - chisquare_icdf(Intg(n),Real(p)) - Returns h such as the probability that a Chi^2 random variable is less than h is p (n is the number of degrees of freedom and 0<=p<=1). - See also: 1/ chisquare_cdf 2/ chisquared - Ex1:chisquare_icdf(2,0.95) - Ex2:chisquare_icdf(4,0.05) - ''' - return GiacMethods['chisquare_icdf'](self, *args) - - def chisquared(self, *args): - r'''From Giac's documentation: - Help for chisquared: - chisquared(Intg(n),Real(x0)) - Returns the probability density of the Chi^2 law at x0 (n is the number of degrees of freedom). - See also: 1/ chisquare_cdf 2/ chisquare_icdf 3/ randvector 4/ ranm - Ex1:chisquared(2,3.2) - Ex2:chisquared(4,10.5) - Ex3: randvector(3,chisquare,2) - Ex4: ranm(4,3,chisquare,2) - ''' - return GiacMethods['chisquared'](self, *args) - - def chisquared_cdf(self, *args): - r'''From Giac's documentation: - Help for chisquared_cdf: - chisquared_cdf(Intg(n),Real(x0)) - Returns the probability that a Chi^2 random variable is less than x0 (n is the number of degrees of freedom). - See also: 1/ UTPC 2/ chisquare_icdf 3/ chisquared - Ex1:chisquared_cdf(2,6.1) - Ex2:chisquared_cdf(4,6.1) - ''' - return GiacMethods['chisquared_cdf'](self, *args) - - def chisquared_icdf(self, *args): - r'''From Giac's documentation: - Help for chisquared_icdf: - chisquared_icdf(Intg(n),Real(p)) - Returns h such as the probability that a Chi^2 random variable is less than h is p (n is the number of degrees of freedom and 0<=p<=1). - See also: 1/ chisquare_cdf 2/ chisquared - Ex1:chisquared_icdf(2,0.95) - Ex2:chisquared_icdf(4,0.05) - ''' - return GiacMethods['chisquared_icdf'](self, *args) - - def chisquaret(self, *args): - r'''From Giac's documentation: - Help for chisquaret: - chisquaret(Data,[Func],[FuncParams]) - Chi^2 test : fitness between 2 (or n) samples or between 1 sample and a distribution law (multinomial or given by a law). - See also: 1/ normalt 2/ studentt 3/ kolmogorovt - Ex1:chisquaret([57,54]) - Ex2:chisquaret([1,1,1,1,1,0,0,1,0,0,1,1,1,0,1,1,0,1,1,0,0,0,0],[.4,.6]) - Ex3:chisquaret([57,30],[.6,.4]) - Ex4:chisquaret([17,15,12,15],[15,13,13,14]) - Ex5:chisquaret(ranv(1000,binomial,10,.5),binomial) - Ex6:chisquaret(ranv(1000,binomial,10,.5),binomial,11,.5) - Ex7:chisquaret(ranv(1000,normald,0,.2),normald) - Ex8:chisquaret([11,16,17,22,14,10],[1/6,1/6,1/6,1/6,1/6,1/6]) - Ex9:chisquaret([11,16,17,22,14,10],[(1/6)$6]) - ''' - return GiacMethods['chisquaret'](self, *args) - - def choice(self, *args): - r'''From Giac's documentation: - Help for choice: - choice(Lst(L)) - choice(L)=rand(L)=one extracted element of L. - See also: 1/ rand 2/ sample - Ex1:choice([1,2,3,4,5,6]) - Ex2:choice(["r","r","r","b","n"]) - Ex3: L:=[1,2,3,4,5,6];L:=choice(L) - Ex4: L:=[1,2,3,4,5,6];L.choice() - ''' - return GiacMethods['choice'](self, *args) - - def cholesky(self, *args): - r'''From Giac's documentation: - Help for cholesky: - cholesky(Mtrx) - For a numerical symmetric matrix A, returns L matrix such that A=L*tran(L). - See also: 1/ lu 2/ qr 3/ gauss - Ex1:cholesky([[3,1],[1,4]]) - ''' - return GiacMethods['cholesky'](self, *args) - - def chr(self, *args): - r'''From Giac's documentation: - Help for chr: - chr(Intg or Lst(Intg)) - Returns the string corresponding to the character code of the argument. - See also: 1/ asc 2/ ord - Ex1:chr(65) - Ex2:chr([65,66,67]) - ''' - return GiacMethods['chr'](self, *args) - - def chrem(self, *args): - r'''From Giac's documentation: - Help for chrem: - chrem(LstIntg(a,b,c....),LstIntg(p,q,r,....)) - Chinese remainders for integers or for polynomials. - See also: 1/ gcd 2/ fracmod 3/ chinrem 4/ ichinrem - Ex1:chrem(symbolique.) - Ex2:chrem([2,3],[7,5]) - Ex3:chrem([2,4,6],[3,5,7]) - Ex4:chrem([2,4,6,7],[3,5,7,11]) - Ex5:chrem([2*x+1,4*x+2,6*x-1,x+1],[3,5,7,11]) - ''' - return GiacMethods['chrem'](self, *args) - - def chromatic_index(self, *args): - r'''From Giac's documentation: - Help for chromatic_index: - chromatic_index(Graph(G),[Lst(cols)]) - Returns the number of colors in the minimal edge coloring of G. If an unassigned identifier cols is given, the coloring is stored to it. - See also: 1/ minimal_edge_coloring 2/ chromatic_number - Ex1:chromatic_index(graph("petersen")) - Ex2:chromatic_index(graph("dodecahedron"),colors) - ''' - return GiacMethods['chromatic_index'](self, *args) - - def chromatic_number(self, *args): - r'''From Giac's documentation: - Help for chromatic_number: - chromatic_number(Graph(G)) - Returns the chromatic number of G. - Ex1:chromatic_number(graph("petersen")) - ''' - return GiacMethods['chromatic_number'](self, *args) - - def chromatic_polynomial(self, *args): - r'''From Giac's documentation: - Help for chromatic_polynomial: - chromatic_polynomial(Graph(G),[Var(t)]) - Returns the chromatic polynomial [or its value at point t] of undirected unweighted graph G. - See also: 1/ flow_polynomial 2/ reliability_polynomial 3/ tutte_polynomial - Ex1:chromatic_polynomial(graph("petersen")) - Ex2:chromatic_polynomial(graph("petersen"),3) - ''' - return GiacMethods['chromatic_polynomial'](self, *args) - - def circle(self, *args): - r'''From Giac's documentation: - Help for circle: - circle((Pnt(M) or Cplx(M),(Pnt(N) or Cplx(zN)),[Real(a)],[Real(b)],[Var(A)],[Var(B)]) - Define for 2-d a circle with a diameter MN (arg1=M or zM,arg2=N) or with center and radius (arg1=M or zM,arg2=zN) and (center=M and radius=abs(zN)) [or the arc AB, A angle a, B angle b, (MN=angle 0) or M(M+zN)=angle 0] or by its equation and for 3-d with a diameter and a third point. - See also: 1/ circumcircle 2/ incircle 3/ excircle 4/ center 5/ radius 6/ sphere 7/ Circle - Ex1:circle(0,point(2*i)) - Ex2:circle(i,i) - Ex3:circle(i,1) - Ex4:circle(0,i,pi/4,pi/2) - Ex5:circle(0,i,pi/4,pi/2,A,B) - Ex6:circle(x^2+y^2-x-y) - Ex7:circle(cercle(point([-1,0,0]),point([1,0,0]),point([0,2,0]))) - Ex8:circle(cercle([-1,0,0],point([1,0,0]),[0,2,0])) - ''' - return GiacMethods['circle'](self, *args) - - def circumcircle(self, *args): - r'''From Giac's documentation: - Help for circumcircle: - circumcircle((Pnt or Cplx),(Pnt or Cplx),((Pnt or Cplx)) - circumcircle(A,B,C)=circumcircle of the triangle ABC. - See also: 1/ circle 2/ incircle 3/ excircle - Ex1:circumcircle(0,1,1+i) - ''' - return GiacMethods['circumcircle'](self, *args) - - def classes(self, *args): - r'''From Giac's documentation: - Help for classes: - classes(Lst(l),[ClassMin],[ClassSize||Lst(Center)]) - Returns the matrix [[class,number],...] obtained with class_min and class_size: see init of geo or argument 2 and 3 or with the list of centers. - See also: 1/ histogram 2/ cumulated_frequencies 3/ bar_plot 4/ frequencies - Ex1:classes([1,1.2,1.4,1.6,1.8,2,2.5]) - Ex2:classes([1,1.2,1.4,1.6,1.8,2,2.5],1.2,0.5) - Ex3:classes([1,1.2,1.4,1.6,1.8,2,2.5],1,[1.2,1.6,2,2.4]) - Ex4:classes([1,1.2,1.4,1.6,1.8,2,2.5],1,[1.2,1.6,2.2]) - Ex5:classes([0,0.5,1,1.5,2,2.5,3,3.5,4],[0..2,2..4,4..6]) - ''' - return GiacMethods['classes'](self, *args) - - def clear(self, *args): - r'''From Giac's documentation: - Help for clear: - clear(NULL) - Clears the list of pixels. - See also: 1/ set_pixel 2/ show_pixels - Ex1:clear() - ''' - return GiacMethods['clear'](self, *args) - - def clique_cover(self, *args): - r'''From Giac's documentation: - Help for clique_cover: - clique_cover(Graph(G),[Intg(k)]) - Returns a clique vertex cover of G [containing at most k cliques]. - See also: 1/ clique_cover_number 2/ maximal_cliques - Ex1:clique_cover(graph("petersen")) - Ex2:clique_cover(cycle_graph(5)) - Ex3:clique_cover(graph_complement(complete_graph(3,4))) - ''' - return GiacMethods['clique_cover'](self, *args) - - def clique_cover_number(self, *args): - r'''From Giac's documentation: - Help for clique_cover_number: - clique_cover_number(Graph(G)) - Returns the clique cover number of G. - See also: 1/ clique_cover 3/ maximal_cliques - Ex1:clique_cover_number(graph("petersen")) - Ex2:clique_cover_number(cycle_graph(5)) - Ex3:clique_cover_number(graph_complement(complete_graph(3,4))) - ''' - return GiacMethods['clique_cover_number'](self, *args) - - def clique_number(self, *args): - r'''From Giac's documentation: - Help for clique_number: - clique_number(Graph(G)) - Returns the clique number of G, which is equal to the size of a maximum clique in G. - See also: 1/ maximum_clique - Ex1:clique_number(graph_complement(complete_graph(3,4))) - ''' - return GiacMethods['clique_number'](self, *args) - - def clique_stats(self, *args): - r'''From Giac's documentation: - Help for clique_stats: - clique_stats(Graph(G),[Intg(k)||Intrv(m..n)]) - Returns the list of numbers of maximal cliques of size s in G for each s. If parameter k is given, the number of k-cliques is returned. If an interval m..n is given, only cliques with size between m and n (inclusive) are counted (m also may be +infinity). - See also: 1/ maximum_clique 2/ clique_cover - Ex1:clique_stats(random_graph(50,0.5)) - Ex2:clique_stats(random_graph(50,0.5),5) - Ex3:clique_stats(random_graph(50,0.5),3..5) - ''' - return GiacMethods['clique_stats'](self, *args) - - def clustering_coefficient(self, *args): - r'''From Giac's documentation: - Help for clustering_coefficient: - clustering_coefficient(Graph(G),[Vrtx(v)]) - Returns the average clustering coefficient of undirected graph G, or the local clustering coefficient of the vertex v in G. - See also: 1/ network_transitivity 2/ number_of_triangles - Ex1:clustering_coefficient(graph(%{[1,2],[2,3],[2,4],[3,4],[4,1]%})) - Ex2:clustering_coefficient(graph(%{[1,2],[2,3],[2,4],[3,4],[4,1]%}),2) - ''' - return GiacMethods['clustering_coefficient'](self, *args) - - def coeff(self, *args): - r'''From Giac's documentation: - Help for coeff: - coeff(Expr(P),[Var]) - Returns the list of coefficients of a polynomial with respect to the 2nd argument or the coefficient of degree the 3rd argument. - See also: 1/ pcoeff 2/ fcoeff - Ex1:coeff(x*3+2) - Ex2:coeff(5*y^2-3,y) - Ex3:coeff(5*y^2-3,y,2) - ''' - return GiacMethods['coeff'](self, *args) - - def coeffs(self, *args): - r'''From Giac's documentation: - Help for coeffs: - coeffs(Expr(P),[Var]) - Returns the list of coefficients of a polynomial with respect to the 2nd argument or the coefficient of degree the 3rd argument. - See also: 1/ pcoeff 2/ fcoeff - Ex1:coeffs(x*3+2) - Ex2:coeffs(5*y^2-3,y) - Ex3:coeffs(5*y^2-3,y,2) - ''' - return GiacMethods['coeffs'](self, *args) - - def col(self, *args): - r'''From Giac's documentation: - Help for col: - col(Mtrx(A),Intg(n)||Interval(n1..n2)) - Returns column n or the sequence of the columns n1..n2 of the matrix A, or optional argument of count,count_eq,count_inf,count_sup. - See also: 1/ row 2/ count 3/ count_eq 4/ count_inf 5/ count_sup - Ex1:col([[1,2,3],[4,5,6],[7,8,9]],1) - Ex2:col([[1,2,3],[4,5,6],[7,8,9]],0..1) - Ex3: count_eq(3,[[3,2,3],[4,3,2],[3,2,1]],col) - ''' - return GiacMethods['col'](self, *args) - - def colDim(self, *args): - r'''From Giac's documentation: - Help for colDim: - colDim(Mtrx) - Number of columns of a matrix. - See also: 1/ rowdim - Ex1:colDim([[1,2,3],[4,5,6]]) - Ex2:colDim([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['colDim'](self, *args) - - def colNorm(self, *args): - r'''From Giac's documentation: - Help for colNorm: - colNorm(Vect or Mtrx) - Returns the max of the l1_norm of the columns of a matrix: colNorm(a_{j,k})=max_k(sum_j(|a_{j,k}|)). - See also: 1/ norm - Ex1:colNorm([[1,2],[3,-4]]) - Ex2:colNorm([[1,2,3,-4],[-5,3,2,1]]) - ''' - return GiacMethods['colNorm'](self, *args) - - def colSwap(self, *args): - r'''From Giac's documentation: - Help for colSwap: - colSwap(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by swapping the n1-th column and the n2-th column. - See also: 1/ rowSwap - Ex1:colSwap([[1,2],[3,4],[5,6]],0,1) - ''' - return GiacMethods['colSwap'](self, *args) - - def coldim(self, *args): - r'''From Giac's documentation: - Help for coldim: - coldim(Mtrx) - Number of columns of a matrix. - See also: 1/ rowdim - Ex1:coldim([[1,2,3],[4,5,6]]) - Ex2:coldim([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['coldim'](self, *args) - - def collect(self, *args): - r'''From Giac's documentation: - Help for collect: - collect(Poly or LstPoly) - Integer factorization of a polynomial (or of a list of poly). - See also: 1/ factor 2/ factors - Ex1:collect(x^2-4) - Ex2:collect(x^2-2) - Ex3:collect([x^2-2,x^2-4]) - ''' - return GiacMethods['collect'](self, *args) - - def colnorm(self, *args): - r'''From Giac's documentation: - Help for colnorm: - colnorm(Vect or Mtrx) - Returns the max of the l1_norm of the columns of a matrix: colNorm(a_{j,k})=max_k(sum_j(|a_{j,k}|)). - See also: 1/ norm - Ex1:colnorm([[1,2],[3,-4]]) - Ex2:colnorm([[1,2,3,-4],[-5,3,2,1]]) - ''' - return GiacMethods['colnorm'](self, *args) - - def color(self, *args): - r'''From Giac's documentation: - Help for color: - color([GeoObj or legende],Intg) - Draws a geometrical object with colors black=0 red=1 green=2 yellow=3 blue=4, filled with the color in the interior of a closed curve,line_width_n (00 (by default a=1 for sine window) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ bartlett_hann_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(cosine_window(randvector(1000,0..1),1.5)) - ''' - return GiacMethods['cosine_window'](self, *args) - - def cot(self, *args): - r'''From Giac's documentation: - Help for cot: - cot(Expr) - Cotangent. - See also: 1/ acot 2/ tan - Ex1:cot(pi/2) - ''' - return GiacMethods['cot'](self, *args) - - def cote(self, *args): - r'''From Giac's documentation: - Help for cote: - cote(Vect) - Third coordinate (z) of a 3-d point. - See also: 1/ abscissa 2/ ordinate 3/ coordinates - Ex1:cote(point[1,2,3]) - Ex2:cote(point(1,2,3)) - ''' - return GiacMethods['cote'](self, *args) - - def count(self, *args): - r'''From Giac's documentation: - Help for count: - count(Fnc(f)||LstIntg,(Lst||Mtrx)(l),[Opt(row||col)]) - Returns f(l[0])+f(l[1])+...+f(l[size(l)-1]) or counts the number of occurrences if the argument is a vector of integers. - See also: 1/ count_eq 2/ count_inf 3/ count_sup - Ex1:count(id,[-2/5,-1,0,1,2,3/5]) - Ex2:count(1,[-2,-1,0,1,2,3]) - Ex3:count([1,3,1,1,2,10,3]) - Ex4:count((x)->x>2,[3/2,5/2,8/3]) - Ex5:count((x)->x==1,[-2,1,0,1,2,3]) - Ex6:count((x)->x>2,[[3,5/2],[4,1]]) - Ex7:count((x)->x>2,[[3,5/2],[4,1]],row) - Ex8:count((x)->x>2,[[3,5/2],[4,1]],col) - Ex9:count((x)->x>2 && x<4,[[3,9/2],[4,1]]) - Ex10:count((x)->x<2 || x>4,[[3,9/2],[4,1]]) - ''' - return GiacMethods['count'](self, *args) - - def count_eq(self, *args): - r'''From Giac's documentation: - Help for count_eq: - count_eq(Real(a),(Lst||Mtrx)(L),[Opt(row||col)]) - Returns the number of elements of L equal to a. - See also: 1/ count 2/ count_inf 3/ count_sup - Ex1:count_eq(1,[-2,1,0,1,2,-3]) - Ex2:count_eq(4,[[3,4],[4,1]]) - Ex3:count_eq(4,[[3,4],[4,1]],row) - Ex4:count_eq(4,[[3,4],[4,1]],col) - ''' - return GiacMethods['count_eq'](self, *args) - - def count_inf(self, *args): - r'''From Giac's documentation: - Help for count_inf: - count_inf(Real(a),(Lst||Mtrx)(L),[Opt(row||col)]) - Returns the number of elements of L strictly less than a. - See also: 1/ count 2/ count_eq 3/ count_sup - Ex1:count_inf(1,[-2,-1,0,1,2,3]) - Ex2:count_inf(4,[[3,5],[4,1]]) - Ex3:count_inf(4,[[3,5],[4,1]],row) - Ex4:count_inf(4,[[3,5],[4,1]],col) - ''' - return GiacMethods['count_inf'](self, *args) - - def count_sup(self, *args): - r'''From Giac's documentation: - Help for count_sup: - count_sup(Real(a),(Lst||Mtrx)(L),[Opt(row||col)]) - Returns the number of elements of L strictly greater than a. - See also: 1/ count 2/ count_inf 3/ count_eq - Ex1:count_sup(1,[-2,-1,0,1,2,3]) - Ex2:count_sup(3,[[3,5],[4,1]]) - Ex3:count_sup(3,[[3,5],[4,1]],row) - Ex4:count_sup(3,[[3,5],[4,1]],col) - ''' - return GiacMethods['count_sup'](self, *args) - - def courbe_parametrique(self, *args): - r'''From Giac's documentation: - Help for courbe_parametrique: - courbe_parametrique(Cplx||Lst,Var||Lst(Var)) - plotparam(a(x)+i*b(x),x=x0..x1) draws the curve X=a(x),Y=b(x) x=x0..x1 or plotparam([a(u,v),b(u,v),c(u,v)],[u=u0..u1,v=v0..v1]) draws the surface X=a(u,v),Y=b(u,v),Z=c(u,v) u=u0..u1 and v=v0..v1. - See also: 1/ plotfunc 2/ plotpolar 3/ arc - Ex1:courbe_parametrique(sin(t)+i*cos(t),t) - Ex2:courbe_parametrique(exp(i*t),t=0..pi/2,affichage=1) - Ex3:courbe_parametrique(exp(i*t),t=0..pi/2,affichage=1+rempli) - Ex4:courbe_parametrique([sin(x),cos(x)],x=0..1) - Ex5:courbe_parametrique([sin(x),cos(x)],x=0..1,affichage=rouge) - Ex6:courbe_parametrique(sin(x)+i*cos(x),x=0..1,tstep=0.01) - Ex7:courbe_parametrique([v*cos(u),v*sin(u),v],[u,v]) - Ex8:courbe_parametrique([v*cos(u),v*sin(u),v],[u=0..pi,v=0..3],ustep=0.1,vstep=0.2) - ''' - return GiacMethods['courbe_parametrique'](self, *args) - - def courbe_polaire(self, *args): - r'''From Giac's documentation: - Help for courbe_polaire: - courbe_polaire(Expr,Var,VarMin,VarMax) - plotpolar(f(x),x,a,b) draws the polar curve r=f(x) for x in [a,b]. - See also: 1/ plotparam 2/ plotfunc 3/ plotpolar - Ex1:courbe_polaire(sin(2*x),x,0,pi) - Ex2:courbe_polaire(sin(2*x),x,0,pi,tstep=0.1) - ''' - return GiacMethods['courbe_polaire'](self, *args) - - def covariance(self, *args): - r'''From Giac's documentation: - Help for covariance: - covariance(Lst||Mtrx,[Lst]) - Returns the covariance of the elements of its argument. - See also: 1/ correlation 2/ covariance_correlation - Ex1:covariance([[1,2],[1,1],[4,7]]) - ''' - return GiacMethods['covariance'](self, *args) - - def covariance_correlation(self, *args): - r'''From Giac's documentation: - Help for covariance_correlation: - covariance_correlation(Lst||Mtrx,[Lst]) - Returns the list of the covariance and the correlation of the elements of its argument. - See also: 1/ covariance 2/ correlation - Ex1:covariance_correlation([[1,2],[1,1],[4,7]]) - ''' - return GiacMethods['covariance_correlation'](self, *args) - - def cpartfrac(self, *args): - r'''From Giac's documentation: - Help for cpartfrac: - cpartfrac(RatFrac) - Performs partial fraction decomposition in ℂ of a fraction. - See also: 1/ factor 2/ normal - Ex1:cpartfrac((x)/(4-x^2)) - Ex2:cpartfrac((x^2-2*x+3)/(x^2-3*x+2)) - Ex3:cpartfrac(a/(z*(z-b)),z) - ''' - return GiacMethods['cpartfrac'](self, *args) - - def crationalroot(self, *args): - r'''From Giac's documentation: - Help for crationalroot: - crationalroot(Poly(P)) - Returns the list of complex rational roots of P without indicating the multiplicity. - See also: 1/ proot 2/ froot 3/ complexroot 4/ rationalroot 5/ realroot - Ex1:crationalroot(2*x^3+(-5-7*i)*x^2+(-4+14*i)*x+8-4*i) - ''' - return GiacMethods['crationalroot'](self, *args) - - def crayon(self, *args): - r'''From Giac's documentation: - Help for crayon: - crayon(Color) - Changes the color of the pencil (without parameter, returns the current color). - See also: 1/ leve_crayon 2/ baisse_crayon - Ex1: crayon vert - Ex2:crayon(rouge) - Ex3:crayon(5) - Ex4:crayon(gomme) - ''' - return GiacMethods['crayon'](self, *args) - - def createwav(self, *args): - r'''From Giac's documentation: - Help for createwav: - createwav(Lst(data),[opts]) - Returns an audio clip from data, optionally setting channel count, bit depth, sample rate, duration and normalization level. - See also: 1/ readwav 2/ writewav 3/ playsnd - Ex1:createwav(duration=3.5) - Ex2:createwav(sin(2*pi*440*soundsec(2)),samplerate=48000) - Ex3:createwav(sin(2*pi*440*soundsec(2)),bit_depth=8) - Ex4:createwav(10*sin(2*pi*440*soundsec(2)),normalize=-3) - Ex5: t:=soundsec(3):;L,R:=sin(2*pi*440*t),sin(2*pi*445*t):;createwav([L,R]) - ''' - return GiacMethods['createwav'](self, *args) - - def cross(self, *args): - r'''From Giac's documentation: - Help for cross: - cross(Vect(v1),Vect(v2)) - Wedge product. - See also: 1/ dot - Ex1:cross([1,2],[3,4]) - Ex2:cross([1,2,3],[4,5,6]) - ''' - return GiacMethods['cross'](self, *args) - - def crossP(self, *args): - r'''From Giac's documentation: - Help for crossP: - crossP(Vect(v1),Vect(v2)) - Wedge product. - See also: 1/ dot - Ex1:crossP([1,2],[3,4]) - Ex2:crossP([1,2,3],[4,5,6]) - ''' - return GiacMethods['crossP'](self, *args) - - def cross_correlation(self, *args): - r'''From Giac's documentation: - Help for cross_correlation: - cross_correlation(cross_correlation(Lst(u),Lst(v))) - Returns the cross_correlation of two signals u and v. - See also: 1/ auto_correlation 2/ correlation - Ex1:cross_correlation([1,2],[3,4,5]) - ''' - return GiacMethods['cross_correlation'](self, *args) - - def cross_point(self, *args): - r'''From Giac's documentation: - Help for cross_point: - cross_point(Opt) - Option of the display command for a point. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),point_point) - Ex2: F:=display(point(2+1.5*i),rhombus_point) - ''' - return GiacMethods['cross_point'](self, *args) - - def cross_ratio(self, *args): - r'''From Giac's documentation: - Help for cross_ratio: - cross_ratio(Pnt or Cplx(a),Pnt or Cplx(b),Pnt or Cplx(c),Pnt or Cplx(d)) - Returns the complex number equal to ((c-a)/(c-b))/((d-a)/(d-b)). - See also: 1/ harmonic_conjugate 2/ is_conjugate - Ex1:cross_ratio(i,2+i,3/2+i,3+i) - Ex2:cross_ratio(0,1+i,1,i) - Ex3:cross_ratio(0,1,2,3) - ''' - return GiacMethods['cross_ratio'](self, *args) - - def crossproduct(self, *args): - r'''From Giac's documentation: - Help for crossproduct: - crossproduct(Vect(v1),Vect(v2)) - Wedge product. - See also: 1/ dot - Ex1:crossproduct([1,2],[3,4]) - Ex2:crossproduct([1,2,3],[4,5,6]) - ''' - return GiacMethods['crossproduct'](self, *args) - - def csc(self, *args): - r'''From Giac's documentation: - Help for csc: - csc(Expr) - Cosecant: csc(x)=1/sin(x). - See also: 1/ sin 2/ acsc - Ex1:csc(pi/2) - ''' - return GiacMethods['csc'](self, *args) - - def csolve(self, *args): - r'''From Giac's documentation: - Help for csolve: - csolve(LstEq,LstVar) - Returns the list of complex solutions of an equation or a matrix where the rows are ℂ-solutions of a system of polynomial equations. - See also: 1/ cZeros 2/ solve 3/ fslove - Ex1:csolve(x^4-1,x) - Ex2:csolve(x^4-y^4 and x+y=2,[x,y]) - Ex3:csolve(x^4-y^4 and x+y=0 and x^2=2*x,[x,y]) - Ex4:csolve(u*v-u=v and v^2=u,[u,v]) - ''' - return GiacMethods['csolve'](self, *args) - - def csv2gen(self, *args): - r'''From Giac's documentation: - Help for csv2gen: - csv2gen(Strng(filename),Strng(sep),Strng(nl),Strng(decsep),Strng(eof),[string]) - Reads a file (or string) in CSV format. - See also: 1/ read - Ex1:csv2gen("mat.txt",",",char(10),".") - ''' - return GiacMethods['csv2gen'](self, *args) - - def cube(self, *args): - r'''From Giac's documentation: - Help for cube: - cube(Pnt(A),Pnt(B),Pnt(C)) - Draws the direct cube with vertices A,B with a face in the plane (A,B,C). - See also: 1/ parallelepiped 2/ cylinder 3/ icosahedron 4/ dodecahedron 5/ octahedron 6/ tetrahedron 7/ centered_cube - Ex1: cube([0,0,0],[3,0,0],[0,0,1]) - Ex2: A,B,C:=point(1,0,0),point(1,1,0),point(0,1,0);c:=cube(A,B,C);A,B,C,D,E,F,G,H:=sommets(c); - Ex3: A,B,K:=point(1,0,0),point(1,1,0),point(0,2,0);c:=cube(A,B,C);A,B,C,D,E,F,G,H:=sommets(c); - Ex4: c:=cube([0,0,0],[1,0,0],[0,1,0]);c1,c2,c4,c3,c5,c6,c7,c8:=sommets(c); - Ex5: c:=cube([0,0,0],[0,2,0],[0,0,1]);c1,c2,c4,c3,c5,c6,c7,c8:=sommets(c); - ''' - return GiacMethods['cube'](self, *args) - - def cumSum(self, *args): - r'''From Giac's documentation: - Help for cumSum: - cumSum(Lst(l)||Seq||Str) - Returns the list (or the sequence or the string) lr where the elements are the cumulative sum of the list l: lr[k]=sum(l[j],j=0..k) (or lr=sum(l[j],j=0..k)$(k=0..size(l)-1)). - See also: 1/ sum - Ex1:cumSum([0,1,2,3,4]) - Ex2:cumSum(1.2,3,4.5,6) - Ex3:cumSum("a","b","c","d") - ''' - return GiacMethods['cumSum'](self, *args) - - def cumsum(self, *args): - r'''From Giac's documentation: - Help for cumsum: - cumsum(Lst(l)||Seq||Str) - Returns the list (or the sequence or the string) lr where the elements are the cumulative sum of the list l: lr[k]=sum(l[j],j=0..k) (or lr=sum(l[j],j=0..k)$(k=0..size(l)-1)). - See also: 1/ sum - Ex1:cumsum([0,1,2,3,4]) - Ex2:cumsum(1.2,3,4.5,6) - Ex3:cumsum("a","b","c","d") - ''' - return GiacMethods['cumsum'](self, *args) - - def cumulated_frequencies(self, *args): - r'''From Giac's documentation: - Help for cumulated_frequencies: - cumulated_frequencies(Lst || Mtrx) - Draws the diagram of the cumulated frequencies (rows=[value,frequencies]) - See also: 1/ histogram 2/ classes 3/ bar_plot - Ex1:cumulated_frequencies([1,2,1,1,2,1,2,4,3,3]) - Ex2:cumulated_frequencies([(rand(6)+1)$(k=1..100)]) - Ex3:cumulated_frequencies([[1,0.3],[2,0.5],[3,0.2]]) - Ex4:cumulated_frequencies([[1..2,0.3],[2..3,0.5],[3..4,0.2]]) - Ex5:cumulated_frequencies([[1..2,0.3,0.5],[2..3,0.5,0.2],[3..4,0.2,0.3]]) - ''' - return GiacMethods['cumulated_frequencies'](self, *args) - - def curl(self, *args): - r'''From Giac's documentation: - Help for curl: - curl(Lst(A,B,C),Lst(x,y,z)) - curl([A,B,C],[x,y,z])=[dC/dy-dB/dz,dA/dz-dC/dx,dB/dx-dA/dy]. - See also: 1/ derive 2/ divergence - Ex1:curl([2*x*y,x*z,y*z],[x,y,z]) - ''' - return GiacMethods['curl'](self, *args) - - def current_sheet(self, *args): - r'''From Giac's documentation: - Help for current_sheet: - current_sheet([Intg||Inter],[Intg||Letter],[Letter]) - Content of the matrix editor or spreadsheet. - Ex1:current_sheet(1,2) - Ex2:current_sheet(A1..A5,B,G) - ''' - return GiacMethods['current_sheet'](self, *args) - - def curvature(self, *args): - r'''From Giac's documentation: - Help for curvature: - curvature(Curve,Point) - Curvature of curve C at point M. - See also: 1/ osculating_circle 2/ evolute - Ex1:curvature(plot(x^2),point(1,1)) - Ex2:curvature([5*cos(t),5*sin(t)],t) - Ex3:curvature([t,t^2],t) - Ex4:curvature([t,t^2],t,1) - Ex5:curvature([3*exp(t/2)*cos(t),3*exp(t/2)*sin(t)],t) - Ex6:curvature([3*exp(t/2)*cos(t),3*exp(t/2)*sin(t)],t,7) - Ex7: trigcos(curvature([2*cos(t),2*sin(t),3*t],t)) - ''' - return GiacMethods['curvature'](self, *args) - - def curve(self, *args): - r'''From Giac's documentation: - Help for curve: - curve(Expr) - Reserved word. - See also: 1/ - ''' - return GiacMethods['curve'](self, *args) - - def cyan(self, *args): - r'''From Giac's documentation: - Help for cyan: - cyan(Opt) - Option of the display command to display with color. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),red) - Ex2: F:=display(point(2+1.5*i),point_point+green) - ''' - return GiacMethods['cyan'](self, *args) - - def cycle2perm(self, *args): - r'''From Giac's documentation: - Help for cycle2perm: - cycle2perm(Cycle) - Converts the cycle c to a permutation. - See also: 1/ cycles2permu 2/ permu2cycles - Ex1:cycle2perm([1,3,5]) - ''' - return GiacMethods['cycle2perm'](self, *args) - - def cycle_graph(self, *args): - r'''From Giac's documentation: - Help for cycle_graph: - cycle_graph(Intg(n)||Lst(V)) - Returns a cyclic graph with n vertices (or with vertices from list V). - See also: 1/ graph 2/ path_graph - Ex1:cycle_graph(4) - Ex2:cycle_graph(["one","two","three","four","five"]) - ''' - return GiacMethods['cycle_graph'](self, *args) - - def cycleinv(self, *args): - r'''From Giac's documentation: - Help for cycleinv: - cycleinv(Cycle(c)) - Returns the inverse cycle of the cycle c. - See also: 1/ perminv - Ex1:cycleinv([1,3,5]) - ''' - return GiacMethods['cycleinv'](self, *args) - - def cycles2permu(self, *args): - r'''From Giac's documentation: - Help for cycles2permu: - cycles2permu(Lst(Cycle)) - Converts a product of cycles into a permutation. - See also: 1/ permu2cycles 2/ cycle2perm - Ex1:cycles2permu([[1,3,5],[3,4]]) - ''' - return GiacMethods['cycles2permu'](self, *args) - - def cyclotomic(self, *args): - r'''From Giac's documentation: - Help for cyclotomic: - cyclotomic(Expr) - N-th cyclotomic polynomial. - See also: 1/ none - Ex1:cyclotomic(20) - ''' - return GiacMethods['cyclotomic'](self, *args) - - def cylinder(self, *args): - r'''From Giac's documentation: - Help for cylinder: - cylinder(Pnt(A),Vect(v),Real(r),[Real(h)]) - Draws a cylinder with axis (A,v), with radius r [and with altitude h]. - See also: 1/ half_cone 2/ cone - Ex1:cylinder([0,0,0],[0,1,0],2) - Ex2:cylinder([0,0,0],[0,1,0],2,-3) - ''' - return GiacMethods['cylinder'](self, *args) - - def dash_line(self, *args): - r'''From Giac's documentation: - Help for dash_line: - dash_line(Opt) - Option of the display command for a line. - See also: 1/ display - Ex1: display(line(y=x),green+dash_line+line_width_2) - Ex2: d:=display(line(2+i,1),cap_round_line) - ''' - return GiacMethods['dash_line'](self, *args) - - def dashdot_line(self, *args): - r'''From Giac's documentation: - Help for dashdot_line: - dashdot_line(Opt) - Option of the display command for a line. - See also: 1/ display - Ex1: display(line(y=x),green+dash_line+line_width_2) - Ex2: d:=display(line(2+i,1),cap_round_line) - ''' - return GiacMethods['dashdot_line'](self, *args) - - def dashdotdot_line(self, *args): - r'''From Giac's documentation: - Help for dashdotdot_line: - dashdotdot_line(Opt) - Option of the display command for a line. - See also: 1/ display - Ex1: display(line(y=x),green+dash_line+line_width_2) - Ex2: d:=display(line(2+i,1),cap_round_line) - ''' - return GiacMethods['dashdotdot_line'](self, *args) - - def dayofweek(self, *args): - r'''From Giac's documentation: - Help for dayofweek: - dayofweek(Int,Int,Int) - dayofweek(d,m,y) returns the day of the given date (day,month,year) : 0 for sunday, 1 for monday ...6 for saturday. - Ex1:dayofweek(21,4,2014) - Ex2:dayofweek(15,10,1582) - ''' - return GiacMethods['dayofweek'](self, *args) - - def deSolve(self, *args): - r'''From Giac's documentation: - Help for deSolve: - deSolve(Eq,[TimeVar],FncVar) - Solves a differential equation or a differential linear system with constant coefficients. - See also: 1/ integrate 2/ diff 3/ odesolve 4/ plotode 5/ plotfiefd - Ex1:deSolve(y'+x*y=0) - Ex2:deSolve(y'+x*y=0,y) - Ex3:deSolve(y'+x*y=0,[0,1]) - Ex4:deSolve([y'+x*y=0,y(0)=1],y) - Ex5:deSolve([y'=[[1,2],[2,1]]*y+[x,x+1],y(0)=[1,2]]) - Ex6:deSolve(y''+y=0,y) - Ex7:deSolve([y''+y=sin(x),y(0)=1,y'(0)=2],y) - Ex8:deSolve([y''+y=sin(x),y(0)=1,y'(0)=2],x,y) - Ex9:deSolve([y''+y=sin(x),y(0)=1,y'(0)=2],[x,y]) - Ex10:deSolve(diff(y(t),t)+t*y(t)=0,t,y) - Ex11:deSolve(diff(y(t),t)+t*y(t)=0,[t,y]) - Ex12:deSolve((y''+y=sin(x)) and (y(0)=1) and (y'(0)=2),y) - Ex13:deSolve([z''+2*z'+z,z(0)=1,z'(0)=0],u,z) - Ex14:deSolve([z''+2*z'+z,z(0)=1,z'(0)=0],z(u)) - Ex15:deSolve([z'=[[1,2],[2,1]]*z+[t,t+1],z(0)=[1,2]],t,z) - Ex16:deSolve([z'=[[1,2],[2,1]]*z+[t,t+1],z(0)=[1,2]],z(t)) - ''' - return GiacMethods['deSolve'](self, *args) - - def debut_enregistrement(self, *args): - r'''From Giac's documentation: - Help for debut_enregistrement: - debut_enregistrement(Var(nom_du_dessin)) - Begins recording the commands making up the drawing; the name of the resulting procedure is the argument. - See also: 1/ fin_enregistrement - Ex1:debut_enregistrement(maison) - Ex2:debut_enregistrement(arbre) - ''' - return GiacMethods['debut_enregistrement'](self, *args) - - def degree(self, *args): - r'''From Giac's documentation: - Help for degree: - degree(Poly(P),Var(Var)) - Degree of the polynomial P with respect to the second argument. - See also: 1/ valuation 2/ size 3/ total_degree - Ex1:degree(x^3+x) - Ex2:degree([1,0,1,0]) - Ex3:degree(x^3+x*y,y) - ''' - return GiacMethods['degree'](self, *args) - - def degree_sequence(self, *args): - r'''From Giac's documentation: - Help for degree_sequence: - degree_sequence(Graph(G)) - Returns the list of degrees of vertices of G (arc directions are ignored). - See also: 1/ is_graphic_sequence 2/ is_regular 2/ sequence_graph 3/ vertex_degree - Ex1:degree_sequence(graph(trail(1,2,3,4,2))) - ''' - return GiacMethods['degree_sequence'](self, *args) - - def delcols(self, *args): - r'''From Giac's documentation: - Help for delcols: - delcols(Mtrx(A),Interval(n1..n2)||n1) - Returns the matrix where the columns n1..n2 (or n1) of the matrix A are deleted. - See also: 1/ delrows - Ex1:delcols([[1,2,3],[4,5,6],[7,8,9]],1..1) - Ex2:delcols([[1,2,3],[4,5,6],[7,8,9]],0..1) - Ex3:delcols([[1,2,3],[4,5,6],[7,8,9]],1) - ''' - return GiacMethods['delcols'](self, *args) - - def delete_arc(self, *args): - r'''From Giac's documentation: - Help for delete_arc: - delete_arc(Graph(G),Edge(e)||Trail(T)||Lst(E)) - Returns a modified copy of digraph G with arc e (or trail T or list of arcs E) removed. - See also: 1/ add_arc 2/ delete_edge 3/ digraph 4/ edges 5/ has_arc 6/ trail - Ex1:delete_arc(digraph(trail(1,2,3,4,5,1)),[5,1]) - ''' - return GiacMethods['delete_arc'](self, *args) - - def delete_edge(self, *args): - r'''From Giac's documentation: - Help for delete_edge: - delete_edge(Graph(G),Edge(e)||Trail(T)||Lst(E)) - Returns a modified copy of undirected graph G with edge e (or trail T or list of edges E) removed. - See also: 1/ add_edge 2/ delete_arc 3/ edges 4/ graph 5/ has_edge 6/ trail - Ex1:delete_edge(cycle_graph(4),[1,2]) - ''' - return GiacMethods['delete_edge'](self, *args) - - def delete_vertex(self, *args): - r'''From Giac's documentation: - Help for delete_vertex: - delete_vertex(Graph(G),Vrtx(v)||Lst(V)) - Returns a modified copy of G with vertex v (or vertices from V) removed. - See also: 1/ add_vertex 2/ induced_subgraph - Ex1:delete_vertex(cycle_graph(5),[1,4]) - ''' - return GiacMethods['delete_vertex'](self, *args) - - def delrows(self, *args): - r'''From Giac's documentation: - Help for delrows: - delrows(Mtrx(A),Interval(n1..n2)||n1) - Returns the matrix where the rows n1..n2 (or n1) of the matrix A are deleted. - See also: 1/ delcols - Ex1:delrows([[1,2,3],[4,5,6],[7,8,9]],1..1) - Ex2:delrows([[1,2,3],[4,5,6],[7,8,9]],0..1) - Ex3:delrows([[1,2,3],[4,5,6],[7,8,9]],1) - ''' - return GiacMethods['delrows'](self, *args) - - def deltalist(self, *args): - r'''From Giac's documentation: - Help for deltalist: - deltalist(Lst) - Returns the list of the differences of two terms in succession. - See also: 1/ - Ex1:deltalist([1,4,8,9]) - Ex2:deltalist([1,8,4,9]) - ''' - return GiacMethods['deltalist'](self, *args) - - def denom(self, *args): - r'''From Giac's documentation: - Help for denom: - denom(Frac(a/b) or RatFrac) - Returns the denominator of the simplified fraction. - See also: 1/ getDenom 2/ getNum 3/ numer 4/ f2nd - Ex1:denom(25/15) - Ex2:denom((x^3-1)/(x^2-1)) - Ex3:denom(1+(x^3-1)/x^2) - ''' - return GiacMethods['denom'](self, *args) - - def densityplot(self, *args): - r'''From Giac's documentation: - Help for densityplot: - densityplot(Expr,[x=xrange,y=yrange],[z],[xstep],[ystep]) - Shows in the plane with colors the graph of an expression of 2 variables. - See also: 1/ plotfunc 2/ plotcontour - Ex1:densityplot(x^2-y^2,[x=-2..2,y=-2..2],xstep=0.1,ystep=0.1) - Ex2:densityplot(x^2-y^2,[x=-2..2,y=-2..2],z=-2..2,xstep=0.1,ystep=0.1) - ''' - return GiacMethods['densityplot'](self, *args) - - def departures(self, *args): - r'''From Giac's documentation: - Help for departures: - departures(Graph(G),[Vrtx(v)]) - Returns the list of vertices of digraph G which are connected by v with arcs such that tails are in v. If v is omitted, a list of departures is computed for every vertex in G. - See also: 1/ out_degree - Ex1:departures(digraph(%{[1,2],[1,3],[2,3]%}),1) - ''' - return GiacMethods['departures'](self, *args) - - def derive(self, *args): - r'''From Giac's documentation: - Help for derive: - derive(Expr or Fnc,[SeqVar or LstVar],[n]) - Returns the derivative with respect to the 2nd argument. - See also: 1/ ' 2/ function_diff 3/ integrate 4/ taux_accroissement 5/ implicitdiff - Ex1:derive(x^3-x) - Ex2:derive(x^3-x,x,3) - Ex3:derive(x^3-x,quote(x)$3) - Ex4:derive((diff@@3)('x^3-x')) - Ex5:derive(x*y+z*y,y) - Ex6:derive(x*y+z*y,y,z) - Ex7:derive(x*y+z*y,[y,z]) - Ex8: f(x):=sin(2x);g:=diff(f);h:=diff(diff(f)) - ''' - return GiacMethods['derive'](self, *args) - - def deriver(self, *args): - r'''From Giac's documentation: - Help for deriver: - deriver(Expr or Fnc,[SeqVar or LstVar],[n]) - Returns the derivative with respect to the 2nd argument. - See also: 1/ ' 2/ function_diff 3/ integrate 4/ taux_accroissement 5/ implicitdiff - Ex1:deriver(x^3-x) - Ex2:deriver(x^3-x,x,3) - Ex3:deriver(x^3-x,quote(x)$3) - Ex4:deriver((diff@@3)('x^3-x')) - Ex5:deriver(x*y+z*y,y) - Ex6:deriver(x*y+z*y,y,z) - Ex7:deriver(x*y+z*y,[y,z]) - Ex8: f(x):=sin(2x);g:=diff(f);h:=diff(diff(f)) - ''' - return GiacMethods['deriver'](self, *args) - - def desolve(self, *args): - r'''From Giac's documentation: - Help for desolve: - desolve(Eq,[TimeVar],FncVar) - Solves a differential equation or a differential linear system with constant coefficients. - See also: 1/ integrate 2/ diff 3/ odesolve 4/ plotode 5/ plotfiefd - Ex1:desolve(y'+x*y=0) - Ex2:desolve(y'+x*y=0,y) - Ex3:desolve(y'+x*y=0,[0,1]) - Ex4:desolve([y'+x*y=0,y(0)=1],y) - Ex5:desolve([y'=[[1,2],[2,1]]*y+[x,x+1],y(0)=[1,2]]) - Ex6:desolve(y''+y=0,y) - Ex7:desolve([y''+y=sin(x),y(0)=1,y'(0)=2],y) - Ex8:desolve([y''+y=sin(x),y(0)=1,y'(0)=2],x,y) - Ex9:desolve([y''+y=sin(x),y(0)=1,y'(0)=2],[x,y]) - Ex10:desolve(diff(y(t),t)+t*y(t)=0,t,y) - Ex11:desolve(diff(y(t),t)+t*y(t)=0,[t,y]) - Ex12:desolve((y''+y=sin(x)) and (y(0)=1) and (y'(0)=2),y) - Ex13:desolve([z''+2*z'+z,z(0)=1,z'(0)=0],u,z) - Ex14:desolve([z''+2*z'+z,z(0)=1,z'(0)=0],z(u)) - Ex15:desolve([z'=[[1,2],[2,1]]*z+[t,t+1],z(0)=[1,2]],t,z) - Ex16:desolve([z'=[[1,2],[2,1]]*z+[t,t+1],z(0)=[1,2]],z(t)) - ''' - return GiacMethods['desolve'](self, *args) - - def dessine_tortue(self, *args): - r'''From Giac's documentation: - Help for dessine_tortue: - dessine_tortue([Intg(n)]) - Draws the full (or not full if n=1) triangle representing the turtle. - See also: 1/ crayon - Ex1:dessine_tortue() - Ex2:dessine_tortue(0) - Ex3:dessine_tortue(1) - ''' - return GiacMethods['dessine_tortue'](self, *args) - - def det(self, *args): - r'''From Giac's documentation: - Help for det: - det(Mtrx) - Determinant of a square matrix M. - See also: 1/ rref 2/ det_minor 3/ Det - Ex1:det([[1,2],[3,4]]) - Ex2:det([[1,2,3],[1,3,6],[2,5,7]]) - ''' - return GiacMethods['det'](self, *args) - - def det_minor(self, *args): - r'''From Giac's documentation: - Help for det_minor: - det_minor(Mtrx(A)) - Returns the determinant calculated with the calculus of minors. - See also: 1/ det - Ex1:det_minor([[1,2],[3,4]]) - ''' - return GiacMethods['det_minor'](self, *args) - - def developper(self, *args): - r'''From Giac's documentation: - Help for developper: - developper(Expr) - Full distribution of * and / over + and -. - See also: 1/ texpand 2/ normal 3/ simplify 4/ ratnormal - Ex1:developper((x+y)*(z+1)) - Ex2:developper((a+b+c)/d) - Ex3:developper((y+x)*(z+y)*(x+z)) - Ex4:developper((x+3)^4) - Ex5:developper((2*x-2*1)*(x^2-3*x+2)+(x^2-2*x+3)*(2*x-3*1)) - ''' - return GiacMethods['developper'](self, *args) - - def developper_transcendant(self, *args): - r'''From Giac's documentation: - Help for developper_transcendant: - developper_transcendant(Expr) - Expands transcendental expressions. - See also: 1/ tcollect 2/ tlin 3/ lin 4/ trigexpand - Ex1:developper_transcendant(sin(2*x)+exp(x+y)) - Ex2:developper_transcendant(cos(x+y)) - Ex3:developper_transcendant(cos(3*x)) - ''' - return GiacMethods['developper_transcendant'](self, *args) - - def dfc(self, *args): - r'''From Giac's documentation: - Help for dfc: - dfc(Real(x0),Int(n)||Real(eps)) - Returns the continued fraction development at x0 of order n or with precision eps. - See also: 1/ dfc2f 2/ convert - Ex1:dfc(sqrt(2),5) - Ex2:dfc(pi,4) - Ex3:dfc(evalf(pi),1e-09) - Ex4: convert(sqrt(2),confrac,'dev');dev - Ex5: convert(9976/6961,confrac,'l');l - ''' - return GiacMethods['dfc'](self, *args) - - def dfc2f(self, *args): - r'''From Giac's documentation: - Help for dfc2f: - dfc2f(LstFrac_Cont)) - Converts a continued fraction into a real. - See also: 1/ dfc 2/ convert - Ex1:dfc2f([1,1,1]) - Ex2:dfc2f([1,2,[2]]) - ''' - return GiacMethods['dfc2f'](self, *args) - - def diag(self, *args): - r'''From Giac's documentation: - Help for diag: - diag(Lst(l)||(Mtrx(A),[left||right||lu])||Lst(l),Lst(d),Lst(u)) - With 1 argument returns either the diagonal matrix with diagonal l or the diagonal of A, with 2 arguments returns the large left (lower) triangular part of A or the large right (upper) triangular part of A or factors A into 3 parts : strict left (lower) triangular, diagonal, strict right (upper) triangular and with 3 arguments returns the tridiagonal matrix with diagonals l,d,u. - See also: 1/ identity 2/ lu 3/ BlockDiagonal 4/ upper 5/ lower - Ex1:diag([[1,2],[3,4]]) - Ex2:diag([1,2,3]) - Ex3:diag([[1,2],[3,4]],left) - Ex4:diag([[1,2],[3,4]],right) - Ex5:diag([[1,2],[3,4]],lu) - Ex6:diag([1,2],[3,4,5],[6,7]) - ''' - return GiacMethods['diag'](self, *args) - - def diff(self, *args): - r'''From Giac's documentation: - Help for diff: - diff(Expr or Fnc,[SeqVar or LstVar],[n]) - Returns the derivative with respect to the 2nd argument. - See also: 1/ ' 2/ function_diff 3/ integrate 4/ taux_accroissement 5/ implicitdiff - Ex1:diff(x^3-x) - Ex2:diff(x^3-x,x,3) - Ex3:diff(x^3-x,quote(x)$3) - Ex4:diff((diff@@3)('x^3-x')) - Ex5:diff(x*y+z*y,y) - Ex6:diff(x*y+z*y,y,z) - Ex7:diff(x*y+z*y,[y,z]) - Ex8: f(x):=sin(2x);g:=diff(f);h:=diff(diff(f)) - ''' - return GiacMethods['diff'](self, *args) - - def digraph(self, *args): - r'''From Giac's documentation: - Help for digraph: - digraph([Lst(V)],[Set(E)],[Mtrx(A)],[options]) - Create a directed (un)weighted graph from vertices V, edges E and/or adjacency or weight matrix A. All parameters are optional. - See also: 1/ graph 2/ trail - Ex1:digraph(%{[1,2],[2,3],[3,4],[4,1]%}) - Ex2:digraph([a,b,c,d],%{[[a,b],1.0],[[a,c],2.3],[[b,d],3.1],[[c,d],4]%}) - ''' - return GiacMethods['digraph'](self, *args) - - def dijkstra(self, *args): - r'''From Giac's documentation: - Help for dijkstra: - dijkstra(Graph(G),Vrtx(v),[Vrtx(w)||Lst(W)]) - Returns the cheapest weighted path from vertex v to w (or to vertices from W) in undirected graph G. Output is in the form [[v1,v2,...,vk],d] (or list of these) where v1,v2,...,vk are vertices along each path and d is the weight of the path. - See also: 1/ allpairs_distance 2/ shortest_path - Ex1:dijkstra(graph(%{[[1,2],1],[[2,3],3],[[3,4],7],[[4,5],3],[[5,6],3],[[1,6],3]%}),1,4) - ''' - return GiacMethods['dijkstra'](self, *args) - - def dim(self, *args): - r'''From Giac's documentation: - Help for dim: - dim(Mtrx) - Returns the list which gives the dimension of the matrix specified as argument. - See also: 1/ rowdim 2/ coldim 3/ sizes 4/ size - Ex1:dim([[1,2,3],[4,5,6]]) - ''' - return GiacMethods['dim'](self, *args) - - def directed(self, *args): - r'''From Giac's documentation: - Help for directed: - directed(Opt) - Option for graph and digraph commands. - See also: 1/ weighted 2/ graph 3/ digraph - ''' - return GiacMethods['directed'](self, *args) - - def discard_edge_attribute(self, *args): - r'''From Giac's documentation: - Help for discard_edge_attribute: - discard_edge_attribute(Graph(G),Edge(e),Seq(tag1=value1,tag2=value2,..)) - Discards the attributes with tags tag1, tag2, ... assigned to edge e in G and returns the modified copy of G. - See also: 1/ get_edge_attribute 2/ set_edge_attribute 3/ list_edge_attributes - Ex1:discard_edge_attribute(cycle_graph(3),[1,2],"cost") - ''' - return GiacMethods['discard_edge_attribute'](self, *args) - - def discard_graph_attribute(self, *args): - r'''From Giac's documentation: - Help for discard_graph_attribute: - discard_graph_attribute(Graph(G),Seq(tag1=value1,tag2=value2,..)) - Discards the graph attributes with tags tag1, tag2, ... and returns the modified copy of G. - See also: 1/ get_graph_attribute 2/ set_graph_attribute 3/ list_graph_attributes - Ex1:discard_graph_attribute(cycle_graph(3),"name") - ''' - return GiacMethods['discard_graph_attribute'](self, *args) - - def discard_vertex_attribute(self, *args): - r'''From Giac's documentation: - Help for discard_vertex_attribute: - discard_vertex_attribute(Graph(G),Vrtx(v),Seq(tag1=value1,tag2=value2,..)) - Discards the attributes with tags tag1, tag2, ... assigned to vertex v in G and returns the modified copy of G. - See also: 1/ get_vertex_attribute 2/ set_vertex_attribute 3/ list_vertex_attributes - Ex1:discard_vertex_attribute(cycle_graph(3),1,"supply") - ''' - return GiacMethods['discard_vertex_attribute'](self, *args) - - def disjoint_union(self, *args): - r'''From Giac's documentation: - Help for disjoint_union: - disjoint_union(Seq(G1,G2,...)) - Returns the disjoint union of the graphs G1, G2, ... Vertices in the resulting graph are labelled with "k:v", where k is index of the corresponding k-th graph Gk and v is vertex in Gk. - See also: 1/ graph_join 2/ graph_union - Ex1:disjoint_union(is_connected(disjoint_union(cycle_graph(5),path_graph(4)))) - ''' - return GiacMethods['disjoint_union'](self, *args) - - def display(self, *args): - r'''From Giac's documentation: - Help for display: - display([GeoObj or legende],Intg) - Draws a geometrical object with colors black=0 red=1 green=2 yellow=3 blue=4, filled with the color in the interior of a closed curve,line_width_n (0=0 or, the (-n)th previous question if n<0 (by default n=-1 for the previous question). - See also: 1/ ans - Ex1:entry() - Ex2:entry(2) - Ex3:entry(-2) - ''' - return GiacMethods['entry'](self, *args) - - def envelope(self, *args): - r'''From Giac's documentation: - Help for envelope: - envelope(Expr(Xpr),Var(t)||[x,y,t]) - Returns the envelope of the curves with equation Xpr=0 as t varies. - See also: 1/ tangent 2/ locus - Ex1:envelope(y+x*tan(t)-2*sin(t),t) - Ex2:envelope(v+u*tan(t)-3*sin(t),[u,v,t]) - ''' - return GiacMethods['envelope'](self, *args) - - def epsilon(self, *args): - r'''From Giac's documentation: - Help for epsilon: - epsilon(NULL) - Returns the value of epsilon of the CAS configuration. - See also: 1/ epsilon2zero - Ex1:epsilon() - ''' - return GiacMethods['epsilon'](self, *args) - - def epsilon2zero(self, *args): - r'''From Giac's documentation: - Help for epsilon2zero: - epsilon2zero(Expr) - Values < epsilon are replaced by zero. - See also: 1/ evalf - Ex1:epsilon2zero(1e-13+x+5) - ''' - return GiacMethods['epsilon2zero'](self, *args) - - def equal(self, *args): - r'''From Giac's documentation: - Help for equal: - equal(Expr,Expr) - Prefixed version of =. - See also: 1/ = 2/ equal2diff 3/ equal2list 4/ left 5/ right - Ex1: 2*x=4 - Ex2:equal(2*x,4) - Ex3:equal(x^2-3x+2,0) - ''' - return GiacMethods['equal'](self, *args) - - def equal2diff(self, *args): - r'''From Giac's documentation: - Help for equal2diff: - equal2diff(Equal) - A=B or equal(A,B) is converted into the difference A-B. - See also: 1/ left 2/ right 3/ equal2list 4/ equal 5/ = - Ex1:equal2diff(x=2) - Ex2:equal2diff(equal(x,2)) - ''' - return GiacMethods['equal2diff'](self, *args) - - def equal2list(self, *args): - r'''From Giac's documentation: - Help for equal2list: - equal2list(Equal) - A=B or equal(A,B) is converted into the list [A,B]. - See also: 1/ left 2/ right 3/ equal2diff 4/ equal 5/ = - Ex1:equal2list(x=2) - Ex2:equal2list(equal(x,2)) - ''' - return GiacMethods['equal2list'](self, *args) - - def equation(self, *args): - r'''From Giac's documentation: - Help for equation: - equation(GeoObj, VectParam) - equation returns the cartesian equation of a curve. - See also: 1/ parameq - Ex1:equation(line(1-i,i),[x,y]) - ''' - return GiacMethods['equation'](self, *args) - - def equilateral_triangle(self, *args): - r'''From Giac's documentation: - Help for equilateral_triangle: - equilateral_triangle((Pnt(A) or Cplx),(Pnt(B) or Cplx),[Pnt(P)],[Var(C)]) - equilateral_triangle(A,B) (resp equilateral_triangle(A,B,P)) draws the direct equilateral triangle ABC with side AB (resp in the half-plane ABP). - See also: 1/ triangle - Ex1:equilateral_triangle(point(1+i),0) - Ex2:equilateral_triangle(0,1+i,C) - Ex3:equilateral_triangle(point(0,0,0),point(3,3,3),point(0,0,3)) - Ex4:equilateral_triangle(point(0,0,0),point(3,3,3),point(0,0,3),C) - ''' - return GiacMethods['equilateral_triangle'](self, *args) - - def erf(self, *args): - r'''From Giac's documentation: - Help for erf: - erf(Real(x0)) - Returns the approximate value of 2/sqrt(pi)*int(exp(-t^2),t,0,x0). - See also: 1/ erfc - Ex1:erf(1) - Ex2:erf(1/(sqrt(2)))*1/2 - ''' - return GiacMethods['erf'](self, *args) - - def erfc(self, *args): - r'''From Giac's documentation: - Help for erfc: - erfc(Real(x0)) - Returns the approximate value of 2/sqrt(pi)*int(exp(-t^2),t,x0,+infinity). - See also: 1/ erf - Ex1:erfc(1) - Ex2:erfc(1/(sqrt(2)))*1/2 - ''' - return GiacMethods['erfc'](self, *args) - - def error(self, *args): - r'''From Giac's documentation: - Help for error: - error(Str) - Generates the display of an error in a program. - See also: 1/ try 2/ catch - Ex1:error("Argument should be integer") - Ex2:error("je provoque une erreur") - ''' - return GiacMethods['error'](self, *args) - - def est_permu(self, *args): - r'''From Giac's documentation: - Help for est_permu: - est_permu(Lst) - Returns 1 if the argument is a permutation and 0 otherwise. - See also: 1/ is_cycle 2/ permu2cycles - Ex1:est_permu([4,2,3,1]) - Ex2:est_permu([4,2,3,1,0]) - ''' - return GiacMethods['est_permu'](self, *args) - - def euler(self, *args): - r'''From Giac's documentation: - Help for euler: - euler(Intg(n)) - Euler's function (euler(n)=card({p=0):; euler_lagrange(sqrt((1+y'^2)/y),t,y) - ''' - return GiacMethods['euler_lagrange'](self, *args) - - def eval_level(self, *args): - r'''From Giac's documentation: - Help for eval_level: - eval_level([Intg(n)]) - Evaluation level in interactive mode. - Ex1:eval_level() - Ex2:eval_level(1) - Ex3: purge(a,b,c);eval_level(1);a:=b+1; b:=c+1;c:=3; - Ex4: purge(a,b,c);eval_level(2);a:=b+1; b:=c+1;c:=3; - Ex5: purge(a,b,c);eval_level(3);a:=b+1; b:=c+1;c:=3; - ''' - return GiacMethods['eval_level'](self, *args) - - def evala(self, *args): - r'''From Giac's documentation: - Help for evala: - evala(Expr) - Simplifies the expression. - See also: 1/ simplify - Ex1:evala(2*x+y=1) - Ex2:evala(2*x*2) - Ex3:evala((2*x+1)^2) - ''' - return GiacMethods['evala'](self, *args) - - def evalb(self, *args): - r'''From Giac's documentation: - Help for evalb: - evalb(Expr) - Boolean evaluation of the argument. - See also: 1/ evalf 2/ eval - Ex1:evalb(a==2) - Ex2:evalb(sqrt(2)+pi>a) - ''' - return GiacMethods['evalb'](self, *args) - - def evalc(self, *args): - r'''From Giac's documentation: - Help for evalc: - evalc(Expr) - Returns a complex expression simplified to the format real+i*imag. - See also: 1/ normal - Ex1:evalc(-3+4*i+exp(i)) - Ex2:evalc(1/(x+y*i)) - ''' - return GiacMethods['evalc'](self, *args) - - def evalf(self, *args): - r'''From Giac's documentation: - Help for evalf: - evalf(Expr,[Int]) - Numerical evaluation of the first argument (we can give the number of digits as second argument). - See also: 1/ evalb 2/ eval - Ex1:evalf(2/3) - Ex2:evalf(2/3,2) - Ex3:evalf(2*sin(1)) - Ex4:evalf(2*sin(1),40) - Ex5:evalf(sqrt(2)+pi) - Ex6:evalf(sqrt(2)+pi,30) - ''' - return GiacMethods['evalf'](self, *args) - - def evalm(self, *args): - r'''From Giac's documentation: - Help for evalm: - evalm(Expr) - Evaluates its argument. - See also: 1/ evalf - Ex1:evalm(2*sin(pi)) - ''' - return GiacMethods['evalm'](self, *args) - - def even(self, *args): - r'''From Giac's documentation: - Help for even: - even(Intg(n)) - Returns 1 if the integer is even, else returns 0. - See also: 1/ odd - Ex1:even(6) - Ex2:even(1251) - ''' - return GiacMethods['even'](self, *args) - - def evolute(self, *args): - r'''From Giac's documentation: - Help for evolute: - evolute(Curve) - Evolute of a curve C. - See also: 1/ curvature 2/ osculating_circle - Ex1:evolute(plot(x^2)) - Ex2:evolute([t,t^2],t) - Ex3:evolute([3*exp(t/2)*cos(t),3*exp(t/2)*sin(t)],t) - ''' - return GiacMethods['evolute'](self, *args) - - def exact(self, *args): - r'''From Giac's documentation: - Help for exact: - exact(Expr) - Converts the expression to a rational or real expression. - See also: 1/ - Ex1:exact(-2) - Ex2:exact(1.5) - Ex3:exact(1.4141) - Ex4:exact(0.156381102937) - ''' - return GiacMethods['exact'](self, *args) - - def exbisector(self, *args): - r'''From Giac's documentation: - Help for exbisector: - exbisector((Pnt(A) or Cplx),(Pnt(B) or Cplx),(Pnt(C) or Cplx)) - Draws the exterior bisector of the angle (AB,AC) given by 3 points A,B,C. - See also: 1/ angle 2/ bisector - Ex1:exbisector(0,1,i) - ''' - return GiacMethods['exbisector'](self, *args) - - def excircle(self, *args): - r'''From Giac's documentation: - Help for excircle: - excircle((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - excircle(A,B,C) draws the A-excircle of the triangle ABC. - See also: 1/ incircle 2/ circumcircle - Ex1:excircle(0,1,1+i) - ''' - return GiacMethods['excircle'](self, *args) - - def execute(self, *args): - r'''From Giac's documentation: - Help for execute: - execute(Str) - Instruction transforming a string into a command or into a number. - See also: 1/ string - Ex1:execute("ifactor(54)") - Ex2:execute("123") - Ex3:execute("0123") - Ex4:execute(sin,x) - ''' - return GiacMethods['execute'](self, *args) - - def exp(self, *args): - r'''From Giac's documentation: - Help for exp: - exp(Expr or Opt) - Exponential or option of the convert or convertir command (id trig2exp). - See also: 1/ ln 2/ convert 3/ trig2exp - Ex1:exp(0) - Ex2: convert(cos(x),exp) - ''' - return GiacMethods['exp'](self, *args) - - def exp2list(self, *args): - r'''From Giac's documentation: - Help for exp2list: - exp2list(Expr) - Returns the list made with the righthand member of (var=expr0 or var=expr1), to be used after solve in TI mode. - See also: 1/ list2exp - Ex1:exp2list((x=2) or (x=0)) - Ex2:exp2list((x=3 and y=9) or (x=-1 and y=1) ) - ''' - return GiacMethods['exp2list'](self, *args) - - def exp2pow(self, *args): - r'''From Giac's documentation: - Help for exp2pow: - exp2pow(Expr) - Transforms exp(n*ln(x)) to x^n. - See also: 1/ pow2exp - Ex1:exp2pow(exp(3*ln(x))) - Ex2:exp2pow(exp(x*ln(x))) - ''' - return GiacMethods['exp2pow'](self, *args) - - def exp2trig(self, *args): - r'''From Giac's documentation: - Help for exp2trig: - exp2trig(Expr) - Transforms the complex exponential into sine and cosine. - See also: 1/ trig2exp 2/ atrig2ln - Ex1:exp2trig(exp(i*x)) - Ex2:exp2trig(exp(-i*x)) - ''' - return GiacMethods['exp2trig'](self, *args) - - def expand(self, *args): - r'''From Giac's documentation: - Help for expand: - expand(Expr) - Full distribution of * and / over + and -. - See also: 1/ texpand 2/ normal 3/ simplify 4/ ratnormal - Ex1:expand((x+y)*(z+1)) - Ex2:expand((a+b+c)/d) - Ex3:expand((y+x)*(z+y)*(x+z)) - Ex4:expand((x+3)^4) - Ex5:expand((2*x-2*1)*(x^2-3*x+2)+(x^2-2*x+3)*(2*x-3*1)) - ''' - return GiacMethods['expand'](self, *args) - - def expexpand(self, *args): - r'''From Giac's documentation: - Help for expexpand: - expexpand(Expr) - Expands exponentials. - See also: 1/ texpand 2/ lnexpand 3/ trigexpand - Ex1:expexpand(exp(3*x)) - ''' - return GiacMethods['expexpand'](self, *args) - - def expln(self, *args): - r'''From Giac's documentation: - Help for expln: - expln(Opt) - Option of the convert or convertir command (id trig2exp). - See also: 1/ exp 2/ ln 3/ convert 4/ trig2exp - Ex1: convert(cos(x),expln) - ''' - return GiacMethods['expln'](self, *args) - - def exponential(self, *args): - r'''From Giac's documentation: - Help for exponential: - exponential(Real(lambda),Real(x)) - Returns the probability density at x of the exponential law of parameter lambda. - See also: 1/ exponential_cdf 2/ exponential_icdf 3/ randvector 4/ ranm - Ex1:exponential(2.1,3.5) - Ex2:exponential(2.1,0.5) - Ex3: randvector(3,exponential,1.2) - Ex4: ranm(4,3,exponential,1.2) - ''' - return GiacMethods['exponential'](self, *args) - - def exponential_cdf(self, *args): - r'''From Giac's documentation: - Help for exponential_cdf: - exponential_cdf(Real(lambda),Real(x0),[Real(y0)]) - Returns the probability that a exponential random variable of parameter lambda is less than x0 (or between x0 and y0). - See also: 1/ exponentiald 2/ exponential_icdf - Ex1:exponential_cdf(4.2,2.1) - Ex2:exponential_cdf(4.2,2.1,3.2) - ''' - return GiacMethods['exponential_cdf'](self, *args) - - def exponential_icdf(self, *args): - r'''From Giac's documentation: - Help for exponential_icdf: - exponential_icdf(Real(lambda),Real(x0),Real(p)) - Returns h such that the probability that a exponential random variable of parameter lambda is less than h is p (0<=p<=1). - See also: 1/ exponential_cdf 2/ exponentiald - Ex1:exponential_icdf(4.2,0.95) - Ex2:exponential_icdf(4.2,0.6) - ''' - return GiacMethods['exponential_icdf'](self, *args) - - def exponential_regression(self, *args): - r'''From Giac's documentation: - Help for exponential_regression: - exponential_regression(Lst||Mtrx(A),[Lst]) - Returns the coefficients (a,b) of y=b*a^x : it is the best exponential which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ logarithmic_regression - Ex1:exponential_regression([[1.0,2.0],[0.0,1.0],[4.0,7.0]]) - Ex2:exponential_regression([1.0,0.0,4.0],[2.0,1.0,7.0]) - ''' - return GiacMethods['exponential_regression'](self, *args) - - def exponential_regression_plot(self, *args): - r'''From Giac's documentation: - Help for exponential_regression_plot: - exponential_regression_plot(Lst||Mtrx(A),[Lst]) - Returns the plot of y=b*a^x : it is the best exponential which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ logarithmic_regression_plot - Ex1:exponential_regression_plot([[1.0,2.0],[0.0,1.0],[4.0,7.0]]) - Ex2:exponential_regression_plot([1.0,0.0,4.0],[2.0,1.0,7.0]) - ''' - return GiacMethods['exponential_regression_plot'](self, *args) - - def exponentiald(self, *args): - r'''From Giac's documentation: - Help for exponentiald: - exponentiald(Real(lambda),Real(x)) - Returns the probability density at x of the exponential law of parameter lambda. - See also: 1/ exponential_cdf 2/ exponential_icdf 3/ randvector 4/ ranm - Ex1:exponentiald(2.1,3.5) - Ex2:exponentiald(2.1,0.5) - Ex3: randvector(3,exponential,1.2) - Ex4: ranm(4,3,exponential,1.2) - ''' - return GiacMethods['exponentiald'](self, *args) - - def exponentiald_cdf(self, *args): - r'''From Giac's documentation: - Help for exponentiald_cdf: - exponentiald_cdf(Real(lambda),Real(x0),[Real(y0)]) - Returns the probability that a exponential random variable of parameter lambda is less than x0 (or between x0 and y0). - See also: 1/ exponentiald 2/ exponential_icdf - Ex1:exponentiald_cdf(4.2,2.1) - Ex2:exponentiald_cdf(4.2,2.1,3.2) - ''' - return GiacMethods['exponentiald_cdf'](self, *args) - - def exponentiald_icdf(self, *args): - r'''From Giac's documentation: - Help for exponentiald_icdf: - exponentiald_icdf(Real(lambda),Real(x0),Real(p)) - Returns h such that the probability that a exponential random variable of parameter lambda is less than h is p (0<=p<=1). - See also: 1/ exponential_cdf 2/ exponentiald - Ex1:exponentiald_icdf(4.2,0.95) - Ex2:exponentiald_icdf(4.2,0.6) - ''' - return GiacMethods['exponentiald_icdf'](self, *args) - - def export_graph(self, *args): - r'''From Giac's documentation: - Help for export_graph: - export_graph(Graph(G),Str("path/to/graphname")) - Writes G to the file 'graphname.dot' in directory 'path/to' in dot format, returns 1 on success and 0 on failure. - See also: 1/ import_graph - Ex1:export_graph(complete_graph(5),"K5") - ''' - return GiacMethods['export_graph'](self, *args) - - def export_mathml(self, *args): - r'''From Giac's documentation: - Help for export_mathml: - export_mathml(Expr,[display||content]) - Converts an expression to presentation or content MathML block. - See also: 1/ mathml 2/ latex - Ex1:export_mathml(a+2*b) - Ex2:export_mathml(a+2*b,display) - Ex3:export_mathml(a+2*b,content) - ''' - return GiacMethods['export_mathml'](self, *args) - - def expovariate(self, *args): - r'''From Giac's documentation: - Help for expovariate: - expovariate(Real(a)) - Returns a random real according to the exponential distribution with parameter a>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:expovariate(1) - Ex2:expovariate(2) - ''' - return GiacMethods['expovariate'](self, *args) - - def expr(self, *args): - r'''From Giac's documentation: - Help for expr: - expr(Str) - Instruction transforming a string into a command or into a number. - See also: 1/ string - Ex1:expr("ifactor(54)") - Ex2:expr("123") - Ex3:expr("0123") - Ex4:expr(sin,x) - ''' - return GiacMethods['expr'](self, *args) - - def extend(self, *args): - r'''From Giac's documentation: - Help for extend: - extend(Lst,Lst||Seq,Seq||Str,Str||Mtrx,Mtrx) - Concatenates two lists or two strings or two sequences or 2 matrices; L:=concat(L,L1) or L.concat(L1). - See also: 1/ append 2/ cat 3/ semi_augment 4/ border 5/ + - Ex1:extend([1,2],[3,4,5]) - Ex2:extend("bon","jour") - Ex3:extend([[1,2],[3,4]],[[4,5,6],[6,7,8]]) - Ex4: L:=[1,2];L.concat([3,4,5]) - Ex5: S:="abcd";S.concat("efghi") - ''' - return GiacMethods['extend'](self, *args) - - def extract_measure(self, *args): - r'''From Giac's documentation: - Help for extract_measure: - extract_measure(Var) - extract_measure gives as answer the value calculated by the argument. - See also: 1/ angleatraw 2/ distanceatraw 3/ angleat 4/ distanceat 5/ slopeatraw 6/ areaatraw 7/ perimeteratraw 8/ slopeat 5/ areaat 10/ perimeterat - Ex1:extract_measure(distanceatraw(0,1+i,(1+i)/2)) - Ex2:extract_measure(angleatraw(0,1,1+i,1)) - Ex3: A:=point(0);B:=point(1+i);a:=distanceatraw(A,B,(1+i)/2);extract_measure(a) - ''' - return GiacMethods['extract_measure'](self, *args) - - def extrema(self, *args): - r'''From Giac's documentation: - Help for extrema: - extrema(Expr,Var,a,b) - Search extrema of an expression. - Ex1:extrema(-2*cos(x)-cos(x)^2,x) - Ex2:extrema((x^3-1)^4/(2x^3+1)^4,x=0..inf) - Ex3:extrema(x/2-2*sin(x/2),x=-12..12) - Ex4:extrema(x-ln(abs(x)),x) - Ex5: assume(a>=0):;extrema(x^2+a*x,x) - Ex6:extrema(x^7+3x^6+3x^5+x^4+2x^2-x,x) - Ex7:extrema((x^2+x+1)/(x^4+1),x) - Ex8:extrema(x^2+exp(-x),x) - Ex9:extrema(exp(-x)*ln(x),x) - Ex10:extrema(tan(x)*(x^3-5x^2+1),x=-0.5) - Ex11:extrema(tan(x)*(x^3-5x^2+1),x=0.5) - Ex12:extrema(exp(x^2-2x)*ln(x)*ln(1-x),x=0.5) - Ex13:extrema(ln(2+x-sin(x)^2),x=0..2*pi) - Ex14:extrema(x^3-2x*y+3y^4,[x,y]) - Ex15:extrema((2x^2-y)*(y-x^2),[x,y]) //Peano surface - Ex16:extrema(5x^2+3y^2+x*z^2-z*y^2,[x,y,z]) - Ex17:extrema(3*atan(x)-2*ln(x^2+y^2+1),[x,y]) - Ex18:extrema(x*y,x+y=1,[x,y]) - Ex19:extrema(sqrt(x*y),x+y=2,[x,y]) - Ex20:extrema(x*y,x^3+y^3=16,[x,y]) - Ex21:extrema(x^2+y^2,x*y=1,[x=0..inf,y=0..inf]) - Ex22:extrema(ln(x*y^2),2x^2+3y^2=8,[x,y]) - Ex23:extrema(y^2+4y+2x-x^2,x+2y=2,[x,y]) - Ex24: assume(a>0):;extrema(x/a^2+a*y^2,x+y=a,[x,y]) - Ex25:extrema(6x+3y+2z,4x^2+2y^2+z^2=70,[x,y,z]) - Ex26:extrema(x*y*z,x+y+z=1,[x,y,z]) - Ex27:extrema(x*y^2*z^2,x+y+z=5,[x,y,z]) - Ex28:extrema(4y-2z,[2x-y-z=2,x^2+y^2=1],[x,y,z]) - Ex29:extrema((x-3)^2+(y-1)^2+(z-1)^2,x^2+y^2+z^2=4,[x,y,z]) - Ex30:extrema(x+3y-z,2x^2+y^2=z,[x,y,z]) - Ex31:extrema(2x*y+2y*z+x*z,x*y*z=4,[x,y,z]) - Ex32:extrema(x+y+z,[x^2+y^2=1,2x+z=1],[x,y,z]) - Ex33: assume(a>0):;extrema(x+y+z,[y^2-x^2=a,x+2z=1],[x,y,z]) - Ex34:extrema((x-u)^2+(y-v)^2,[x^2/4+y^2/9=1,(u-3)^2+(v+5)^2=1],[u,v,x,y]) - Ex35:extrema(x2^6+x1^3+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) - Ex36:extrema(x*y,-2x^3+15x^2*y+11y^3-24y=0,[x,y]) - Ex37:extrema(x2^4-x1^4-x2^8+x1^10,[x1,x2],order=1) - Ex38:extrema(x2^4-x1^4-x2^8+x1^10,[x1,x2]) - Ex39:extrema(x2^6+x1^3+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) - Ex40:extrema(x2^6+x1^3+2x1^2-x2^2+4x1+4x2,x1^5+x2^4+x1+x2=0,[x1,x2]) - Ex41:extrema(3x^2-2x*y+y^2-8y,[x,y]) - Ex42:extrema(x^3+3x*y^2-15x-12y,[x,y]) - Ex43:extrema(4x*y-x^4-y^4,[x,y]) - Ex44:extrema(x*sin(y),[x,y]) - Ex45:extrema(x^4+y^4,[x,y]) - Ex46:extrema(x^3*y-x*y^3,[x,y]) - Ex47:extrema(x^2+y^2+z^2,x^4+y^4+z^4=1,[x,y,z]) - Ex48:extrema(3x+3y+8z,[x^2+z^2=1,y^2+z^2=1],[x,y,z]) - Ex49:extrema(2x^2+y^2,x^4-x^2+y^2=5,[x,y]) - Ex50:extrema((3x^4-4x^3-12x^2+18)/(12*(1+4y^2)),[x,y]) - Ex51:extrema(x-y+z,[x^2+y^2+z^2=1,x+y+2z=1],[x,y,z]) - Ex52:extrema(ln(x)+2*ln(y)+3*ln(z)+4*ln(u)+5*ln(v),x+y+z+u+v=1,[x,y,z,u,v]) - Ex53:extrema(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,y,z]) - Ex54:extrema(x+y-exp(x)-exp(y)-exp(x+y),[x,y]) - Ex55:extrema(x^2*sin(y)-4*x,[x,y]) - Ex56:extrema((1+y*sinh(x))/(1+y^2+tanh(x)^2),[x,y]) - Ex57:extrema((1+y*sinh(x))/(1+y^2+tanh(x)^2),y=x^2,[x,y]) - ''' - return GiacMethods['extrema'](self, *args) - - def ezgcd(self, *args): - r'''From Giac's documentation: - Help for ezgcd: - ezgcd(Poly,Poly) - GCD of 2 polynomials with at least 2 variables, with the ezgcd algorithm. - See also: 1/ gcd 2/ modgcd 3/ heugcd 4/ psrgcd - Ex1:ezgcd(x^2-2*xy+y^2-1,x-y) - Ex2:ezgcd((x+1)^4-y^4,(x+1-y)^2) - Ex3:ezgcd((x+y-1)*(x+y+1),(x+y+1)^2) - ''' - return GiacMethods['ezgcd'](self, *args) - - def f2nd(self, *args): - r'''From Giac's documentation: - Help for f2nd: - f2nd(Frac or RatFrac) - Returns the list built with the numerator and the denominator of the simplified fraction. - See also: 1/ simp2 2/ numer 3/ denom 4/ getNum 5/ getDenom - Ex1:f2nd(42/12) - Ex2:f2nd((x^2+2*x+1)/(x^2-1)) - ''' - return GiacMethods['f2nd'](self, *args) - - def fMax(self, *args): - r'''From Giac's documentation: - Help for fMax: - fMax(Expr,[Var]) - Returns the abscissa of the maximum of the expression. - See also: 1/ fMin - Ex1:fMax(-x^2+2*x+1,x) - Ex2:fMax(-x^2+2*x+1,x=1..2) - ''' - return GiacMethods['fMax'](self, *args) - - def fMin(self, *args): - r'''From Giac's documentation: - Help for fMin: - fMin(Expr,[Var]) - Returns the abscissa of the minimum of the expression. - See also: 1/ fMax - Ex1:fMin(x^2-2*x+1,x) - Ex2:fMin(x^2-2*x+1,x=1..2) - Ex3:fMin((x-3)^2+(y-5)^2+1,[],[x,y],[1,1]) - Ex4:fMin((x-3)^2+(y-5)^2+1,[x+y^2=1],[x,y],[1,1]) - ''' - return GiacMethods['fMin'](self, *args) - - def fPart(self, *args): - r'''From Giac's documentation: - Help for fPart: - fPart(Real||LstReal) - Returns the fractional part (if x<0 then frac(x)+floor(x)+1=x else frac(x)+floor(x)=x). - See also: 1/ floor 2/ iPart 3/ trunc - Ex1:fPart(1/2) - Ex2:fPart(-1/2) - Ex3:fPart(1.2) - Ex4:fPart(-1.2) - Ex5:fPart([3.4,sqrt(2)]) - ''' - return GiacMethods['fPart'](self, *args) - - def faces(self, *args): - r'''From Giac's documentation: - Help for faces: - faces(Polygon or Polyedr(P)) - Returns the list of the faces (1 face=matrix(n,3) where the n rows are the n vertices of the face) of the polyhedron P. - See also: 1/ polyhedron - Ex1:faces(polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6])) - Ex2:faces(polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6]))[2] - ''' - return GiacMethods['faces'](self, *args) - - def facteurs_premiers(self, *args): - r'''From Giac's documentation: - Help for facteurs_premiers: - facteurs_premiers(Intg(a) or LstIntg) - Returns the list of prime factors of an integer (each factor is followed by its multiplicity). - See also: 1/ ifactor 2/ factors - Ex1:facteurs_premiers(36) - Ex2:facteurs_premiers([36,52]) - ''' - return GiacMethods['facteurs_premiers'](self, *args) - - def factor(self, *args): - r'''From Giac's documentation: - Help for factor: - factor(Expr) - Factors a polynomial. - See also: 1/ ifactor 2/ cfactor 3/ partfrac 4/ normal - Ex1:factor(x^4-1) - Ex2:factor(x^4-4,sqrt(2)) - Ex3:factor(x^4+12*x^3+54*x^2+108*x+81) - ''' - return GiacMethods['factor'](self, *args) - - def factor_xn(self, *args): - r'''From Giac's documentation: - Help for factor_xn: - factor_xn(Poly(P)) - Factors x^n in P (n=degree of polynomial P). - See also: 1/ ifactor 2/ partfrac 3/ normal - Ex1:factor_xn(x^4-1) - Ex2:factor_xn(x^4+12*x^3+54*x^2+108*x+81) - ''' - return GiacMethods['factor_xn'](self, *args) - - def factorial(self, *args): - r'''From Giac's documentation: - Help for factorial: - factorial(Intg(n)|| Real(a)) - factorial(n)=n!. For non-integers, factorial(a)=a! = G(a + 1). This calculates the Gamma function. - See also: 1/ comb 2/ perm - Ex1:factorial(4) - Ex2:factorial(1.2) - ''' - return GiacMethods['factorial'](self, *args) - - def factoriser(self, *args): - r'''From Giac's documentation: - Help for factoriser: - factoriser(Expr) - Factors a polynomial. - See also: 1/ ifactor 2/ cfactor 3/ partfrac 4/ normal - Ex1:factoriser(x^4-1) - Ex2:factoriser(x^4-4,sqrt(2)) - Ex3:factoriser(x^4+12*x^3+54*x^2+108*x+81) - ''' - return GiacMethods['factoriser'](self, *args) - - def factoriser_entier(self, *args): - r'''From Giac's documentation: - Help for factoriser_entier: - factoriser_entier(Intg(a)) - Factorization of an integer into prime factors. - See also: 1/ factor 2/ ecm_factor - Ex1:factoriser_entier(50) - Ex2:factoriser_entier(123456789) - ''' - return GiacMethods['factoriser_entier'](self, *args) - - def factoriser_sur_C(self, *args): - r'''From Giac's documentation: - Help for factoriser_sur_C: - factoriser_sur_C(Expr) - Factorization of the expression in ℂ (on the Gaussian integers if there are more than 2 variables). - See also: 1/ factor - Ex1:factoriser_sur_C(x^2*y+y) - Ex2:factoriser_sur_C(x^2*y^2+y^2+4*x^2+4) - Ex3:factoriser_sur_C(x^2*y^2+y^2+2*x^2+2) - ''' - return GiacMethods['factoriser_sur_C'](self, *args) - - def factors(self, *args): - r'''From Giac's documentation: - Help for factors: - factors(Poly or LstPoly) - Returns the list of prime factors of a polynomial (each factor is followed by its multiplicity). - See also: 1/ factor 2/ ifactors - Ex1:factors(x^4-1) - Ex2:factors([x^2,x^2-1]) - ''' - return GiacMethods['factors'](self, *args) - - def fadeev(self, *args): - r'''From Giac's documentation: - Help for fadeev: - fadeev(Opt) - Option of the pcar or charpoly command to specify the algorithm. - See also: 1/ pcar - Ex1: pcar([[4,1,-2],[1,2,-1],[2,1,0]],fadeev) - ''' - return GiacMethods['fadeev'](self, *args) - - def false(self, *args): - r'''From Giac's documentation: - Help for false: - false() - Boolean equal to false or 0. - See also: 1/ true - Ex1: a:=false - ''' - return GiacMethods['false'](self, *args) - - def falsepos_solver(self, *args): - r'''From Giac's documentation: - Help for falsepos_solver: - falsepos_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['falsepos_solver'](self, *args) - - def fclose(self, *args): - r'''From Giac's documentation: - Help for fclose: - fclose(File(f)) - Closes the file f. - See also: 1/ fprint 2/ fopen - Ex1:fclose(f) - ''' - return GiacMethods['fclose'](self, *args) - - def fcoeff(self, *args): - r'''From Giac's documentation: - Help for fcoeff: - fcoeff(Lst(root||pole,order)) - Returns the polynomial described by the list (root or pole, order). - See also: 1/ pcoeff 2/ froot 3/ proot - Ex1:fcoeff([1,2,0,1,3,-1]) - ''' - return GiacMethods['fcoeff'](self, *args) - - def fdistrib(self, *args): - r'''From Giac's documentation: - Help for fdistrib: - fdistrib(Expr) - Full distribution of * and / over + and -. - See also: 1/ texpand 2/ normal 3/ simplify 4/ ratnormal - Ex1:fdistrib((x+y)*(z+1)) - Ex2:fdistrib((a+b+c)/d) - Ex3:fdistrib((y+x)*(z+y)*(x+z)) - Ex4:fdistrib((x+3)^4) - Ex5:fdistrib((2*x-2*1)*(x^2-3*x+2)+(x^2-2*x+3)*(2*x-3*1)) - ''' - return GiacMethods['fdistrib'](self, *args) - - def fft(self, *args): - r'''From Giac's documentation: - Help for fft: - fft(Vect or (Vect(L),Intg(a),Intg(p)) - Fast Fourier Transform in ℝ or in the field ℤ/pℤ, with a as primitive n-th root of 1 (n=size(L)). - See also: 1/ ifft - Ex1:fft([1,2,3,4,0,0,0,0]) - Ex2:fft(ranm(128),22798,35969) - ''' - return GiacMethods['fft'](self, *args) - - def fieldplot(self, *args): - r'''From Giac's documentation: - Help for fieldplot: - fieldplot(Expr,VectVar,[Opt]) - fieldplot(f(t,y),[t,y]) draws the plotfield of the diff equation y'=f(t,y). - See also: 1/ interactive_plotode 2/ odeplot 3/ odesolve 4/ desolve - Ex1:fieldplot(sin(t*y),[t=-5..5,y=-3..3],xstep=0.5,ystep=0.5) - Ex2:fieldplot(-t*y,[t,y]) - Ex3:fieldplot(-t*y,[t,y],normalize) - Ex4:fieldplot(-t*y,[t,y],normalize,xstep=0.5,ystep=0.5) - Ex5:fieldplot(-t*y,[t=-6.868..6.868,y=-6.868..6.868],normalize) - ''' - return GiacMethods['fieldplot'](self, *args) - - def find(self, *args): - r'''From Giac's documentation: - Help for find: - find(Expr,Vect) - List of positions of an object in a list, a string or a set. - See also: 1/ index 2/ member - Ex1:find(1,[3,x,1,2,1,3]) - Ex2:find(2,[0,1,3,2,4,2,5])[0] - Ex3:find("a","abracadabrant") - Ex4:find("ab","abracadabrant") - Ex5:find(1,%{4,3,1,2%}) - ''' - return GiacMethods['find'](self, *args) - - def find_cycles(self, *args): - r'''From Giac's documentation: - Help for find_cycles: - find_cycles(Graph(G,[length=k||l..u])) - Returns the list of elementary cycles of the digraph G. If option "length" is specified, only cycles of length k resp. of length between l and u are returned. - See also: 1/ is_acyclic - Ex1:find_cycles(digraph(%{[1,2],[1,3],[3,1],[1,4],[2,3],[4,3],[4,5],[5,3],[5,6],[7,6],[8,6],[8,7]%})) - Ex2:find_cycles(digraph(%{[1,2],[1,3],[3,1],[1,4],[2,3],[4,3],[4,5],[5,3],[5,6],[7,6],[8,6],[8,7]%}),length=3) - Ex3:find_cycles(digraph(%{[1,2],[1,3],[3,1],[1,4],[2,3],[4,3],[4,5],[5,3],[5,6],[7,6],[8,6],[8,7]%}),length=3..4) - ''' - return GiacMethods['find_cycles'](self, *args) - - def findhelp(self, *args): - r'''From Giac's documentation: - Help for findhelp: - findhelp(Cmd) - Returns help about the command (if ? is infixed see when) . - See also: 1/ ifte 2/ when - Ex1:findhelp(ifactor) - ''' - return GiacMethods['findhelp'](self, *args) - - def fisher(self, *args): - r'''From Giac's documentation: - Help for fisher: - fisher(Intg(n),Intg(m),Real(x0)) - Returns the probability density of the Fisher-Snedecor law (n and m are the numbers of degrees of freedom). - See also: 1/ fisher_cdf 2/ fisher_icdf 3/ randvector 4/ ranm - Ex1:fisher(4,10,2.1) - Ex2:fisher(4,4,2.1) - Ex3: randvector(5,fisher,4,6) - Ex4: ranm(2,3,fisher,4,6) - ''' - return GiacMethods['fisher'](self, *args) - - def fisher_cdf(self, *args): - r'''From Giac's documentation: - Help for fisher_cdf: - fisher_cdf(Intg(n),Intg(m),Real(x0)) - Returns the probability that a Fisher-Snedecor random variable is less than x0 (n and m are the numbers of degrees of freedom). - See also: 1/ UTPF 2/ fisher_icdf 3/ fisherd - Ex1:fisher_cdf(4,4,2.1) - Ex2:fisher_cdf(4,10,3.5) - ''' - return GiacMethods['fisher_cdf'](self, *args) - - def fisher_icdf(self, *args): - r'''From Giac's documentation: - Help for fisher_icdf: - fisher_icdf(Intg(n),Intg(m),Real(p)) - Returns h such as the probability that a Fisher-Snedecor random variable is less than h is p (n and m are the numbers of degrees of freedom and 0<=p<=1). - See also: 1/ fisher_cdf 2/ fisherd - Ex1:fisher_icdf(4,10,0.95) - Ex2:fisher_icdf(4,10,0.05) - ''' - return GiacMethods['fisher_icdf'](self, *args) - - def fisherd(self, *args): - r'''From Giac's documentation: - Help for fisherd: - fisherd(Intg(n),Intg(m),Real(x0)) - Returns the probability density of the Fisher-Snedecor law (n and m are the numbers of degrees of freedom). - See also: 1/ fisher_cdf 2/ fisher_icdf 3/ randvector 4/ ranm - Ex1:fisherd(4,10,2.1) - Ex2:fisherd(4,4,2.1) - Ex3: randvector(5,fisher,4,6) - Ex4: ranm(2,3,fisher,4,6) - ''' - return GiacMethods['fisherd'](self, *args) - - def fisherd_cdf(self, *args): - r'''From Giac's documentation: - Help for fisherd_cdf: - fisherd_cdf(Intg(n),Intg(m),Real(x0)) - Returns the probability that a Fisher-Snedecor random variable is less than x0 (n and m are the numbers of degrees of freedom). - See also: 1/ UTPF 2/ fisher_icdf 3/ fisherd - Ex1:fisherd_cdf(4,4,2.1) - Ex2:fisherd_cdf(4,10,3.5) - ''' - return GiacMethods['fisherd_cdf'](self, *args) - - def fisherd_icdf(self, *args): - r'''From Giac's documentation: - Help for fisherd_icdf: - fisherd_icdf(Intg(n),Intg(m),Real(p)) - Returns h such as the probability that a Fisher-Snedecor random variable is less than h is p (n and m are the numbers of degrees of freedom and 0<=p<=1). - See also: 1/ fisher_cdf 2/ fisherd - Ex1:fisherd_icdf(4,10,0.95) - Ex2:fisherd_icdf(4,10,0.05) - ''' - return GiacMethods['fisherd_icdf'](self, *args) - - def fitdistr(self, *args): - r'''From Giac's documentation: - Help for fitdistr: - fitdistr(Lst(L),Fnc(D)) - Returns the distribution of type D which fits most closely to the i.i.d. samples in the list L. - See also: 1/ normald 2/ poisson 3/ exponentiald 4/ geometric 5/ gammad 6/ betad 7/ cauchyd 8/ weibulld 9/ sample 10/ randvector 11/ randvar - Ex1:fitdistr(randvector(1000,weibulld,1/2,1),weibull) - Ex2: X:=randvar(normal,stddev=9.5):;Y:=randvar(normal,stddev=1.5):;S:=sample(eval(X/Y,0),1000):;Z:=fitdistr(S,cauchy) - Ex3: X:=randvar(normal,mean=5,variance=2):;S:=sample(exp(X),1000):;fitdistr(log(S),normal) - ''' - return GiacMethods['fitdistr'](self, *args) - - def flatten(self, *args): - r'''From Giac's documentation: - Help for flatten: - flatten(Lst) - Recursively flatten a list containing lists. - See also: 1/ mat2list - Ex1:flatten([[1,[2,3],4],[5,6]]) - ''' - return GiacMethods['flatten'](self, *args) - - def float2rational(self, *args): - r'''From Giac's documentation: - Help for float2rational: - float2rational(Expr) - Converts the expression to a rational or real expression. - See also: 1/ - Ex1:float2rational(-2) - Ex2:float2rational(1.5) - Ex3:float2rational(1.4141) - Ex4:float2rational(0.156381102937) - ''' - return GiacMethods['float2rational'](self, *args) - - def floor(self, *args): - r'''From Giac's documentation: - Help for floor: - floor(Real or Cplx) - Returns the greatest integer <= to the argument. - See also: 1/ round 2/ ceil 3/ iPart 4/ trunc - Ex1:floor(-2.5) - Ex2:floor(2.5-4.2*i) - ''' - return GiacMethods['floor'](self, *args) - - def flow_polynomial(self, *args): - r'''From Giac's documentation: - Help for flow_polynomial: - flow_polynomial(Graph(G),[Var(x)]) - Returns the flow polynomial [or its value at point x] of undirected unweighted graph G. - See also: 1/ chromatic_polynomial 2/ reliability_polynomial 3/ tutte_polynomial - Ex1:flow_polynomial(graph("tetrahedron")) - Ex2:flow_polynomial(graph("tetrahedron"),5) - ''' - return GiacMethods['flow_polynomial'](self, *args) - - def fmod(self, *args): - r'''From Giac's documentation: - Help for fmod: - fmod(Real(a),Real(b)) - Returns a mod b for a and b floats. - Ex1:fmod(10.0,pi) - ''' - return GiacMethods['fmod'](self, *args) - - def foldl(self, *args): - r'''From Giac's documentation: - Help for foldl: - foldl(op,id,Seq(r1,r2,...)) - Returns the composition of the binary operator or function op, with an identity or initial value id onto its arguments r1, r2, ..., associating from the left. - See also: 1/ apply 2/ foldr 3/ map - Ex1:foldl(F,init,a,b,c) - ''' - return GiacMethods['foldl'](self, *args) - - def foldr(self, *args): - r'''From Giac's documentation: - Help for foldr: - foldr(op,id,Seq(r1,r2,...)) - Returns the composition of the binary operator or function op, with an identity or initial value id onto its arguments r1, r2, ..., associating from the right. - See also: 1/ apply 2/ foldl 3/ map - Ex1:foldr(F,init,a,b,c) - ''' - return GiacMethods['foldr'](self, *args) - - def fonction_derivee(self, *args): - r'''From Giac's documentation: - Help for fonction_derivee: - fonction_derivee(Fnc(f)) - Returns the derivative function of the function f. - See also: 1/ diff 2/ ' 3/ @ - Ex1:fonction_derivee(sin+id) - Ex2:fonction_derivee(sq@sin+id) - Ex3:fonction_derivee(ln)(x,y) - Ex4:fonction_derivee(ln)([x,y]) - Ex5: (function_diff @@3)(ln)('x') - ''' - return GiacMethods['fonction_derivee'](self, *args) - - def forward(self, *args): - r'''From Giac's documentation: - Help for forward: - forward(NULL or Real(n)) - The turtle takes n steps forward (by default n=10). - See also: 1/ recule 2/ saute - Ex1: avance 30 - Ex2:forward(30) - ''' - return GiacMethods['forward'](self, *args) - - def fourier(self, *args): - r'''From Giac's documentation: - Help for fourier: - fourier(Expr(f(x)),[Var(x),[Var(s)]]) - Returns the Fourier transform F(s) of f(x). - See also: 1/ ifourier 2/ fourier_cn 3/ fft - Ex1:fourier(x/(x^3-19x+30),x,s) - Ex2:fourier((x^2+1)/(x^2-1),x,s) - Ex3:fourier(3x^2+2x+1,x,s) - Ex4:fourier(Dirac(x-1)+Dirac(x+1),x,s) - Ex5:fourier(exp(-2*abs(x-1)),x,s) - Ex6:fourier(atan(1/(2x^2)),x,s) - Ex7:fourier(BesselJ(3,x),x,s) - Ex8:fourier(sin(x)*sign(x),x,s) - Ex9:fourier(log(abs(x)),x,s) - Ex10:fourier(rect(x),x,s) - Ex11:fourier(exp(-abs(x))*sinc(x),x,s) - Ex12:fourier(1/sqrt(abs(x)),x,s) - Ex13:fourier(1/cosh(2x),x,s) - Ex14:fourier(Gamma(1+i*x/3),x,s) - Ex15:fourier(atan(x/4)/x,x,s) - Ex16:fourier(piecewise(x<=-1,exp(x+1),x<=1,1,exp(2-2x)),x,s) - ''' - return GiacMethods['fourier'](self, *args) - - def fourier_an(self, *args): - r'''From Giac's documentation: - Help for fourier_an: - fourier_an(Expr(f(x)),Var(x),Period(T),Intg(n),Real(a)) - Returns the n-th Fourier coefficient an=2/T*integrate(f(x)*cos(2*pi*n*x/T),a,a+T). - See also: 1/ fourier_cn 2/ fourier_bn 3/ assume - Ex1:fourier_an(x^2,x,2,0,-1) - Ex2:fourier_an(x^2,x,2,n,-1) - ''' - return GiacMethods['fourier_an'](self, *args) - - def fourier_bn(self, *args): - r'''From Giac's documentation: - Help for fourier_bn: - fourier_bn(Expr(f(x)),Var(x),Period(T),Intg(n),Real(a)) - Returns the n-th Fourier coefficient bn=2/T*integrate(f(x)*sin(2*pi*n*x/T),a,a+T). - See also: 1/ fourier_cn 2/ fourier_an 3/ assume - Ex1:fourier_bn(x^2,x,2,0,-1) - Ex2:fourier_bn(x^2,x,2,n,-1) - ''' - return GiacMethods['fourier_bn'](self, *args) - - def fourier_cn(self, *args): - r'''From Giac's documentation: - Help for fourier_cn: - fourier_cn(Expr(f(x)),Var(x),Period(T),Intg(n),Real(a)) - Returns the n-th Fourier coefficient cn=1/T*integrate(f(x)*exp(-2*i*pi*n*x/T),a,a+T). - See also: 1/ fourier_an 2/ fourier_bn 3/ assume - Ex1:fourier_cn(x^2,x,2,0,-1) - Ex2:fourier_cn(x^2,x,2,n,-1) - ''' - return GiacMethods['fourier_cn'](self, *args) - - def fprint(self, *args): - r'''From Giac's documentation: - Help for fprint: - fprint(File(f),Var,[Var,Var...]) - Writes in the file f some data. - See also: 1/ fopen 2/ fclose - Ex1:fprint(f,x+1,"2") - Ex2:fprint(f,"blabla") - Ex3:fprint(f,Unquoted,"blabla") - ''' - return GiacMethods['fprint'](self, *args) - - def frac(self, *args): - r'''From Giac's documentation: - Help for frac: - frac(Real||LstReal) - Returns the fractional part (if x<0 then frac(x)+floor(x)+1=x else frac(x)+floor(x)=x). - See also: 1/ floor 2/ iPart 3/ trunc - Ex1:frac(1/2) - Ex2:frac(-1/2) - Ex3:frac(1.2) - Ex4:frac(-1.2) - Ex5:frac([3.4,sqrt(2)]) - ''' - return GiacMethods['frac'](self, *args) - - def fracmod(self, *args): - r'''From Giac's documentation: - Help for fracmod: - fracmod(Expr(Xpr),Intg(n)) - Returns the fraction a/b such as b*Xpr=a mod n, -sqrt(n)/20),Real(b>0),Real(x>=0)) - Returns the probability density of the Gamma law (=x^(a-1)*exp(-b*x)*b^a/Gamma(a)). - See also: 1/ gammad_cdf; 2/ gammad_icdf - Ex1:gammad(2.2,1.5,0.8) - ''' - return GiacMethods['gammad'](self, *args) - - def gammad_cdf(self, *args): - r'''From Giac's documentation: - Help for gammad_cdf: - gammad_cdf(Real(a>0),Real(b>0),Real(x0>=0),[Real(y0>=0)]) - Returns the probability that a Gamma random variable (with a and b as parameters) is less than x0 or between x0 and y0. - See also: 1/ gammad 2/ gammad_icdf - Ex1:gammad_cdf(2,1,2.96) - Ex2:gammad_cdf(2,1,1.4,2.96) - ''' - return GiacMethods['gammad_cdf'](self, *args) - - def gammad_icdf(self, *args): - r'''From Giac's documentation: - Help for gammad_icdf: - gammad_icdf(Real(a>0),Real(b>0),Real(0<=p<=1)) - Returns h such that the probability that a Gamma random variable is less than h is p (0<=p<=1). - See also: 1/ gammad_cdf 2/ gammad - Ex1:gammad_icdf(2,1,0.95) - Ex2:gammad_icdf(2,1,0.5) - ''' - return GiacMethods['gammad_icdf'](self, *args) - - def gammavariate(self, *args): - r'''From Giac's documentation: - Help for gammavariate: - gammavariate(Real(a),Real(b)) - Returns a random real according to the Gamma distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:gammavariate(1,2) - Ex2:gammavariate(1.5,4) - ''' - return GiacMethods['gammavariate'](self, *args) - - def gauss(self, *args): - r'''From Giac's documentation: - Help for gauss: - gauss(Expr,VectVar) - Splits a quadratic form as a sum/difference of squares. - See also: 1/ cholesky - Ex1:gauss(x^2+2*a*x*y,[x,y]) - ''' - return GiacMethods['gauss'](self, *args) - - def gauss15(self, *args): - r'''From Giac's documentation: - Help for gauss15: - gauss15(Opt) - Option of the area command. - See also: 1/ area - Ex1: area(x^2,x=0..1,5,simpson) - Ex2: area(x^2,x=0..1,5,rombergt) - Ex3: area(x^2,x=0..1,5,rombergm) - Ex4:gauss15(area(x^2,x=0..1,5,gauss15)) - ''' - return GiacMethods['gauss15'](self, *args) - - def gauss_seidel_linsolve(self, *args): - r'''From Giac's documentation: - Help for gauss_seidel_linsolve: - gauss_seidel_linsolve([Real(omega)],Mtrx(A),Vect(b),Real(eps),[Int(maxiter)]) - Resolution of a linear system A*X=b by the iterative Gauss-Seidel method (by defaut omega=1) or by relaxation method, with eps as error margin and a number of iterations less than maxiter. - See also: 1/ jacobi_linsolve 2/ linsolve - Ex1: a:=[[100,2],[2,100]];gauss_seidel_linsolve(a,[0,1],1e-12); - Ex2: a:=[[100,2],[2,100]];gauss_seidel_linsolve(table(a),[0,1],1e-12); - Ex3: a:=[[100,2],[2,100]];gauss_seidel_linsolve(1.5,a,[0,1],1e-12); - Ex4: a:=[[100,2],[2,100]];gauss_seidel_linsolve(1.5,table(a),[0,1],1e-12); - ''' - return GiacMethods['gauss_seidel_linsolve'](self, *args) - - def gaussian_window(self, *args): - r'''From Giac's documentation: - Help for gaussian_window: - gaussian_window(Lst,[Real(a)],[Interval(n1..n2)]) - Applies the Gaussian windowing function with parameter 0= b (default a=0.2 and b=1/6.) - See also: 1/ gbasis 2/ gbasis_max_pairs 3/ gbasis_reinject - Ex1:gbasis_reinject(0.1) - Ex2:gbasis_reinject(0.1,0.05) - ''' - return GiacMethods['gbasis_reinject'](self, *args) - - def gbasis_simult_primes(self, *args): - r'''From Giac's documentation: - Help for gbasis_simult_primes: - gbasis_simult_primes(Intg) - Gbasis fine-tuning: maximal number of Groebner basis modulo a prime that are computed simultaneously to rebuild a Groebner basis over Q (default 16). Set it to a smaller value if short in memory. - See also: 1/ gbasis 2/ gbasis_max_pairs 3/ gbasis_reinject - Ex1:gbasis_simult_primes(3) - ''' - return GiacMethods['gbasis_simult_primes'](self, *args) - - def gcd(self, *args): - r'''From Giac's documentation: - Help for gcd: - gcd((Intg(a) or Poly),(Intg(b) or Poly)) - Returns the greatest common divisor of 2 polynomials of several variables or of 2 integers or of 2 rationals. - See also: 1/ lcm 2/ euler 2/ modgcd 3/ ezgcd 4/ psrgcd 5/ heugcd 6/ Gcd - Ex1:gcd(45,75) - Ex2:gcd(15/7,50/9) - Ex3:gcd(x^2-2*x+1,x^3-1) - Ex4:gcd(t^2-2*t+1,t^2+t-2) - Ex5:gcd((x^2-1)*(y^2-1)*z^2,x^3*y^3*z+(-(y^3))*z+x^3*z-z) - ''' - return GiacMethods['gcd'](self, *args) - - def gcdex(self, *args): - r'''From Giac's documentation: - Help for gcdex: - gcdex((Poly or Lst),(Poly or Lst),[Var]) - Extended greatest common divisor of 2 polynomials. - See also: 1/ gcd 2/ iegcd - Ex1:gcdex((x-1)^2,x^3-1) - Ex2:gcdex((X-1)^2,X^3-1,X) - Ex3:gcdex([1,-2,1],[1,0,0,-1]) - Ex4:gcdex([1,-2,1],[1,-1,2]) - ''' - return GiacMethods['gcdex'](self, *args) - - def genpoly(self, *args): - r'''From Giac's documentation: - Help for genpoly: - genpoly(Poly(P),Intg(b),Var) - Returns the reconstruction of a n-variables polynomial Q(-b/2<=coef<=b/2) from an (n-1)-variable polynomial P and a base b (subst(Q,var=b)=P). - See also: 1/ - Ex1:genpoly(15,4,x) - Ex2:genpoly(7*y+5,6,x) - Ex3:genpoly(7*y-5*z,10,x) - ''' - return GiacMethods['genpoly'](self, *args) - - def geometric(self, *args): - r'''From Giac's documentation: - Help for geometric: - geometric(Real(p),Intg(k)) - Returns the value at k of the geometric law with parameter p (0cos(2*x)) - Ex4:getType(1.414) - ''' - return GiacMethods['getType'](self, *args) - - def get_edge_attribute(self, *args): - r'''From Giac's documentation: - Help for get_edge_attribute: - get_edge_attribute(Graph(G),Edge(e),Seq(tag1=value1,tag2=value2,..)) - Returns the attributes tag1, tag2, ... assigned to edge e in G as a sequence of the corresponding values. - See also: 1/ discard_edge_attribute 2/ set_edge_attribute 3/ list_edge_attributes - Ex1:get_edge_attribute(cycle_graph(3),[1,2],"cost") - ''' - return GiacMethods['get_edge_attribute'](self, *args) - - def get_edge_weight(self, *args): - r'''From Giac's documentation: - Help for get_edge_weight: - get_edge_weight(Graph(G),Edge(e)) - Returns the weight of the edge e in the weighted graph G. - See also: 1/ is_weighted 2/ make_weighted 3/ set_edge_weight 4/ weight_matrix - Ex1:get_edge_weight(graph(%{[[1,2],5],[[2,3],6]%}),[1,2]) - ''' - return GiacMethods['get_edge_weight'](self, *args) - - def get_graph_attribute(self, *args): - r'''From Giac's documentation: - Help for get_graph_attribute: - get_graph_attribute(Graph(G),Seq(tag1=value1,tag2=value2,..)) - Return the graph attributes tag1, tag2, ..., as a sequence of the corresponding values. - See also: 1/ discard_graph_attribute 2/ set_graph_attribute 3/ list_graph_attributes - Ex1:get_graph_attribute(cycle_graph(3),"name") - ''' - return GiacMethods['get_graph_attribute'](self, *args) - - def get_vertex_attribute(self, *args): - r'''From Giac's documentation: - Help for get_vertex_attribute: - get_vertex_attribute(Graph(G),Vrtx(v),Seq(tag1=value1,tag2=value2,..)) - Returns the attributes tag1, tag2, ... assigned to vertex v in G as a sequence of the corresponding values. - See also: 1/ discard_vertex_attribute 2/ set_vertex_attribute 3/ list_vertex_attributes - Ex1:get_vertex_attribute(cycle_graph(3),1,"supply") - ''' - return GiacMethods['get_vertex_attribute'](self, *args) - - def girth(self, *args): - r'''From Giac's documentation: - Help for girth: - girth(Graph(G)) - Returns the length of the shortest cycle in the undirected unweighted graph G. - See also: 1/ odd_girth - Ex1:girth(graph("petersen")) - Ex2:girth(hypercube_graph(3)) - ''' - return GiacMethods['girth'](self, *args) - - def gl_showaxes(self, *args): - r'''From Giac's documentation: - Help for gl_showaxes: - gl_showaxes(Opt=Boolean) - Option that shows or hides axes. - See also: 1/ switch_axes 2/ axes - Ex1: gl_showaxes=true;plot(sin(x)) - Ex2: gl_showaxes=false;plot(sin(x)) - ''' - return GiacMethods['gl_showaxes'](self, *args) - - def grad(self, *args): - r'''From Giac's documentation: - Help for grad: - grad(Expr(Xpr),LstVar) - Returns the gradient of the expression Xpr. - See also: 1/ hessian - Ex1:grad(2*x^2*y-x*z^3,[x,y,z]) - ''' - return GiacMethods['grad'](self, *args) - - def gramschmidt(self, *args): - r'''From Giac's documentation: - Help for gramschmidt: - gramschmidt(Basis(B),ScalarProd(Sp)) - Returns an orthonormal basis of E with basis B for the scalar product Sp. - See also: 1/ - Ex1:gramschmidt(-2) - Ex2:gramschmidt([1,1+x],(p,q)->integrate(p*q,x,-1,1)) - ''' - return GiacMethods['gramschmidt'](self, *args) - - def graph(self, *args): - r'''From Giac's documentation: - Help for graph: - graph([Lst(V)],[Set(E)],[Mtrx(A)],[options]) - Create an (un)directed (un)weighted graph from vertices V, edges E, and/or adjacency or weight matrix A. All parameters are optional. - See also: 1/ digraph 2/ trail - Ex1:graph(5) - Ex2:graph([a,b,c]) - Ex3:graph([1,2,3],%{[1,2],[2,3],[3,1]%}) - Ex4:graph(trail(1,2,3,4,1),directed=true) - Ex5:graph([a,b,c],[[0,2,0],[2,0,3],[0,3,0]]) - Ex6:graph("petersen") - Ex7:graph([[0,1,1,0],[1,0,0,1],[1,0,0,0],[0,1,0,0]]) - ''' - return GiacMethods['graph'](self, *args) - - def graph_automorphisms(self, *args): - r'''From Giac's documentation: - Help for graph_automorphisms: - graph_automorphisms(Graph(G)) - Returns the sequence of generators of Aut(G), the automorphism group of G. Each element is a permutation in the form of list of disjoint cycles. - See also: 1/ cycles2permu 2/ isomorphic_copy 3/ permute_vertices - Ex1:graph_automorphisms(graph("petersen")) - ''' - return GiacMethods['graph_automorphisms'](self, *args) - - def graph_charpoly(self, *args): - r'''From Giac's documentation: - Help for graph_charpoly: - graph_charpoly(Graph(G),[Var(x)]) - Returns the value p(x) of the characteristic polynomial p of G. If x is omitted, a list of coefficients of p is returned. - See also: 1/ graph_spectrum 2/ charpoly - Ex1:graph_charpoly(graph(%{[1,2],[2,3]%})) - Ex2:graph_charpoly(graph("shrikhande")) - ''' - return GiacMethods['graph_charpoly'](self, *args) - - def graph_complement(self, *args): - r'''From Giac's documentation: - Help for graph_complement: - graph_complement(Graph(G)) - Return the graph with the same vertex set as G, but whose edge (arc) set consists of the edges (arcs) not present in G. - See also: 1/ edges - Ex1:graph_complement(cycle_graph(5)) - ''' - return GiacMethods['graph_complement'](self, *args) - - def graph_diameter(self, *args): - r'''From Giac's documentation: - Help for graph_diameter: - graph_diameter(Graph(G)) - Returns the maximum distance between a pair of vertices in G or +infinity if G is disconnected. - See also: 1/ allpairs_distance 2/ dijkstra 3/ shortest_path 4/ vertex_distance - Ex1:graph_diameter(graph("petersen")) - ''' - return GiacMethods['graph_diameter'](self, *args) - - def graph_equal(self, *args): - r'''From Giac's documentation: - Help for graph_equal: - graph_equal(Graph(G1),Graph(G2)) - Returns true iff the input graphs G1 and G2 are equal, that is when the sets of vertices and edges of G1 and G2, as well as the orderings of vertices in both graphs, mutually coincide. If the graphs are weighted (they must both be (un)weighted and (un)directed), weights given to the same edge in two graphs must be equal. - See also: 1/ edges 2/ graph_vertices - Ex1:graph_equal(graph([1,2,3],%{[1,2],[2,3],[3,1]%}),graph(trail(1,2,3,1))) - ''' - return GiacMethods['graph_equal'](self, *args) - - def graph_join(self, *args): - r'''From Giac's documentation: - Help for graph_join: - graph_join(Graph(G),Graph(H)) - Returns the graph obtained by connecting every vertex from G with every vertex from H. The vertex labels in the resulting graph are strings of form "1:u" and "2:v" where u and v are vertices from G and H, respectively. - See also: 1/ disjoint_union 2/ graph_union - Ex1:graph_join(edges(graph_join(cycle_graph(3),graph(2)))) - ''' - return GiacMethods['graph_join'](self, *args) - - def graph_power(self, *args): - r'''From Giac's documentation: - Help for graph_power: - graph_power(Graph(G),Intg(k)) - Returns the k-th power of G, where two vertices are connected iff there exists a path of length at most k in the original graph. - See also: 1/ adjacency matrix 2/ graph_diameter 3/ shortest_path - Ex1:graph_power(edges(graph_power(path_graph(5),3))) - ''' - return GiacMethods['graph_power'](self, *args) - - def graph_rank(self, *args): - r'''From Giac's documentation: - Help for graph_rank: - graph_rank(Graph(G),[Lst(E)]) - Returns the graph rank of G. If optional set E of edges is given, the rank of the spanning subgraph of G with edge set E is returned. - See also: 1/ connected_components 2/ number_of_vertices - Ex1:graph_rank(graph(%{[1,2],[3,4],[4,5]%})) - Ex2:graph_rank(graph(%{[1,2],[3,4],[4,5]%}),[[1,2],[3,4]) - ''' - return GiacMethods['graph_rank'](self, *args) - - def graph_spectrum(self, *args): - r'''From Giac's documentation: - Help for graph_spectrum: - graph_spectrum(Graph(G)) - Returns the graph spectrum of G as a list of lists with two elements, each containing an eigenvalue and its multiplicity. - See also: 1/ graph_charpoly 2/ seidel_spectrum 3/ is_integer_graph - Ex1:graph_spectrum(cycle_graph(5)) - ''' - return GiacMethods['graph_spectrum'](self, *args) - - def graph_union(self, *args): - r'''From Giac's documentation: - Help for graph_union: - graph_union(Seq(G1,G2,...)) - Returns the union of the graphs G1, G2, ... The set of vertices of the resulting graph is the union of the sets of vertices of the input graphs and the set of edges of the resulting graph is the union of sets of edges of the input graphs. If the input graphs are weighted, the weight of any common edge is the sum of the weights of that edge in G1, G2, ... - See also: 1/ disjoint_union 2/ graph_join - Ex1:graph_union(edges(graph_union(cycle_graph(4),path_graph(5)))) - ''' - return GiacMethods['graph_union'](self, *args) - - def graph_vertices(self, *args): - r'''From Giac's documentation: - Help for graph_vertices: - graph_vertices(Graph(G)) - Return the list of vertices in G. - See also: 1/ add_vertex 2/ graph 3/ neighbors 4/ permute_vertices 5/ relabel_vertices - Ex1:graph_vertices(graph(%{[a,c],[b,c],[a,b]%})) - ''' - return GiacMethods['graph_vertices'](self, *args) - - def greduce(self, *args): - r'''From Giac's documentation: - Help for greduce: - greduce(Poly,LstPoly,LstVar,[order]) - Returns the remainder of the division of a polynomial by a Groebner basis. - See also: 1/ gbasis - Ex1:greduce(x*y-1,[x^2-y^2,2*x*y-y^2,y^3],[x,y]) - Ex2:greduce(x1^2*x3^2,[x3^3-1,-x2^2-x2*x3-x3^2,x1+x2+x3],[x1,x2,x3],tdeg) - Ex3:greduce(x1^2*x3^2-x2,[x3^3-1,-x2^2-x2*x3-x3^2,x1+x2+x3],[x1,x2,x3]) - ''' - return GiacMethods['greduce'](self, *args) - - def greedy_color(self, *args): - r'''From Giac's documentation: - Help for greedy_color: - greedy_color(Graph(G),[Permu(p)]) - Returns the list of vertex colors (positive integers) obtained by coloring vertices one at a time [in the order given by permutation p], assigning to it the smallest available color. - See also: 1/ is_vertex_colorable 2/ chromatic_number - Ex1:greedy_color(graph("petersen")) - ''' - return GiacMethods['greedy_color'](self, *args) - - def grid_graph(self, *args): - r'''From Giac's documentation: - Help for grid_graph: - grid_graph(Intg(m),Intg(n),[triangle]) - Returns a [triangular] grid graph on m*n vertices, where m,n>=2. - See also: 1/ torus_grid_graph - Ex1:grid_graph(5,8) - ''' - return GiacMethods['grid_graph'](self, *args) - - def groupermu(self, *args): - r'''From Giac's documentation: - Help for groupermu: - groupermu(Permut(a),Permut(b)) - Returns the group of permutations generated by a and b. - See also: 1/ - Ex1:groupermu([1,2,0],[3,1,2,0]) - ''' - return GiacMethods['groupermu'](self, *args) - - def hadamard(self, *args): - r'''From Giac's documentation: - Help for hadamard: - hadamard(Mtrx,Mtrx) - Hadamard bound of a matrix or element by element multiplication of 2 matrices. - See also: 1/ .* 2/ * - Ex1:hadamard([[1,2],[3,4]]) - Ex2:hadamard([[1,2],[3,4]],[[3,4],[5,6]]) - ''' - return GiacMethods['hadamard'](self, *args) - - def half_cone(self, *args): - r'''From Giac's documentation: - Help for half_cone: - half_cone(Pnt(A),Vect(v),Real(t),[Real(h)]) - Draws a half-cone with vertex A, direction v and with half_angle=t [and with altitude h]. - See also: 1/ cone 2/ cylinder - Ex1:half_cone([0,0,0],[0,0,1],pi/6) - Ex2:half_cone([0,0,0],[0,1,1],pi/6,-4) - ''' - return GiacMethods['half_cone'](self, *args) - - def half_line(self, *args): - r'''From Giac's documentation: - Help for half_line: - half_line((Pnt or Cplx),(Pnt or Cplx)) - half_line(A,B) draws the half-line AB with A as origin. - See also: 1/ line - Ex1:half_line(i,1+i) - Ex2:half_line(point(i),point(1+i)) - ''' - return GiacMethods['half_line'](self, *args) - - def halftan(self, *args): - r'''From Giac's documentation: - Help for halftan: - halftan(Expr) - Transforms sin(x),cos(x) and tan(x) to functions of tan(x/2). - Ex1:halftan(sin(x)) - Ex2:halftan(cos(x)) - Ex3:halftan(tan(x)) - ''' - return GiacMethods['halftan'](self, *args) - - def halftan_hyp2exp(self, *args): - r'''From Giac's documentation: - Help for halftan_hyp2exp: - halftan_hyp2exp(ExprTrig) - Transforms the trigonometric functions in tan(x/2) and hyperbolic functions to exp. - See also: 1/ hyp2exp 2/ halftan - Ex1:halftan_hyp2exp(sin(x)+sinh(x)) - ''' - return GiacMethods['halftan_hyp2exp'](self, *args) - - def halt(self, *args): - r'''From Giac's documentation: - Help for halt: - halt(NULL) - Puts a program in step-by-step debug mode. - See also: 1/ - Ex1:halt() - ''' - return GiacMethods['halt'](self, *args) - - def hamdist(self, *args): - r'''From Giac's documentation: - Help for hamdist: - hamdist(Intg,Intg) - Bitwise Hamming distance. - Ex1:hamdist(0x12,0x38) - ''' - return GiacMethods['hamdist'](self, *args) - - def hamming_window(self, *args): - r'''From Giac's documentation: - Help for hamming_window: - hamming_window(Lst,[Interval(n1..n2)]) - Applies the Hamming windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ bartlett_hann_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(hamming_window(randvector(1000,0..1))) - ''' - return GiacMethods['hamming_window'](self, *args) - - def hann_poisson_window(self, *args): - r'''From Giac's documentation: - Help for hann_poisson_window: - hann_poisson_window(Lst,[Interval(n1..n2)]) - Applies the Hann-Poisson windowing function with parameter a (by default a=1) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ bartlett_hann_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(hann_poisson_window(randvector(1000,0..1),2)) - ''' - return GiacMethods['hann_poisson_window'](self, *args) - - def hann_window(self, *args): - r'''From Giac's documentation: - Help for hann_window: - hann_window(Lst,[Interval(n1..n2)]) - Applies the Hann windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ bartlett_hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(hann_window(randvector(1000,0..1))) - ''' - return GiacMethods['hann_window'](self, *args) - - def harmonic_conjugate(self, *args): - r'''From Giac's documentation: - Help for harmonic_conjugate: - harmonic_conjugate(Line or Pnt(A),Line or Pnt(B),Line or Pnt(C)) - Returns the harmonic conjugate C with respect to A and B of 3 points or of 3 parallel or concurrent lines or the line of conjugates of a point with respect to 2 lines. - See also: 1/ is_harmonic 2/ harmonic_division - Ex1:harmonic_conjugate(0,2,3/2) - Ex2:harmonic_conjugate(0,1+i,2+2*i) - Ex3:harmonic_conjugate(line(0,1+i),line(0,3+i),line(0,i)) - Ex4:harmonic_conjugate(line(0,1+i),line(0,3+i),point(3/2+i)) - ''' - return GiacMethods['harmonic_conjugate'](self, *args) - - def harmonic_division(self, *args): - r'''From Giac's documentation: - Help for harmonic_division: - harmonic_division(Pnt or Line,Pnt or Line,Pnt or Line,Var) - Returns 4 points (resp lines) and affects the last argument, such that the 4 points (resp lines) are in a harmonic division and assigns the fourth point to the variable name. - See also: 1/ harmonic_conjugate 2/ is_harmonic - Ex1:harmonic_division(0,2,3/2,D) - Ex2:harmonic_division(0,1+i,2+2*i,D) - Ex3:harmonic_division(line(i,0),line(i,1+i),line(i,3+2*i),D) - Ex4:harmonic_division(line(0,1+i),line(0,3+i),line(0,i),D) - ''' - return GiacMethods['harmonic_division'](self, *args) - - def has(self, *args): - r'''From Giac's documentation: - Help for has: - has(Expr,Var) - Checks if a variable is in an expression. - See also: 1/ lname 2/ lvar - Ex1:has(x+y,x) - Ex2:has(x+y,n) - ''' - return GiacMethods['has'](self, *args) - - def has_arc(self, *args): - r'''From Giac's documentation: - Help for has_arc: - has_arc(Graph(G),Edge(e)) - Returns true iff the arc e=[i,j] is contained in digraph G or, if e={i,j} is a set, iff G has both edges [i,j] and [j,i]. - See also: 1/ edges 2/ has_edge - Ex1:has_arc(digraph(trail(1,2,3,4,1)),[4,2]) - Ex2:has_arc(digraph(trail(1,2,3,4,1)),%{4,2%}) - ''' - return GiacMethods['has_arc'](self, *args) - - def has_edge(self, *args): - r'''From Giac's documentation: - Help for has_edge: - has_edge(Graph(G),Edge(e)) - Returns true iff the edge e=[i,j] is contained in undirected graph G. - See also: 1/ edges 2/ has_arc - Ex1:has_edge(graph(trail(1,2,3,4,1)),[2,4]) - ''' - return GiacMethods['has_edge'](self, *args) - - def hasard(self, *args): - r'''From Giac's documentation: - Help for hasard: - hasard(Intg(n) or Interval(p..n) or NULL,[Intg(b1) or Lst(L)],[Intg(b2)]) - (hasard n)=a random integer (resp (hasard p,n)=a real or hasard(p..n)=a real function) with uniform distribution in 0..n-1 (resp in [p;n])(hasard= (hasard 0,1)=a random real in [0,1[) or hasard(n,b1,b2)=n integers between b1 and b2 or hasard(n,L)=n elements of L. If hasard has only one argument, () are not necessary (compatibility with turtle language). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ srand - Ex1: hasard 4 - Ex2: hasard(4) - Ex3:hasard(0,2) - Ex4: hasard 0..2 - Ex5: f:=hasard 0..2 - Ex6:hasard(3,1,10) - Ex7:hasard(3,["r","r","r","b","n"]) - ''' - return GiacMethods['hasard'](self, *args) - - def head(self, *args): - r'''From Giac's documentation: - Help for head: - head(Vect or Seq or Str) - Shows the first element of a vector or a sequence or a string. - See also: 1/ back 2/ tail 3/ mid 4/ left 5/ right - Ex1:head(1,2,3) - Ex2:head([1,2,3]) - Ex3:head("bonjour") - ''' - return GiacMethods['head'](self, *args) - - def heading(self, *args): - r'''From Giac's documentation: - Help for heading: - heading(NULL or Real) - Returns the turtle cap in degrees or turns the turtle in the direction given by the argument. - See also: 1/ position 2/ initialise - Ex1: cap - Ex2:heading() - Ex3:heading(cap 90) - ''' - return GiacMethods['heading'](self, *args) - - def heapify(self, *args): - r'''From Giac's documentation: - Help for heapify: - heapify(List) - Partial ordering of a list as a heap. - See also: 1/ heappush 2/ heappop - ''' - return GiacMethods['heapify'](self, *args) - - def heappop(self, *args): - r'''From Giac's documentation: - Help for heappop: - heappop(List) - Removes and returns the root node of a heap. - See also: 1/ heapify 2/ heappush - ''' - return GiacMethods['heappop'](self, *args) - - def heappush(self, *args): - r'''From Giac's documentation: - Help for heappush: - heappush(List,Object) - Adds an object in a heap. - See also: 1/ heapify 2/ heappop - ''' - return GiacMethods['heappush'](self, *args) - - def hermite(self, *args): - r'''From Giac's documentation: - Help for hermite: - hermite(Intg(n)||Matr(A)) - Returns the Hermite polynomial of degree n or the Hermite normal form for a matrix with polynomial coefficients (I,U such that I*A=U). - See also: 1/ legendre 2/ laguerre 3/ smith 4/ ihermite 5/ ismith - Ex1:hermite(3) - Ex2: n:=5; a:=ranm(n,n) % 17; l,u:=hermite(x-a);normal(l*(x-a)-u); - ''' - return GiacMethods['hermite'](self, *args) - - def hessenberg(self, *args): - r'''From Giac's documentation: - Help for hessenberg: - hessenberg(Mtrx(A),[Intg(n)]) - Matrix reduction to Hessenberg form. Returns [P,B] such that B=inv(P)*A*P, by default n=0 the result is exact else the result is numeric. For n=-1 B is triangular, n=-2 P is orthogonal and if n is prime the result is mod n. - See also: 1/ SCHUR - Ex1:hessenberg([[1,2,3],[4,5,6],[7,8,1]]) - Ex2:hessenberg([[1,2,3,4],[4,5,6,7],[7,8,9,0],[0,1,2,3]]) - Ex3:hessenberg([[1,2,3],[4,5,6],[7,8,1]],-1) - Ex4:hessenberg([[1,2,3],[4,5,6],[7,8,1]],-2) - Ex5:hessenberg([[1,2,3],[4,5,6],[7,8,1]],3) - ''' - return GiacMethods['hessenberg'](self, *args) - - def hessian(self, *args): - r'''From Giac's documentation: - Help for hessian: - hessian(Expr(Xpr),LstVar) - Returns the hessian of the expression Xpr. - See also: 1/ grad - Ex1:hessian(2*x^2*y-x*z,[x,y,z]) - ''' - return GiacMethods['hessian'](self, *args) - - def heugcd(self, *args): - r'''From Giac's documentation: - Help for heugcd: - heugcd(Poly,Poly) - GCD of 2 polynomials, with the algorithm called heuristic pgcd. - See also: 1/ gcd 2/ modgcd 3/ ezgcd 4/ psrgcd - Ex1:heugcd(x^4-1,(x-1)^2) - ''' - return GiacMethods['heugcd'](self, *args) - - def hexagon(self, *args): - r'''From Giac's documentation: - Help for hexagon: - hexagon(Pnt(A)||Cplx,Pnt(B)||Cplx,[Pnt(P)],[Var(C)],[Var(D)],[Var(E)],[Var(F)]) - Returns and draws the hexagon of side AB (ABCDEF is direct) (in the plane ABP). - See also: 1/ isopolygon 2/ polygon - Ex1:hexagon(i,1+i) - Ex2:hexagon(i,1+i,C,D,E,F) - Ex3:hexagon(point(0,0,0),point(3,3,3),point(0,0,3)) - Ex4:hexagon(point(0,0,0),point(3,3,3),point(0,0,3),C,D,E,F) - ''' - return GiacMethods['hexagon'](self, *args) - - def highlight_edges(self, *args): - r'''From Giac's documentation: - Help for highlight_edges: - highlight_edges(Graph(G),Edge(e)||Lst(E),[Color(c)||Lst(C)]) - Changes color of edge e resp. colors of edges in E of the input graph V to c resp C (by default red) and returns the modified copy of G. - See also: 1/ highlight_vertex 2/ highlight_subgraph 3/ highlight_trail - Ex1: draw_graph(highlight_edges(cycle_graph(3),[1,2])) - ''' - return GiacMethods['highlight_edges'](self, *args) - - def highlight_subgraph(self, *args): - r'''From Giac's documentation: - Help for highlight_subgraph: - highlight_subgraph(Graph(G),Graph(S)||Lst(S1,S2,..),Seq(c1,c2)) - Changes colors of edges and vertices from the sugbraph S or list of subgraphs S1, S2, ... of G to c1 and c2, respectively (red and green by default), and returns the modified copy of G. - See also: 1/ highlight_edges 2/ highlight_vertex 3/ highlight_trail - Ex1: draw_graph(highlight_subgraph(cycle_graph(5),path_graph(3))) - ''' - return GiacMethods['highlight_subgraph'](self, *args) - - def highlight_trail(self, *args): - r'''From Giac's documentation: - Help for highlight_trail: - highlight_trail(Graph(G),Trail(t)||Lst(T),[Color(c)||Lst(C)]) - Changes colors of edges in G which lie along the trail t resp. trails in T to c resp. C (by default red) and returns the modified copy of G. - See also: 1/ highlight_edges 2/ highlight_subgraph 3/ highlight_vertex - Ex1: draw_graph(highlight_trail(cycle_graph(5),trail(1,2,3),green) - ''' - return GiacMethods['highlight_trail'](self, *args) - - def highlight_vertex(self, *args): - r'''From Giac's documentation: - Help for highlight_vertex: - highlight_vertex(Graph(G),Vrtx(v)||Lst(V),[Color(c)||Lst(C)]) - Changes the color of vertex v resp. colors of vertices from V in G to c resp. C (green by default) and returns the modified copy of G. - See also: 1/ highlight_edges 2/ highlight_subgraph 3/ highlight_trail - Ex1: draw_graph(highlight_vertex(cycle_graph(3),1)) - ''' - return GiacMethods['highlight_vertex'](self, *args) - - def highpass(self, *args): - r'''From Giac's documentation: - Help for highpass: - highpass(Lst(s),Real(c),[Intg(samplerate)]) - Returns the result of applying a simple first-order highpass RC filter with cutoff frequency c (the default samplerate is 44100) to the given signal s. - See also: 1/ lowpass 2/ moving_average - Ex1: f:=unapply(periodic(sign(x),x,-1/880,1/880),x):;s:=createwav(apply(f,soundsec(1))):;playsnd(highpass(s,5000)) - ''' - return GiacMethods['highpass'](self, *args) - - def hilbert(self, *args): - r'''From Giac's documentation: - Help for hilbert: - hilbert(Intg(n)) - Returns the order n Hilbert matrix : Hjk=1/(j+k+1) j,k=1..n. - See also: 1/ - Ex1:hilbert(4) - ''' - return GiacMethods['hilbert'](self, *args) - - def histogram(self, *args): - r'''From Giac's documentation: - Help for histogram: - histogram(Lst(data),[Lst(eff) || Intg(nc) || Real(classmin)],[Real(classsize)]) - Draws the histogram of data, optional arguments are eff (number of data for each data element) or nc the number of classes or the classes minimum and size. - See also: 1/ cumulated_frequencies 2/ classes 3/ bar_plot 4/ frequencies - Ex1:histogram([1,2,1,1,2,1,2,4,3,3]) - Ex2:histogram([1,2,1,1,2,1,2,4,3,3],0.5,1) - Ex3:histogram(seq(rand(1000),k,0,100),0,100) - Ex4:histogram(binomial,10,.5) - Ex5:histogram([[0,1],[1,4],[2,3],[3,2],[4,1]]) - Ex6:histogram([[1.5..1.65,50],[1.65..1.7,20],[1.7..1.8,30]]) - Ex7:histogram(seq(rand(1000),k,0,100),0,100) - Ex8:histogram(seq(rand(1000),k,0,100),10) - ''' - return GiacMethods['histogram'](self, *args) - - def hold(self, *args): - r'''From Giac's documentation: - Help for hold: - hold(Expr) - Returns its argument unevaluated (and also a:=quote(a) purges a). - See also: 1/ - Ex1:hold(1+2) - Ex2:hold(1/x+1/(x-1)) - Ex3:hold((x+1)*(x-1)) - ''' - return GiacMethods['hold'](self, *args) - - def homogeneize(self, *args): - r'''From Giac's documentation: - Help for homogeneize: - homogeneize(Expr(P),[Var(t)]) - Make P homogeneous by adding a variable (by default t) - Ex1:homogeneize(x^2-1) - Ex2:homogeneize(x^2-y,z) - ''' - return GiacMethods['homogeneize'](self, *args) - - def homothety(self, *args): - r'''From Giac's documentation: - Help for homothety: - homothety(Pnt(C),Real(k),Pnt(A)) - homothety(C,k,A)=point A1 such as vect(C,A1)=k*vect(C,A) i.e in 2d it is the similarity with center C, coeff abs(k) and angle arg(k). - See also: 1/ similarity 2/ inversion - Ex1:homothety(1+i,1/3,i) - Ex2:homothety(point(1,1,1),1/3,point(0,1,0)) - Ex3: h:=homothety(1+i,1/3);h(i) - Ex4: h:=homothety(point(1,1,1),1/3);h(point(0,1,0)) - ''' - return GiacMethods['homothety'](self, *args) - - def horner(self, *args): - r'''From Giac's documentation: - Help for horner: - horner(Poly(P),Real(a)) - Returns the value of P(a) calculated with Horner's method. With horner(list_alpha_i,list_x_i,x), evals an interpolation polynomial from the divided differences of x. - See also: 1/ convert 2/ base 3/ revlist - Ex1:horner(x^2+1,2) - Ex2:horner([1,0,1],2) - Ex3:horner(x^2+y*x+y^3-1,2,y) - Ex4: X:=[0.0,1.0,2.0]; A:=lagrange(X,exp,lagrange); horner(A,X,1.5); - ''' - return GiacMethods['horner'](self, *args) - - def hybrid_solver(self, *args): - r'''From Giac's documentation: - Help for hybrid_solver: - hybrid_solver(Opt) - Argument for fsolve giving the method for solving a system of numerical equations. - See also: 1/ fsolve - Ex1: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],dnewton_solver) - Ex2: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrid_solver) - Ex3: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrids_solver) - Ex4: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridj_solver) - Ex5: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridsj_solver) - Ex6: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],newtonj_solver) - ''' - return GiacMethods['hybrid_solver'](self, *args) - - def hybridj_solver(self, *args): - r'''From Giac's documentation: - Help for hybridj_solver: - hybridj_solver(Opt) - Argument for fsolve giving the method for solving a system of numerical equations. - See also: 1/ fsolve - Ex1: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],dnewton_solver) - Ex2: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrid_solver) - Ex3: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrids_solver) - Ex4: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridj_solver) - Ex5: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridsj_solver) - Ex6: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],newtonj_solver) - ''' - return GiacMethods['hybridj_solver'](self, *args) - - def hybrids_solver(self, *args): - r'''From Giac's documentation: - Help for hybrids_solver: - hybrids_solver(Opt) - Argument for fsolve giving the method for solving a system of numerical equations. - See also: 1/ fsolve - Ex1: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],dnewton_solver) - Ex2: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrid_solver) - Ex3: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrids_solver) - Ex4: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridj_solver) - Ex5: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridsj_solver) - Ex6: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],newtonj_solver) - ''' - return GiacMethods['hybrids_solver'](self, *args) - - def hybridsj_solver(self, *args): - r'''From Giac's documentation: - Help for hybridsj_solver: - hybridsj_solver(Opt) - Argument for fsolve giving the method for solving a system of numerical equations. - See also: 1/ fsolve - Ex1: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],dnewton_solver) - Ex2: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrid_solver) - Ex3: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrids_solver) - Ex4: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridj_solver) - Ex5: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridsj_solver) - Ex6: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],newtonj_solver) - ''' - return GiacMethods['hybridsj_solver'](self, *args) - - def hyp2exp(self, *args): - r'''From Giac's documentation: - Help for hyp2exp: - hyp2exp(ExprHyperb) - Transforms the hyperbolic functions to the exponential function. - See also: 1/ halftan_hyp2exp - Ex1:hyp2exp(cosh(x)) - ''' - return GiacMethods['hyp2exp'](self, *args) - - def hyperbola(self, *args): - r'''From Giac's documentation: - Help for hyperbola: - hyperbola(Focus(F1),Focus(F2),(Pnt(M) or Real(a))) - hyperbola(F1,F2,M)=hyperbola with foci F1,F2 through M or (|MF1-MF2|=2*a geo2d) and hyperbola(p(x,y)) draws the conic if deg(p)=2. - See also: 1/ ellipse 2/ parabola - Ex1:hyperbola(-1,1,point(1+i)) - Ex2:hyperbola(-1,1,sqrt(5)-1) - Ex3:hyperbola(point(-1,0,0),point(1,0,0),point(1,1,1)) - Ex4:hyperbola(x^2-y^2+y+2) - ''' - return GiacMethods['hyperbola'](self, *args) - - def hypercube_graph(self, *args): - r'''From Giac's documentation: - Help for hypercube_graph: - hypercube_graph(Intg(n)) - Constructs and returns the hypercube graph in dimension n (with 2^n vertices). - See also: 1/ graph - Ex1:hypercube_graph(3) - ''' - return GiacMethods['hypercube_graph'](self, *args) - - def iPart(self, *args): - r'''From Giac's documentation: - Help for iPart: - iPart(Real||LstReal) - Returns the argument without its fractional part (type=DOM_FLOAT). - See also: 1/ fPart 2/ floor 3/ trunc - Ex1:iPart(4.3) - Ex2:iPart(sqrt(2)) - Ex3:iPart(4.3,sqrt(2)) - ''' - return GiacMethods['iPart'](self, *args) - - def iabcuv(self, *args): - r'''From Giac's documentation: - Help for iabcuv: - iabcuv(Intg(a),Intg(b),Intg(c)) - Returns [u,v] such that au+bv=c for 3 integers a,b,c. - See also: 1/ iegcd 2/ abcuv - Ex1:iabcuv(21,28,7) - Ex2:iabcuv(21,28,14) - Ex3:iabcuv(21,28,1) - ''' - return GiacMethods['iabcuv'](self, *args) - - def ibasis(self, *args): - r'''From Giac's documentation: - Help for ibasis: - ibasis(Lst(Vect,..,Vect),Lst(Vect,..,Vect)) - Basis of the intersection of two vector spaces. - See also: 1/ basis - Ex1:ibasis([[1,0,0],[0,1,0]],[[1,1,1],[0,0,1]]) - ''' - return GiacMethods['ibasis'](self, *args) - - def ibpdv(self, *args): - r'''From Giac's documentation: - Help for ibpdv: - ibpdv(Expr(f(x)),Expr(v(x)),[Var(x)],[Real(a)],[Real(b)]) - Integration by parts of f(x)=u(x)*v'(x) with f(x) as 1st argument and v(x) (or 0) as 2nd argument. You can specify a variable of integration and also calculate the integral (bounds a and b). - See also: 1/ ibpu 2/ int - Ex1:ibpdv(ln(x),x) - Ex2:ibpdv(ln(x),x,x,1,3) - Ex3:ibpdv(x*ln(x),x^2/2) - Ex4:ibpdv([x*ln(x),-1],0) - Ex5:ibpdv(ibpdv(ln(x),x,x,2,3),0,x,2,3) - ''' - return GiacMethods['ibpdv'](self, *args) - - def ibpu(self, *args): - r'''From Giac's documentation: - Help for ibpu: - ibpu(Expr(f(x)),Expr(u(x)),[Var(x)],[Real(a)],[Real(b)]) - Integration by parts of f(x)=u(x)*v'(x) with f(x) as 1st argument and u(x) (or 0) as 2nd argument. You can specify a variable of integration and also calculate the integral (bounds a and b). - See also: 1/ ibpdv 2/ int - Ex1:ibpu(ln(x),ln(x)) - Ex2:ibpu(ln(x),ln(x),x,1,3) - Ex3:ibpu(x*ln(x),ln(x)) - Ex4:ibpu([x*ln(x),-1],0) - Ex5:ibpu(ibpu(ln(x),ln(x),x,2,3),0,x,2,3) - ''' - return GiacMethods['ibpu'](self, *args) - - def icdf(self, *args): - r'''From Giac's documentation: - Help for icdf: - icdf(Func,FuncParams) - Inverse cumulative distribution function. - See also: 1/ cdf 2/ binomial_icdf 3/ normald_icdf - Ex1:icdf(binomial,10,0.5,0.6) - Ex2:icdf(normald,0.0,1.0,0.975) - ''' - return GiacMethods['icdf'](self, *args) - - def ichinrem(self, *args): - r'''From Giac's documentation: - Help for ichinrem: - ichinrem(LstIntg(a,p),LstIntg(b,q)) - Chinese remainders for integers. - See also: 1/ gcd 2/ fracmod 3/ chinrem 4/ chrem - Ex1:ichinrem([2,7],[3,5]) - Ex2:ichinrem([2%7,3%5]) - Ex3:ichinrem([2%7,3%5,1%9]) - Ex4:ichinrem([(x+1)%2,(x+2)%3,(3*x-1)%5]) - ''' - return GiacMethods['ichinrem'](self, *args) - - def ichrem(self, *args): - r'''From Giac's documentation: - Help for ichrem: - ichrem(LstIntg(a,p),LstIntg(b,q)) - Chinese remainders for integers. - See also: 1/ gcd 2/ fracmod 3/ chinrem 4/ chrem - Ex1:ichrem([2,7],[3,5]) - Ex2:ichrem([2%7,3%5]) - Ex3:ichrem([2%7,3%5,1%9]) - Ex4:ichrem([(x+1)%2,(x+2)%3,(3*x-1)%5]) - ''' - return GiacMethods['ichrem'](self, *args) - - def icomp(self, *args): - r'''From Giac's documentation: - Help for icomp: - icomp(Intg(n),Intg(k),[zeros=true||false]) - Returns the list of compositions of n into k parts. - See also: 1/ sum - Ex1:icomp(4,2) - Ex2:icomp(6,3,zeros=false) - ''' - return GiacMethods['icomp'](self, *args) - - def icontent(self, *args): - r'''From Giac's documentation: - Help for icontent: - icontent(Poly,[Var]) - GCD of the integer coefficients of a polynomial. - See also: 1/ - Ex1:icontent(24x^3+6x^2-12x+18) - Ex2:icontent(24t^3+6t^2-12t+18,t) - ''' - return GiacMethods['icontent'](self, *args) - - def icosahedron(self, *args): - r'''From Giac's documentation: - Help for icosahedron: - icosahedron(Pnt(A),Pnt(B),Pnt(C)) - Draws an icosahedron with center A, vertex B and such that the plane ABC contains one vertex among the 5 nearest vertices from B. - See also: 1/ octahedron 2/ dodecahedron 3/ cube 4/ tetrahedron - Ex1:icosahedron([0,0,0],[sqrt(5),0,0],[1,2,0]) - Ex2:icosahedron(evalf([0,0,0],[3,2,4],[1,1,0])) - ''' - return GiacMethods['icosahedron'](self, *args) - - def id(self, *args): - r'''From Giac's documentation: - Help for id: - id(Seq) - The name of the identity function (ℝ^n -> ℝ^n). - See also: 1/ sq 2/ sqrt - Ex1:id(1,2,3) - ''' - return GiacMethods['id'](self, *args) - - def identity(self, *args): - r'''From Giac's documentation: - Help for identity: - identity(Intg(n)) - Returns the identity matrix of specified dimension n. - See also: 1/ ranm - Ex1:identity(3) - Ex2:identity(5) - ''' - return GiacMethods['identity'](self, *args) - - def idivis(self, *args): - r'''From Giac's documentation: - Help for idivis: - idivis(Intg(a) or LstIntg) - Returns the list of divisors of an integer. - See also: 1/ divis 2/ ifactors - Ex1:idivis(36) - Ex2:idivis([36,49]) - ''' - return GiacMethods['idivis'](self, *args) - - def idn(self, *args): - r'''From Giac's documentation: - Help for idn: - idn(Intg(n)) - Returns the identity matrix of specified dimension n. - See also: 1/ ranm - Ex1:idn(3) - Ex2:idn(5) - ''' - return GiacMethods['idn'](self, *args) - - def iegcd(self, *args): - r'''From Giac's documentation: - Help for iegcd: - iegcd(Intg,Intg) - Extended greatest common divisor of 2 integers. - See also: 1/ gcd 2/ iabcuv 3/ egcd - Ex1:iegcd(45,75) - Ex2:iegcd(21,28) - Ex3:iegcd(30,49) - ''' - return GiacMethods['iegcd'](self, *args) - - def ifactor(self, *args): - r'''From Giac's documentation: - Help for ifactor: - ifactor(Intg(a)) - Factorization of an integer into prime factors. - See also: 1/ factor 2/ ecm_factor - Ex1:ifactor(50) - Ex2:ifactor(123456789) - ''' - return GiacMethods['ifactor'](self, *args) - - def ifactors(self, *args): - r'''From Giac's documentation: - Help for ifactors: - ifactors(Intg(a) or LstIntg) - Returns the list of prime factors of an integer (each factor is followed by its multiplicity). - See also: 1/ ifactor 2/ factors - Ex1:ifactors(36) - Ex2:ifactors([36,52]) - ''' - return GiacMethods['ifactors'](self, *args) - - def ifourier(self, *args): - r'''From Giac's documentation: - Help for ifourier: - ifourier(Expr(F(s)),[Var(s),[Var(x)]]) - Returns the inverse Fourier transform f(x) of F(s). - See also: 1/ fourier 2/ fourier_cn 3/ ifft - Ex1:ifourier(2*pi*(Dirac(s)-sign(s)*sin(s)),s,x) - Ex2:ifourier(-2/(s^2-1),s,x) - Ex3:ifourier(pi/(exp(pi*s/4)+exp(-pi*s/4)),s,x) - Ex4:ifourier(6*pi*exp(-exp(-3*s)-3*s),s,x) - Ex5:ifourier(pi*ugamma(0,4*abs(s)),s,x) - Ex6:ifourier(fourier(exp(-abs(x)),x,s)^2,s,x) - Ex7:ifourier(sinc(s),s,x) - ''' - return GiacMethods['ifourier'](self, *args) - - def igamma(self, *args): - r'''From Giac's documentation: - Help for igamma: - igamma(Real(a),Real(x),[1]) - Calculates of gamma at a point (a,x). If a and x>0, igamma(a,x)=int(e^{-t}*t^{a-1},t=0..x), (igamma(a,x,1)=igamma(a,x)/Gamma(a)). - See also: 1/ Psi 2/ Beta 3/ Gamma 3/ ugamma - Ex1:igamma(5.0,2.0) - Ex2:igamma(-5.1,2.1) - Ex3:igamma(5.0,2.0,1) - ''' - return GiacMethods['igamma'](self, *args) - - def igcd(self, *args): - r'''From Giac's documentation: - Help for igcd: - igcd((Intg(a) or Poly),(Intg(b) or Poly)) - Returns the greatest common divisor of 2 polynomials of several variables or of 2 integers or of 2 rationals. - See also: 1/ lcm 2/ euler 2/ modgcd 3/ ezgcd 4/ psrgcd 5/ heugcd 6/ Gcd - Ex1:igcd(45,75) - Ex2:igcd(15/7,50/9) - Ex3:igcd(x^2-2*x+1,x^3-1) - Ex4:igcd(t^2-2*t+1,t^2+t-2) - Ex5:igcd((x^2-1)*(y^2-1)*z^2,x^3*y^3*z+(-(y^3))*z+x^3*z-z) - ''' - return GiacMethods['igcd'](self, *args) - - def igcdex(self, *args): - r'''From Giac's documentation: - Help for igcdex: - igcdex(Intg,Intg) - Extended greatest common divisor of 2 integers. - See also: 1/ gcd 2/ iabcuv 3/ egcd - Ex1:igcdex(45,75) - Ex2:igcdex(21,28) - Ex3:igcdex(30,49) - ''' - return GiacMethods['igcdex'](self, *args) - - def ihermite(self, *args): - r'''From Giac's documentation: - Help for ihermite: - ihermite(Mtrx(A)) - Hermite normal form of a matrix with coefficients in ℤ : returns L,U such that L is invertible in ℤ, U is upper triangular and U=L*A. - See also: 1/ ismith - Ex1:ihermite([[9,-36,30], [-36,192,-180], [30,-180,180]]) - Ex2:ihermite([[1,2,3],[4,5,6],[7,8,9]]) - ''' - return GiacMethods['ihermite'](self, *args) - - def ilaplace(self, *args): - r'''From Giac's documentation: - Help for ilaplace: - ilaplace(Expr,[Var],[IlapVar]) - Inverse Laplace transform of a rational fraction. - See also: 1/ laplace 2/ ztrans 3/ invztrans 4/ Heaviside - Ex1:ilaplace(1/(x^2+1)^2) - Ex2:ilaplace(s/(s^4-1),s,x) - Ex3:ilaplace(exp(-s)/s,s,x) - ''' - return GiacMethods['ilaplace'](self, *args) - - def im(self, *args): - r'''From Giac's documentation: - Help for im: - im(Cplx) - Returns the imaginary part of a complex number. - See also: 1/ re 2/ conj - Ex1:im(1+2*i) - Ex2:im((1+2*i)^2) - Ex3:im([1+2*i,(1+2*i)^2]) - ''' - return GiacMethods['im'](self, *args) - - def imag(self, *args): - r'''From Giac's documentation: - Help for imag: - imag(Cplx) - Returns the imaginary part of a complex number. - See also: 1/ re 2/ conj - Ex1:imag(1+2*i) - Ex2:imag((1+2*i)^2) - Ex3:imag([1+2*i,(1+2*i)^2]) - ''' - return GiacMethods['imag'](self, *args) - - def image(self, *args): - r'''From Giac's documentation: - Help for image: - image(Mtrx(M)) - Image of a linear map with matrix M. - See also: 1/ ker 2/ rref - Ex1:image([[1,2],[3,6]]) - Ex2:image([[1,2,3],[1,3,6],[2,5,9]]) - ''' - return GiacMethods['image'](self, *args) - - def implicitdiff(self, *args): - r'''From Giac's documentation: - Help for implicitdiff: - implicitdiff(constr,[depvars],y,diffvars) - Implicit differentiation. - See also: 1/ diff - Ex1:implicitdiff(x^2*y+y^2=1,y,x) - Ex2:implicitdiff(R=P*V/T,P,T) - Ex3:implicitdiff([x^2+y=z,x+y*z=1],[y(x),z(x)],y,x) - Ex4:implicitdiff([x^2+y=z,x+y*z=1],[y(x),z(x)],[y,z],x) - Ex5:implicitdiff(y=x^2/z,y,x) - Ex6:implicitdiff(y=x^2/z,y,z) - Ex7:implicitdiff(y^3+x^2=1,y,x) - Ex8:implicitdiff(y^3+x^2=1,y,x,x) - Ex9:implicitdiff(a*x^3*y-2y/z=z^2,y(x,z),x) - Ex10:implicitdiff(a*x^3*y-2y/z=z^2,y(x,z),x,z) - Ex11:implicitdiff([-2x*z+y^2=1,x^2-exp(x*z)=y],[y(x),z(x)],y,x) - Ex12:implicitdiff([-2x*z+y^2=1,x^2-exp(x*z)=y],[y(x),z(x)],[y,z],x) - Ex13:implicitdiff([a*sin(u*v)+b*cos(w*x)=c,u+v+w+x=z,u*v+w*x=z],[u(x,z),v(x,z),w(x,z)],u,z) - Ex14:implicitdiff(x*y,-2x^3+15x^2*y+11y^3-24y=0,y(x),x$2) - Ex15:implicitdiff((x-u)^2+(y-v)^2,[x^2/4+y^2/9=1,(u-3)^2+(v+5)^2=1],[v(u,x),y(u,x)],u,x) - Ex16:implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order=1) - Ex17:implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order=2,[1,-1,0]) - Ex18: pd:=implicitdiff(x*y*z,-2x^3+15x^2*y+11y^3-24y=0,[x,z,y],order=4,[0,z,0]);pd[4,0,0] - ''' - return GiacMethods['implicitdiff'](self, *args) - - def implicitplot(self, *args): - r'''From Giac's documentation: - Help for implicitplot: - implicitplot(Expr,Var1,Var2) - plotimplicit(f(x,y),x,y) or plotimplicit(f(x,y),[x,y]) draws graph of f(x,y)=0. - See also: 1/ plotcontour 2/ unfactored 3/ plotinequation - Ex1:implicitplot(x^2+y^2-1,x,y) - Ex2:implicitplot(x^4+y^4=x^2-y^2) - Ex3:implicitplot(x^2+y^2-1,x,y,unfactored) - Ex4:implicitplot(x^2+4*y^3-1) - Ex5:implicitplot(x^2+4*y^3-k)$(k=-5..5) - Ex6:implicitplot(y^3=x^3-x^2,x,y,xstep=0.1,ystep=0.1) - Ex7:implicitplot(y^3=x^3-x^2,[x,y],xstep=0.1,ystep=0.1) - Ex8:implicitplot((x+5)^2+(y+4)^2-1,x=-6..-4,y=-5..-3) - Ex9:implicitplot((x+5)^2+(y+4)^2-1,[x=-6..-4,y=-5..-3]) - ''' - return GiacMethods['implicitplot'](self, *args) - - def import_graph(self, *args): - r'''From Giac's documentation: - Help for import_graph: - import_graph(Str("path/to/graphname[.dot]")) - Returns the graph constructed from instructions in the file 'path/to/graphname.dot' (in dot format), or "undef" on failure. - Ex1:import_graph("K5.dot") - ''' - return GiacMethods['import_graph'](self, *args) - - def inString(self, *args): - r'''From Giac's documentation: - Help for inString: - inString(Str(l),Elem(e)) - Tests if e is in the string l (returns -1 or k if l[k]=e). - See also: 1/ contains - Ex1:inString("abcd","b") - Ex2:inString("abcd","e") - ''' - return GiacMethods['inString'](self, *args) - - def in_ideal(self, *args): - r'''From Giac's documentation: - Help for in_ideal: - in_ideal(Poly,Lst,LstVar,[order]) - Checks whether a polynomial or list of polynomials belongs to an ideal given by a Grobner basis (2nd argument) with respect to a variable list. - See also: 1/ gbasis 2/ greduce - Ex1:in_ideal((x+y)^2,[y^2,x^2+2*x*y],[x,y]) - Ex2:in_ideal(x+y,[y^2,x^2+2*x*y],[x,y]) - ''' - return GiacMethods['in_ideal'](self, *args) - - def incidence_matrix(self, *args): - r'''From Giac's documentation: - Help for incidence_matrix: - incidence_matrix(Graph(G)) - Returns the incidence matrix of G whose rows are indexed by the vertices and columns by the edges (in order defined by the command 'edges'). - See also: 1/ incident_edges - Ex1:incidence_matrix(graph("tetrahedron")) - ''' - return GiacMethods['incidence_matrix'](self, *args) - - def incident_edges(self, *args): - r'''From Giac's documentation: - Help for incident_edges: - incident_edges(Graph(G),Vrtx(v)) - Returns the list of all edges incident to the vertex v of G (or to the vertices in the list v). - See also: 1/ adjacency_matrix 2/ vertex_degree 3/ incidence_matrix 4/ neighbors - Ex1:incident_edges(cycle_graph(8),[1,5,7]) - ''' - return GiacMethods['incident_edges'](self, *args) - - def incircle(self, *args): - r'''From Giac's documentation: - Help for incircle: - incircle((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - incircle(A,B,C) draws the incircle of the triangle ABC. - See also: 1/ excircle 2/ circumcircle - Ex1:incircle(0,1,1+i) - ''' - return GiacMethods['incircle'](self, *args) - - def increasing_power(self, *args): - r'''From Giac's documentation: - Help for increasing_power: - increasing_power(:=Intg(0 or 1)) - Pseudo-variable to control the display of polynomials. - See also: 1/ cas_setup - Ex1: increasing_power:=1 - Ex2: increasing_power:=0 - ''' - return GiacMethods['increasing_power'](self, *args) - - def independence_number(self, *args): - r'''From Giac's documentation: - Help for independence_number: - independence_number(Graph(G)) - Returns the independence number of G. - See also: 1/ clique_number 2/ graph_complement 3/ maximum_clique 4/ maximum_independent_set - Ex1:independence_number(complete_graph(3,4)) - ''' - return GiacMethods['independence_number'](self, *args) - - def indets(self, *args): - r'''From Giac's documentation: - Help for indets: - indets(Expr) - List of variables in the expression. - See also: 1/ has 2/ lvar - Ex1:indets(exp(x)*2*sin(y)) - ''' - return GiacMethods['indets'](self, *args) - - def index(self, *args): - r'''From Giac's documentation: - Help for index: - index(Vect,Expr) - Index of the first position of an object in a list, a string or a set or returns an error message. - See also: 1/ find 2/ member - Ex1:index([3,x,1,2,1,3],1) - Ex2:index([0,1,3,2,4,2,5],2) - Ex3:index(%{4,3,1,2%},1) - Ex4:index("abracadabrant","c") - ''' - return GiacMethods['index'](self, *args) - - def induced_subgraph(self, *args): - r'''From Giac's documentation: - Help for induced_subgraph: - induced_subgraph(Graph(G),Lst(V)) - Returns the subgraph of G induced by the vertices in list V. - See also: 1/ subgraph - Ex1:induced_subgraph(cycle_graph(6),[1,2,6]) - ''' - return GiacMethods['induced_subgraph'](self, *args) - - def inequationplot(self, *args): - r'''From Giac's documentation: - Help for inequationplot: - inequationplot(Expr,[x=xrange,y=yrange],[xstep],[ystep]) - Shows the graph of the solutions of inequalities with 2 variables. - See also: 1/ plotfunc 2/ plotcontour 3/ plotdensity 4/ plotimplicit - Ex1:inequationplot(x^2-y^2<3) - Ex2:inequationplot(x^2-y^2<3,[x=-2..2,y=-2..2],xstep=0.1,ystep=0.1) - Ex3:inequationplot(3-(x^2-y^2),[x=-2..2,y=-2..2],xstep=0.1,ystep=0.1) - Ex4:inequationplot([x+y>3,x^2ln(a*b^n) for integers n. - See also: 1/ texpand - Ex1:lncollect(ln(x)+2*ln(y)) - ''' - return GiacMethods['lncollect'](self, *args) - - def lnexpand(self, *args): - r'''From Giac's documentation: - Help for lnexpand: - lnexpand(Expr) - Expands logarithms. - See also: 1/ texpand 2/ expexpand 3/ trigexpand - Ex1:lnexpand(ln(3*x)) - ''' - return GiacMethods['lnexpand'](self, *args) - - def locus(self, *args): - r'''From Giac's documentation: - Help for locus: - locus(Pnt,Elem) - locus(M,A) draws the locus of M (or locus(d,A) draws the envelope of d) when A:=element(C) (C is a curve). The example instructions below must be written in a geometric level on different lines. - See also: 1/ envelope 2/ trace - Ex1: A:=element(circle(i,1+i));M:=homothety(0,2,A);locus(M,A) - Ex2: A:=element(line(x=0));d:=perpen_bisector(1,A);locus(d,A) - ''' - return GiacMethods['locus'](self, *args) - - def log(self, *args): - r'''From Giac's documentation: - Help for log: - log(Expr or Opt) - Natural logarithm or option of the convert or convertir command (id trig2exp). - See also: 1/ exp 2/ convert 3/ trig2exp 4/ log10 - Ex1:log(1) - Ex2:log(e) - Ex3: convert(cos(x),ln) - ''' - return GiacMethods['log'](self, *args) - - def log10(self, *args): - r'''From Giac's documentation: - Help for log10: - log10(Expr) - Common logarithm (base 10). - See also: 1/ alog10 2/ ln - Ex1:log10(10) - ''' - return GiacMethods['log10'](self, *args) - - def logarithmic_regression(self, *args): - r'''From Giac's documentation: - Help for logarithmic_regression: - logarithmic_regression(Lst||Mtrx(A),[Lst]) - Returns the coefficients a and b of y=a*ln(x)+b : it is the best logarithm which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ exponential_regression - Ex1:logarithmic_regression([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex2:logarithmic_regression([1.0,2.0,3.0,4.0],[1.0,4.0,9.0,16.0]) - ''' - return GiacMethods['logarithmic_regression'](self, *args) - - def logarithmic_regression_plot(self, *args): - r'''From Giac's documentation: - Help for logarithmic_regression_plot: - logarithmic_regression_plot(Lst||Mtrx(A),[Lst]) - Returns the plot of y=a*ln(x)+b : it is the best logarithm which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ exponential_regression_plot - Ex1:logarithmic_regression_plot([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex2:logarithmic_regression_plot([1.0,2.0,3.0,4.0],[1.0,4.0,9.0,16.0]) - ''' - return GiacMethods['logarithmic_regression_plot'](self, *args) - - def logb(self, *args): - r'''From Giac's documentation: - Help for logb: - logb(Real) - Logarithm with base b. - See also: 1/ log 2/ log10 - Ex1:logb(5,2) - Ex2:logb(7,10) - ''' - return GiacMethods['logb'](self, *args) - - def logistic_regression(self, *args): - r'''From Giac's documentation: - Help for logistic_regression: - logistic_regression(Lst(L),Real(x0),Real(y0)) - Returns y,y',C,y'max,xmax,R : y is a logistic function (sol of y'/y=a*y+b), such that y(x0)=y0 and where [y'(x0),y'(x0+1)...] is the best approximation of L. - See also: 1/ polynomial_regression 2/ power_regression 3/ linear_regression - Ex1:logistic_regression(evalf([1,2,4,6,8,7,5]),1,2) - Ex2:logistic_regression([0.0,1.0,2.0,3.0,4.0],0.0,1.0) - ''' - return GiacMethods['logistic_regression'](self, *args) - - def logistic_regression_plot(self, *args): - r'''From Giac's documentation: - Help for logistic_regression_plot: - logistic_regression_plot(Lst(L),Real(x0),Real(y0)) - Returns the plot of a logistic function y such that y(x0)=y0 and where [y'(x0),y'(x0+1)...] is the best approximation of L. - See also: 1/ polynomial_regression_plot 2/ power_regression_plot 3/ linear_regression_plot - Ex1:logistic_regression_plot(evalf([1,2,4,6,8,7,5]),1,2) - Ex2:logistic_regression_plot([0.0,1.0,2.0,3.0,4.0],0.0,1.0) - ''' - return GiacMethods['logistic_regression_plot'](self, *args) - - def lower(self, *args): - r'''From Giac's documentation: - Help for lower: - lower(Mtrx||Strng) - Returns the lower triangular matrix (under the diagonal, included) or writes a string in lowercase. - See also: 1/ diag 2/ upper - Ex1:lower([[1,2,3],[4,5,6],[7,8,9]]) - Ex2:lower("HELLO") - ''' - return GiacMethods['lower'](self, *args) - - def lowest_common_ancestor(self, *args): - r'''From Giac's documentation: - Help for lowest_common_ancestor: - lowest_common_ancestor(Graph(T),Vrtx(r),Seq(u,v)||Lst([u1,v1],[u2,v2],...)) - Returns the lowest common ancestor of nodes u and v in the tree graph T with root r, or the list of lowest common ancestors of all pairs [uk,vk]. - See also: 1/ is_tree 2/ tree_height - Ex1: T:=random_tree(30); lowest_common_ancestor(T,15,10,20) - Ex2: T:=random_tree(30); lowest_common_ancestor(T,15,[[10,20],[11,19]]) - ''' - return GiacMethods['lowest_common_ancestor'](self, *args) - - def lowpass(self, *args): - r'''From Giac's documentation: - Help for lowpass: - lowpass(Lst(s),Real(c),[Intg(samplerate)]) - Returns the result of applying a simple first-order lowpass RC filter with cutoff frequency c (the default samplerate is 44100) to the given signal s. - See also: 1/ highpass 2/ moving_average - Ex1: f:=unapply(periodic(sign(x),x,-1/880,1/880),x):;s:=createwav(apply(f,soundsec(1))):;playsnd(lowpass(s,1000)) - ''' - return GiacMethods['lowpass'](self, *args) - - def lp_assume(self, *args): - r'''From Giac's documentation: - Help for lp_assume: - lp_assume(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_assume'](self, *args) - - def lp_bestprojection(self, *args): - r'''From Giac's documentation: - Help for lp_bestprojection: - lp_bestprojection(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_bestprojection'](self, *args) - - def lp_binary(self, *args): - r'''From Giac's documentation: - Help for lp_binary: - lp_binary(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_binary'](self, *args) - - def lp_binaryvariables(self, *args): - r'''From Giac's documentation: - Help for lp_binaryvariables: - lp_binaryvariables(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_binaryvariables'](self, *args) - - def lp_breadthfirst(self, *args): - r'''From Giac's documentation: - Help for lp_breadthfirst: - lp_breadthfirst(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_breadthfirst'](self, *args) - - def lp_depthfirst(self, *args): - r'''From Giac's documentation: - Help for lp_depthfirst: - lp_depthfirst(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_depthfirst'](self, *args) - - def lp_depthlimit(self, *args): - r'''From Giac's documentation: - Help for lp_depthlimit: - lp_depthlimit(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_depthlimit'](self, *args) - - def lp_firstfractional(self, *args): - r'''From Giac's documentation: - Help for lp_firstfractional: - lp_firstfractional(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_firstfractional'](self, *args) - - def lp_gaptolerance(self, *args): - r'''From Giac's documentation: - Help for lp_gaptolerance: - lp_gaptolerance(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_gaptolerance'](self, *args) - - def lp_hybrid(self, *args): - r'''From Giac's documentation: - Help for lp_hybrid: - lp_hybrid(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_hybrid'](self, *args) - - def lp_initialpoint(self, *args): - r'''From Giac's documentation: - Help for lp_initialpoint: - lp_initialpoint(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_initialpoint'](self, *args) - - def lp_integer(self, *args): - r'''From Giac's documentation: - Help for lp_integer: - lp_integer(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_integer'](self, *args) - - def lp_integertolerance(self, *args): - r'''From Giac's documentation: - Help for lp_integertolerance: - lp_integertolerance(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_integertolerance'](self, *args) - - def lp_integervariables(self, *args): - r'''From Giac's documentation: - Help for lp_integervariables: - lp_integervariables(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_integervariables'](self, *args) - - def lp_interiorpoint(self, *args): - r'''From Giac's documentation: - Help for lp_interiorpoint: - lp_interiorpoint(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_interiorpoint'](self, *args) - - def lp_iterationlimit(self, *args): - r'''From Giac's documentation: - Help for lp_iterationlimit: - lp_iterationlimit(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_iterationlimit'](self, *args) - - def lp_lastfractional(self, *args): - r'''From Giac's documentation: - Help for lp_lastfractional: - lp_lastfractional(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_lastfractional'](self, *args) - - def lp_maxcuts(self, *args): - r'''From Giac's documentation: - Help for lp_maxcuts: - lp_maxcuts(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_maxcuts'](self, *args) - - def lp_maximize(self, *args): - r'''From Giac's documentation: - Help for lp_maximize: - lp_maximize(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_maximize'](self, *args) - - def lp_method(self, *args): - r'''From Giac's documentation: - Help for lp_method: - lp_method(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_method'](self, *args) - - def lp_mostfractional(self, *args): - r'''From Giac's documentation: - Help for lp_mostfractional: - lp_mostfractional(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_mostfractional'](self, *args) - - def lp_nodelimit(self, *args): - r'''From Giac's documentation: - Help for lp_nodelimit: - lp_nodelimit(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_nodelimit'](self, *args) - - def lp_nodeselect(self, *args): - r'''From Giac's documentation: - Help for lp_nodeselect: - lp_nodeselect(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_nodeselect'](self, *args) - - def lp_nonnegative(self, *args): - r'''From Giac's documentation: - Help for lp_nonnegative: - lp_nonnegative(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_nonnegative'](self, *args) - - def lp_nonnegint(self, *args): - r'''From Giac's documentation: - Help for lp_nonnegint: - lp_nonnegint(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_nonnegint'](self, *args) - - def lp_pseudocost(self, *args): - r'''From Giac's documentation: - Help for lp_pseudocost: - lp_pseudocost(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_pseudocost'](self, *args) - - def lp_simplex(self, *args): - r'''From Giac's documentation: - Help for lp_simplex: - lp_simplex(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_simplex'](self, *args) - - def lp_timelimit(self, *args): - r'''From Giac's documentation: - Help for lp_timelimit: - lp_timelimit(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_timelimit'](self, *args) - - def lp_variables(self, *args): - r'''From Giac's documentation: - Help for lp_variables: - lp_variables(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_variables'](self, *args) - - def lp_varselect(self, *args): - r'''From Giac's documentation: - Help for lp_varselect: - lp_varselect(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_varselect'](self, *args) - - def lp_verbose(self, *args): - r'''From Giac's documentation: - Help for lp_verbose: - lp_verbose(Opt) - Options for lpsolve command. - See also: 1/ lpsolve - ''' - return GiacMethods['lp_verbose'](self, *args) - - def lpsolve(self, *args): - r'''From Giac's documentation: - Help for lpsolve: - lpsolve(Expr(o),[List(c)],[bounds],[options]) - Solves a (mixed integer/binary) LP problem in general form. - See also: 1/ nlpsolve 2/ fsolve - Ex1:lpsolve(2x+y-z+4,[x<=1,y>=2,x+3y-z=2,2x-y+z<=8,-x+y<=5]) - Ex2:lpsolve(-4x-5y,[x+2y<=6,5x+4y<=20,0<=x,0<=y]) - Ex3:lpsolve(-7x+2y,[4x-12y<=20,-x+3y<=3],x=-5..5,y=0..inf,lp_maximize=true) - Ex4:lpsolve(x-y-2z+3,[-3x-y+z<=3,2x-3y>=4z,x-z=y,x>=0,y<=0],lp_maximize) - Ex5:lpsolve(-x-y,[y<=3x+1/2,y<=-5x+2],assume=lp_nonnegative) - Ex6:lpsolve(x+y,[x<=8,-x+y<=4,-x+2y>=6,2x+y<=25,3x+y>=18,-x+2y>=6],assume=lp_nonnegative) - Ex7:lpsolve(45.55x1+21.87x2,[1.64x1+2.67x2<=31.2,2.11x1+2.3x2>=13.9],assume=lp_nonnegative) - Ex8:lpsolve(3x+4y,[x<=4,x+3y<=15,-x+2y>=5,x-y>=9,x+y=6],assume=lp_nonnegative,lp_maximize=true) - Ex9:lpsolve(-6x+4y+z,[5x-10y<=20,2z-3y=6,-x+3y<=3],x=1..20,y=0..inf) - Ex10:lpsolve(-5x-7y,[7x+y<=35,-x+3y<=6],assume=integer) - Ex11:lpsolve(x+3y+3z,[x+3y+2z<=7,2x+2y+z<=11],assume=lp_nonnegative,lp_integervariables=[x,z],lp_maximize) - Ex12:lpsolve(2x+5y,[3x-y=1,x-y<=5],assume=nonnegint) - Ex13:lpsolve(x1+x2,[2x1+5x2<=16,6x1+5x2<=30],assume=nonnegint,lp_maximize) - Ex14:lpsolve(8x1+11x2+6x3+4x4,[5x1+7x2+4x3+3x4<=14],assume=lp_binary,lp_maximize) - Ex15:lpsolve(x1+x2,[1867x1+1913x2=3618894],assume=nonnegint,lp_verbose=true) - ''' - return GiacMethods['lpsolve'](self, *args) - - def lsmod(self, *args): - r'''From Giac's documentation: - Help for lsmod: - lsmod(NULL) - Displays the installed dynamic libraries. - See also: 1/ insmod 2/ rmmod - Ex1:lsmod() - ''' - return GiacMethods['lsmod'](self, *args) - - def lsq(self, *args): - r'''From Giac's documentation: - Help for lsq: - lsq(Mtrx(A),(Mtrx || Vect)(B)) - Returns the vector (resp matrix) X which is the minimum of the euclidean (resp Frobenius) norm of A*X-B corresponding to the linear system A*X=B when B is a vector (resp matrix). - See also: 1/ lu 2/ QR - Ex1:lsq([[1,2],[3,4]],[5,11]) - Ex2:lsq([[1,2],[3,4]],[[5,-1],[11,-1]]) - ''' - return GiacMethods['lsq'](self, *args) - - def lu(self, *args): - r'''From Giac's documentation: - Help for lu: - lu(Mtrx) - For a numerical matrix A, returns p permutation, L and U such that PA=LU (P=permu2mat(p)). - See also: 1/ qr 2/ cholesky 3/ LU - Ex1:lu([[1,2],[3,4]]) - Ex2:lu([[6,12,18],[5,14,31],[3,8,18]]) - ''' - return GiacMethods['lu'](self, *args) - - def lvar(self, *args): - r'''From Giac's documentation: - Help for lvar: - lvar(Expr) - List of variables of an object (with rational dependence). - See also: 1/ lname 2/ has - Ex1:lvar(exp(x)*2*sin(y)) - Ex2:lvar(exp(x)*2*sin(y)+ln(x)) - ''' - return GiacMethods['lvar'](self, *args) - - def mRow(self, *args): - r'''From Giac's documentation: - Help for mRow: - mRow(Expr(Xpr),Mtrx(A),Intg(n1)) - Multiplies row n1 of the matrix A by Xpr. - See also: 1/ rowAdd 2/ mRowAdd - Ex1:mRow(12,[[1,2],[3,4],[5,6]],0) - ''' - return GiacMethods['mRow'](self, *args) - - def mRowAdd(self, *args): - r'''From Giac's documentation: - Help for mRowAdd: - mRowAdd(Expr(Xpr),Mtrx(A),Intg(n1),Intg(n2)) - Multiplies row n1 of the matrix A by Xpr, then adds it to the row n2. - See also: 1/ rowAdd 2/ mRow - Ex1:mRowAdd(12,[[1,2],[3,4],[5,6]],0,2) - ''' - return GiacMethods['mRowAdd'](self, *args) - - def magenta(self, *args): - r'''From Giac's documentation: - Help for magenta: - magenta(Opt) - Option of the display command to display with color. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),red) - Ex2: F:=display(point(2+1.5*i),point_point+green) - ''' - return GiacMethods['magenta'](self, *args) - - def make_directed(self, *args): - r'''From Giac's documentation: - Help for make_directed: - make_directed(Graph(G),[Mrtx(A)]) - Returns the copy of undirected graph G in which every edge is converted to a pair of arcs [and with weights specified by matrix A]. - See also: 1/ is_directed 2/ make_weighted 3/ underlying_graph - Ex1:make_directed(cycle_graph(4),[[0,0,0,1],[2,0,1,3],[0,1,0,4],[5,0,4,0]]) - ''' - return GiacMethods['make_directed'](self, *args) - - def make_weighted(self, *args): - r'''From Giac's documentation: - Help for make_weighted: - make_weighted(Graph(G),[Mrtx(M)]) - Returns a copy of G with edge/arc weights set as specified by matrix M. If M is omitted, a square matrix of ones is used. If G is undirected, M is assumed to be symmetric. - See also: 1/ get_edge_weight 2/ is_weighted 3/ make_directed 4/ set_edge_weight 5/ underlying_graph 6/ weight_matrix - Ex1:make_weighted(cycle_graph(3),[[0,2,3],[2,0,1],[3,1,0]]) - ''' - return GiacMethods['make_weighted'](self, *args) - - def makelist(self, *args): - r'''From Giac's documentation: - Help for makelist: - makelist(Fnc,InitVal,FinalVal,StepVal) - Returns a list made with a function or with a constant. - See also: 1/ seq 2/ range 3/ makemat 4/ $ - Ex1:makelist(x->x^2,1,10,2) - Ex2:makelist(4,1,10) - Ex3:makelist(4,5,10) - Ex4:makelist(x->ifte(x<5,"A","B"),1,10) - ''' - return GiacMethods['makelist'](self, *args) - - def makemat(self, *args): - r'''From Giac's documentation: - Help for makemat: - makemat(Fnct(f),RowsNumb,ColsNumb) - Creates a matrix. - See also: 1/ matrix - Ex1:makemat((j,k)->j+k,3,2) - Ex2:makemat((j,k)->1/(j+k+1),2,3) - Ex3:makemat(sqrt(2),2,3) - ''' - return GiacMethods['makemat'](self, *args) - - def makesuite(self, *args): - r'''From Giac's documentation: - Help for makesuite: - makesuite(Vect||Lst) - Returns a sequence made with a vector. - See also: 1/ makevector 2/ op - Ex1:makesuite([1,2,3]) - ''' - return GiacMethods['makesuite'](self, *args) - - def makevector(self, *args): - r'''From Giac's documentation: - Help for makevector: - makevector(Seq) - Returns a vector made with a sequence. - See also: 1/ makesuite - Ex1:makevector(1,2,3) - ''' - return GiacMethods['makevector'](self, *args) - - def map(self, *args): - r'''From Giac's documentation: - Help for map: - map(Lst(l),Fnc(f)) - Applies the function f at the elements of the list l or at a polynomial of internal format. - See also: 1/ apply 2/ set 3/ unapply - Ex1:map([1,2,3],x->x^3) - Ex2:map([1,2,3],unapply(x^3,x)) - Ex3:map(%%%{1,[2,0]%%%}+%%%{2,[1,1]%%%},(a,b,c)->a*(b+2*c)) - ''' - return GiacMethods['map'](self, *args) - - def maple2mupad(self, *args): - r'''From Giac's documentation: - Help for maple2mupad: - maple2mupad(Str("Name_Maplefile"),Str("Name_Mupadfile")) - maple2mupad("file1","file2") translates file1(Maple) to file2(MuPAD). - See also: 1/ maple2xcas - ''' - return GiacMethods['maple2mupad'](self, *args) - - def maple2xcas(self, *args): - r'''From Giac's documentation: - Help for maple2xcas: - maple2xcas(Str("NameMapleFile"),Str("NameXcasFile")) - maple2xcas("file1","file2") translates file1(Maple) to file2(Xcas). - See also: 1/ maple2mupad - ''' - return GiacMethods['maple2xcas'](self, *args) - - def maple_ifactors(self, *args): - r'''From Giac's documentation: - Help for maple_ifactors: - maple_ifactors(Intg(n)) - Returns 1 or -1 for the sign and the prime factors with their multiplicity of n in a matrix, such as ifactors in Maple. - See also: 1/ ifactors - Ex1:maple_ifactors(120) - ''' - return GiacMethods['maple_ifactors'](self, *args) - - def maple_mode(self, *args): - r'''From Giac's documentation: - Help for maple_mode: - maple_mode(Intg(0) or 1 or 2 or 3) - Switches to mode Xcas (0), Maple (1), Mupad (2), TI89 (3). - See also: 1/ python_compat - Ex1:maple_mode(1) - Ex2:maple_mode(0) - ''' - return GiacMethods['maple_mode'](self, *args) - - def markov(self, *args): - r'''From Giac's documentation: - Help for markov: - markov(Mtrx(M),[Real(eps)]) - Computation of the proper elements of a Markov chain transition matrix M, returns the list of sequence of positive recurrent states, the list of corresponding invariant probabilities, the list of other strongly connected components, the list of probabilities to end up in the sequence of recurrent states. - See also: 1/ randmarkov 2/ plotproba - Ex1:markov([[0,0,1/2,0,1/2],[0,0,1,0,0],[1/4,1/4,0,1/4,1/4],[0,0,1/2,0,1/2],[0,0,0,0,1]]) - ''' - return GiacMethods['markov'](self, *args) - - def mat2list(self, *args): - r'''From Giac's documentation: - Help for mat2list: - mat2list(Mtrx) - Returns the list of the terms of the matrix. - See also: 1/ list2mat 2/ flatten - Ex1:mat2list([[1,8],[4,9]]) - ''' - return GiacMethods['mat2list'](self, *args) - - def mathml(self, *args): - r'''From Giac's documentation: - Help for mathml: - mathml(Expr) - Converts the expression into a string to display maths for the web. - See also: 1/ export_mathml 2/ latex - Ex1:mathml(1/2) - ''' - return GiacMethods['mathml'](self, *args) - - def matpow(self, *args): - r'''From Giac's documentation: - Help for matpow: - matpow(Mtrx,Intg(n)) - Calculates the nth power of a matrix by jordanization. - See also: 1/ &^ 2/ ^ - Ex1:matpow([[1,2],[3,4]],n) - ''' - return GiacMethods['matpow'](self, *args) - - def matrix(self, *args): - r'''From Giac's documentation: - Help for matrix: - matrix(Intg(p),Intg(q),(Fnc(f) or Val(a))) - Makes a matrix m(j,k) with p rows and q cols, m(j,k)=f(j,k) or m(j,k)=a : the index start at 0 or 1 according to the mode (Xcas or Maple) (or option of apply) or make a matrix with a table. - See also: 1/ makemat 2/ makelist 3/ apply - Ex1:matrix(2,3,(j,k)->1/(j+k+1)) - Ex2:matrix(3,2,(j,k)->j+k) - Ex3:matrix(2,3,4) - Ex4: A[0..2,0..2]:=1;A[0..1,1..2]:=2;a:=matrix(A) - ''' - return GiacMethods['matrix'](self, *args) - - def matrix_norm(self, *args): - r'''From Giac's documentation: - Help for matrix_norm: - matrix_norm(Mtrx,[2]||[inf]) - Matrix norm induced by l1norm or by l2norm or by linfinty norm. - See also: 1/ l1norm 2/ l2 norm 3/ linfnorm 4/ frobenius_norm - Ex1:matrix_norm([[1,2,3],[3,-9,6],[4,5,6]]) - Ex2:matrix_norm([[1,2,3],[3,-9,6],[4,5,6]],1) - Ex3:matrix_norm([[1,2,3],[3,-9,6],[4,5,6]],2) - Ex4:matrix_norm([[1,2,3],[3,-9,6],[4,5,6]],inf) - ''' - return GiacMethods['matrix_norm'](self, *args) - - def max(self, *args): - r'''From Giac's documentation: - Help for max: - max(Seq||Lst) - Maximum of elements of a sequence or a list of reals. - See also: 1/ min - Ex1:max(25,35) - ''' - return GiacMethods['max'](self, *args) - - def maxflow(self, *args): - r'''From Giac's documentation: - Help for maxflow: - maxflow(Graph(G),Vrtx(s),Vrtx(t)) - Returns the optimal value for the max flow problem for network G with the source s and sink t along with an optimal flow (as a matrix). - See also: 1/ minimum_cut - Ex1:maxflow(digraph(%{[[1,2],2],[[2,3],4],[[3,4],3],[[1,5],3],[[5,2],1],[[5,4],2]%}),1,4) - ''' - return GiacMethods['maxflow'](self, *args) - - def maximal_independent_set(self, *args): - r'''From Giac's documentation: - Help for maximal_independent_set: - maximal_independent_set(Graph(G)) - Returns a maximal set of mutually independent (non-adjacent) vertices in G. - See also: 1/ maximum_independent_set - Ex1:maximal_independent_set(graph("petersen")) - ''' - return GiacMethods['maximal_independent_set'](self, *args) - - def maximize(self, *args): - r'''From Giac's documentation: - Help for maximize: - maximize(Expr,[Constr],Vars,[Options]) - Maximizes a function. - Ex1:maximize(sin(x),[x=0..4]) - Ex2:maximize(x^4-x^2,x=-3..3,locus) - Ex3:maximize(piecewise(x<=-2,x+6,x<=1,x^2,3/2-x/2),x=-3..2) - Ex4:maximize(x^2-3x+y^2+3y+3,[x=2..4,y=-4..-2],point) - Ex5:maximize(2x^2-y^2+6y,x^2+y^2<=16,[x,y]) - Ex6:maximize(sqrt(x^2+y^2)-z,[x^2+y^2<=16,x+y+z=10],[x,y,z]) - Ex7:maximize((1+x^2+3y+5x-4*x*y)/(1+x^2+y^2),x^2/4+y^2/3=9,[x,y]) - Ex8:maximize(cos(x)^2+cos(y)^2,x+y=pi/4,[x,y],locus) - ''' - return GiacMethods['maximize'](self, *args) - - def maximum_clique(self, *args): - r'''From Giac's documentation: - Help for maximum_clique: - maximum_clique(Graph(G)) - Returns the maximum clique of undirected graph G as a list of vertices. - See also: 1/ clique_number 2/ is_clique 3/ maximum_independent_set - Ex1:maximum_clique(graph_complement(complete_graph(3,4))) - ''' - return GiacMethods['maximum_clique'](self, *args) - - def maximum_degree(self, *args): - r'''From Giac's documentation: - Help for maximum_degree: - maximum_degree(Graph(G)) - Returns the largest degree among the vertices of G. - See also: 1/ minimum_degree 2/ vertex_degree - Ex1:maximum_degree(digraph(trail(1,2,3,4,5,6,4,7,8,2))) - ''' - return GiacMethods['maximum_degree'](self, *args) - - def maximum_independent_set(self, *args): - r'''From Giac's documentation: - Help for maximum_independent_set: - maximum_independent_set(Graph(G)) - Returns the maximum independent vertex set of G. - See also: 1/ clique_number 2/ graph_complement 3/ independence_number 4/ maximum_clique - Ex1:maximum_independent_set(complete_graph(3,4)) - ''' - return GiacMethods['maximum_independent_set'](self, *args) - - def maximum_matching(self, *args): - r'''From Giac's documentation: - Help for maximum_matching: - maximum_matching(Graph(G)) - Returns the list of edges representing maximum matching in G. - See also: 1/ maximum_independent_set - Ex1: G:=graph("soccerball"); draw_graph(highlight_edges(G,maximum_matching(G))) - ''' - return GiacMethods['maximum_matching'](self, *args) - - def maxnorm(self, *args): - r'''From Giac's documentation: - Help for maxnorm: - maxnorm(Vect or Mtrx) - Norm with the max of a vector (or of a matrix): maxnorm([x1,x2,..,xn])=max(|x1|,..,|xn|). - See also: 1/ l2norm 2/ l1norm - Ex1:maxnorm([1,2]) - Ex2:maxnorm([1,2,3,-4]) - Ex3:maxnorm([[1,2],[3,-4]]) - ''' - return GiacMethods['maxnorm'](self, *args) - - def mean(self, *args): - r'''From Giac's documentation: - Help for mean: - mean(Lst||Mtrx,[Lst]) - Mean of a list with the second argument as weight or of the columns of a matrix. - See also: 1/ stddev - Ex1:mean([1,2,3]) - Ex2:mean([1,2,3],[1,2,3]) - Ex3:mean([[1,2,3],[1,2,3]]) - ''' - return GiacMethods['mean'](self, *args) - - def median(self, *args): - r'''From Giac's documentation: - Help for median: - median(Lst||Mtrx,[Lst]) - Returns the median of a list with the second argument as weight or of the columns of a matrix. - See also: 1/ quartiles - Ex1:median([1,2,3,5,10,4]) - Ex2:median([1,2,3,5,10,4],[1,2,3,1,2,3]) - ''' - return GiacMethods['median'](self, *args) - - def median_line(self, *args): - r'''From Giac's documentation: - Help for median_line: - median_line((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - median_line(A,B,C) draws the median-line through A of the triangle ABC. - See also: 1/ midpoint 2/ perpen_bisector - Ex1:median_line(-1,1-i,i) - ''' - return GiacMethods['median_line'](self, *args) - - def member(self, *args): - r'''From Giac's documentation: - Help for member: - member(Elem(e),(Lst(l) or Set(l))) - Tests if e is in the list or set l (=0, or k+1 with l[k]=e). - See also: 1/ contains 2/ est_element 3/ find 4/ index - Ex1:member(1,[4,3,1,2]) - Ex2:member(1,%{4,3,1,2%}) - ''' - return GiacMethods['member'](self, *args) - - def mgf(self, *args): - r'''From Giac's documentation: - Help for mgf: - mgf(Func,[Real(Param_1),Real(Param_2)]) - Returns the moment generating function of a probability distribution from normal, binomial, Poisson, beta, gamma distribution - Ex1:mgf(normald,1,0) - Ex2:mgf(poisson,5) - Ex3:mgf(binomial,n,p) - ''' - return GiacMethods['mgf'](self, *args) - - def mid(self, *args): - r'''From Giac's documentation: - Help for mid: - mid(Lst(l) or Str(l),Intg(d),Intg(n)) - Returns the extracted list of l with n elements (by default n=size(l)-d) and beginning at index d. - See also: 1/ head 2/ tail 3/ left 4/ right 5/ subMat - Ex1:mid([0,1,2,3,4,5,6],2,3) - Ex2:mid([0,1,2,3,4,5,6],2) - Ex3:mid("azertyuiop",2,4) - Ex4:mid("azertyuiop",2) - Ex5:mid([[1,2],[3,4],[5,6]],1) - ''' - return GiacMethods['mid'](self, *args) - - def middle_point(self, *args): - r'''From Giac's documentation: - Help for middle_point: - middle_point(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['middle_point'](self, *args) - - def midpoint(self, *args): - r'''From Giac's documentation: - Help for midpoint: - midpoint((Pnt or Cplx),(Pnt or Cplx)) - midpoint(A,B) draws the midpoint of the segment AB. - See also: 1/ median_line 2/ perpen_bisector - Ex1:midpoint(-2,2i) - ''' - return GiacMethods['midpoint'](self, *args) - - def min(self, *args): - r'''From Giac's documentation: - Help for min: - min(Seq||Lst) - Minimum of elements of a sequence or a list of reals. - See also: 1/ max - Ex1:min(25,35) - ''' - return GiacMethods['min'](self, *args) - - def minimal_edge_coloring(self, *args): - r'''From Giac's documentation: - Help for minimal_edge_coloring: - minimal_edge_coloring(Graph(G),[sto]) - Finds the minimal edge coloring of G and returns the sequence n,L where n is the class of G (1 for D colors and 2 for D+1 colors) and L is the list of colors of edges of G as returned by the edges command, or a copy of G with colored edges if the option 'sto' is specified. - See also: 1/ chromatic_index 2/ minimal_vertex_coloring 3/ edges - Ex1:minimal_edge_coloring(graph("petersen")) - Ex2: G:=minimal_edge_coloring(graph("dodecahedron"),sto); draw_graph(G) - ''' - return GiacMethods['minimal_edge_coloring'](self, *args) - - def minimal_spanning_tree(self, *args): - r'''From Giac's documentation: - Help for minimal_spanning_tree: - minimal_spanning_tree(Graph(G)) - Returns the minimal spanning tree of undirected graph G. - See also: 1/ spanning_tree - Ex1:minimal_spanning_tree(graph([[0,1,0,4,0,0],[1,0,1,0,4,0],[0,1,0,3,0,1],[4,0,3,0,1,0],[0,4,0,1,0,4],[0,0,1,0,4,0]])) - ''' - return GiacMethods['minimal_spanning_tree'](self, *args) - - def minimal_vertex_coloring(self, *args): - r'''From Giac's documentation: - Help for minimal_vertex_coloring: - minimal_vertex_coloring(Graph(G),[sto]) - Computes the minimal vertex coloring for G and returns the colors in the order of vertices. If optional parameter "sto" is given, the colors are assigned to vertices and the modified copy of G is returned. - See also: 1/ chromatic_number 2/ is_vertex_colorable - Ex1:minimal_vertex_coloring(graph("petersen")) - Ex2: draw_graph(minimal_vertex_coloring(graph("petersen"),sto)) - ''' - return GiacMethods['minimal_vertex_coloring'](self, *args) - - def minimax(self, *args): - r'''From Giac's documentation: - Help for minimax: - minimax(Expr,Var=a..b,n,[Options]) - Implementation of Remez method for minimax polynomial approximation of degree n of a continuous bounded function on a..b. - Ex1:minimax(x*exp(-x),x=0..10,12) - Ex2:minimax(x*sin(x),x=0..10,15) - Ex3:minimax(ln(2+x-sin(x)^2),x=0..2*pi,20) - Ex4:minimax(cos(x^2-x+1),x=-2..2,17) - Ex5:minimax(atan(x),x=-5..5,30) - Ex6:minimax(tanh(sin(9x)),x=-1/2..1/2,15) - Ex7:minimax(abs(x),x=-1..1,25) - Ex8:minimax(abs(x)*sqrt(abs(x)),x=-2..2,15) - Ex9:minimax(min(1/cosh(3*sin(x)),sin(9x/10)),x=-3..4,30) - Ex10:minimax(when(x==0,0,exp(-1/x^2)),x=-1..1,25) - ''' - return GiacMethods['minimax'](self, *args) - - def minimize(self, *args): - r'''From Giac's documentation: - Help for minimize: - minimize(Expr,[Constr],Vars,[Options]) - Minimizes a function. - Ex1:minimize(sin(x),[x=0..4]) - Ex2:minimize(x^4-x^2,x=-3..3,locus) - Ex3:minimize(piecewise(x<=-2,x+6,x<=1,x^2,3/2-x/2),x=-3..2) - Ex4:minimize(x^2-3x+y^2+3y+3,[x=2..4,y=-4..-2],point) - Ex5:minimize(2x^2-y^2+6y,x^2+y^2<=16,[x,y]) - Ex6:minimize(sqrt(x^2+y^2)-z,[x^2+y^2<=16,x+y+z=10],[x,y,z]) - Ex7:minimize((1+x^2+3y+5x-4*x*y)/(1+x^2+y^2),x^2/4+y^2/3=9,[x,y]) - Ex8:minimize(cos(x)^2+cos(y)^2,x+y=pi/4,[x,y],locus) - ''' - return GiacMethods['minimize'](self, *args) - - def minimum_cut(self, *args): - r'''From Giac's documentation: - Help for minimum_cut: - minimum_cut(Graph(G),Vrtx(s),Vrtx(t)) - Returns the list of edges forming a minimum cut in a directed graph G with the source s and sink t. - See also: 1/ maxflow - Ex1:minimum_cut(digraph(%{[[1,2],2],[[2,3],4],[[3,4],3],[[1,5],3],[[5,2],1],[[5,4],2]%}),1,4) - ''' - return GiacMethods['minimum_cut'](self, *args) - - def minimum_degree(self, *args): - r'''From Giac's documentation: - Help for minimum_degree: - minimum_degree(Graph(G)) - Returns the smallest degree among the vertices of G. - See also: 1/ maximum_degree 2/ vertex_degree - Ex1:minimum_degree(digraph(trail(1,2,3,4,5,6,4,7,8,2))) - ''' - return GiacMethods['minimum_degree'](self, *args) - - def mkisom(self, *args): - r'''From Giac's documentation: - Help for mkisom: - mkisom(Vect,(Sign(1) or -1)) - Matrix of an isometry given by its proper elements. - See also: 1/ isom - Ex1:mkisom([1,2],1) - Ex2:mkisom([[1,0,0],pi/3],-1) - Ex3:mkisom(pi,1) - ''' - return GiacMethods['mkisom'](self, *args) - - def mksa(self, *args): - r'''From Giac's documentation: - Help for mksa: - mksa(Unit) - Converts units to the MKSA international unit system. - See also: 1/ convert 2/ ufactor - Ex1:mksa(1_N) - ''' - return GiacMethods['mksa'](self, *args) - - def modgcd(self, *args): - r'''From Giac's documentation: - Help for modgcd: - modgcd(Poly,Poly) - GCD of 2 polynomials, with the modular algorithm. - See also: 1/ gcd 2/ heugcd 3/ ezgcd 4/ psrgcd - Ex1:modgcd(x^4-1,(x-1)^2) - ''' - return GiacMethods['modgcd'](self, *args) - - def mods(self, *args): - r'''From Giac's documentation: - Help for mods: - mods(Intg,Intg) - Returns the Euclidean symmetric remainder of two integers. - See also: 1/ irem 2/ iquo 3/ mod 4/ fracmod - Ex1:mods(8,3) - Ex2:mods(10,4) - Ex3:mods(11,7) - ''' - return GiacMethods['mods'](self, *args) - - def monotonic(self, *args): - r'''From Giac's documentation: - Help for monotonic: - monotonic() - Returns a real that increases as time passes - Ex1:monotonic() - ''' - return GiacMethods['monotonic'](self, *args) - - def montre_tortue(self, *args): - r'''From Giac's documentation: - Help for montre_tortue: - montre_tortue(NULL) - Shows the turtle. - See also: 1/ cache_tortue - Ex1:montre_tortue() - ''' - return GiacMethods['montre_tortue'](self, *args) - - def moustache(self, *args): - r'''From Giac's documentation: - Help for moustache: - moustache(Lst,[Lst],[x=a..b||y=a..b]) - Box and Whisker plot for a statistical series. - See also: 1/ quartiles - Ex1:moustache([-1,1,2,2.2,3,4,-2,5]) - Ex2:moustache([1,2,3,5,10,4],x=1..2) - Ex3:moustache([1,2,3,5,10,4],[1,2,3,1,2,3]) - Ex4:moustache([[6,0,1,3,4,2,5],[0,1,3,4,2,5,6],[1,3,4,2,5,6,0],[3,4,2,5,6,0,1],[4,2,5,6,0,1,3],[2,5,6,0,1,3,4]]) - ''' - return GiacMethods['moustache'](self, *args) - - def moving_average(self, *args): - r'''From Giac's documentation: - Help for moving_average: - moving_average(Lst(A),Intg(n)) - Applies a moving average filter of length n to a signal sample A, and returns its result as an array of length nops(A)-n+1. - See also: 1/ lowpass - Ex1: snd:=soundsec(2):;data:=0.5*threshold(3*sin(2*pi*220*snd),[-1.0,1.0])+randvector(length(snd),normald,0,0.05):;moving_average(data,25) - ''' - return GiacMethods['moving_average'](self, *args) - - def moyal(self, *args): - r'''From Giac's documentation: - Help for moyal: - moyal(Expr,Expr,VectVar) - Moyal product of 2 symbols. - See also: 1/ - Ex1:moyal(x^2+y^4,x^4-y^2,[x,y],5) - ''' - return GiacMethods['moyal'](self, *args) - - def moyenne(self, *args): - r'''From Giac's documentation: - Help for moyenne: - moyenne(Lst||Mtrx,[Lst]) - Mean of a list with the second argument as weight or of the columns of a matrix. - See also: 1/ stddev - Ex1:moyenne([1,2,3]) - Ex2:moyenne([1,2,3],[1,2,3]) - Ex3:moyenne([[1,2,3],[1,2,3]]) - ''' - return GiacMethods['moyenne'](self, *args) - - def mul(self, *args): - r'''From Giac's documentation: - Help for mul: - mul(Expr||Lst,[Var||Lst],[Intg(a)],[Intg(b)],[Intg(p)]) - Multiplies the values of the expression when the variable go from a to b with a step p (product(expression,var,begin,end,step) by default p=1) or product of the elements of a list or product element by element of 2 lists or matrices. - See also: 1/ sum - Ex1:mul(n,n,1,10,2) - Ex2:mul(1/n,n,1,10) - Ex3:mul(1/n,n,11,1) - Ex4:mul(1/n,n,10,1,1) - Ex5:mul([2,3,4,5]) - Ex6:mul([2,3,4],[5,6,7]) - Ex7:mul([[2,3,4],[5,6,7]],[[2,3,4],[5,6,7]]) - ''' - return GiacMethods['mul'](self, *args) - - def mult_c_conjugate(self, *args): - r'''From Giac's documentation: - Help for mult_c_conjugate: - mult_c_conjugate(Expr) - Returns the expression after multiplication by the complex conjugate of the denominator (or of the numerator if no denominator). - See also: 1/ mult_conjugate - Ex1:mult_c_conjugate(1/(3+i*2)) - Ex2:mult_c_conjugate(3+i*2) - ''' - return GiacMethods['mult_c_conjugate'](self, *args) - - def mult_conjugate(self, *args): - r'''From Giac's documentation: - Help for mult_conjugate: - mult_conjugate(Expr) - Returns the expression after multiplication by the conjugate of the denominator (or of the numerator if no denominator). - See also: 1/ mult_c_conjugate - Ex1:mult_conjugate(sqrt(3)-sqrt(2)) - Ex2:mult_conjugate(1/(sqrt(3)-sqrt(2))) - ''' - return GiacMethods['mult_conjugate'](self, *args) - - def multinomial(self, *args): - r'''From Giac's documentation: - Help for multinomial: - multinomial(Intg(n),Vect(p),Vect(k)) - Returns n!/(k0!*k1!*..;kj!)*(p0^k0*p1^k1..*pj^kj) (sum(p)=1 and sum(k)=n). - See also: 1/ binomial 2/ randvector 3/ ranm - Ex1:multinomial(10,[0.5,0.5],[3,7]) - Ex2:multinomial(10,[0.2,0.3,0.5],[1,3,6]) - Ex3: randvector(3,multinomial,[1/2,1/3,1/6]) - Ex4: ranm(4,3,multinomial,[1/2,1/3,1/6]) - ''' - return GiacMethods['multinomial'](self, *args) - - def multiplier_conjugue(self, *args): - r'''From Giac's documentation: - Help for multiplier_conjugue: - multiplier_conjugue(Expr) - Returns the expression after multiplication by the conjugate of the denominator (or of the numerator if no denominator). - See also: 1/ mult_c_conjugate - Ex1:multiplier_conjugue(sqrt(3)-sqrt(2)) - Ex2:multiplier_conjugue(1/(sqrt(3)-sqrt(2))) - ''' - return GiacMethods['multiplier_conjugue'](self, *args) - - def multiplier_conjugue_complexe(self, *args): - r'''From Giac's documentation: - Help for multiplier_conjugue_complexe: - multiplier_conjugue_complexe(Expr) - Returns the expression after multiplication by the complex conjugate of the denominator (or of the numerator if no denominator). - See also: 1/ mult_conjugate - Ex1:multiplier_conjugue_complexe(1/(3+i*2)) - Ex2:multiplier_conjugue_complexe(3+i*2) - ''' - return GiacMethods['multiplier_conjugue_complexe'](self, *args) - - def multiply(self, *args): - r'''From Giac's documentation: - Help for multiply: - multiply(Intg or Lst, Intg or Lst) - Returns the product of the 2 arguments. - See also: 1/ * - Ex1:multiply(41,-4) - Ex2:multiply([4,1],[-4,2]) - Ex3:multiply([[4,1],[-4,1]],[[4,1],[-4,1]]) - ''' - return GiacMethods['multiply'](self, *args) - - def mupad2maple(self, *args): - r'''From Giac's documentation: - Help for mupad2maple: - mupad2maple(Str("NameMupadFile"),Str("NameMapleFile")) - mupad2maple("file1","file2") translates file1(MuPAD) to file2(Maple). - See also: 1/ mupad2xcas - ''' - return GiacMethods['mupad2maple'](self, *args) - - def mupad2xcas(self, *args): - r'''From Giac's documentation: - Help for mupad2xcas: - mupad2xcas(Str("NameMupadFile"),Str("NameXcasFile")) - mupad2xcas("file1","file2") translates file1(MuPAD) to file2(Xcas). - See also: 1/ mupad2maple - ''' - return GiacMethods['mupad2xcas'](self, *args) - - def mycielski(self, *args): - r'''From Giac's documentation: - Help for mycielski: - mycielski(Graph(G)) - Returns the Mycielskian of undirected graph G. - See also: 1/ chromatic_number 2/ number_of_triangles - Ex1:mycielski(graph("petersen")) - Ex2: is_isomorphic(mycielski(mycielski(path_graph(2))),graph("grotzsch")) - ''' - return GiacMethods['mycielski'](self, *args) - - def nCr(self, *args): - r'''From Giac's documentation: - Help for nCr: - nCr(Intg(n),Intg(r)) - comb(n,r)=number of combinations of r objects taken among n : n!/(r!(n-r)!) (If n<0 comb(n,r)=n(n-1)..(n-r+1)/r!). - See also: 1/ factorial 2/ perm - Ex1:nCr(4,2) - ''' - return GiacMethods['nCr'](self, *args) - - def nDeriv(self, *args): - r'''From Giac's documentation: - Help for nDeriv: - nDeriv(Expr(Xpr),Var(Var),[Real(h)]) - Returns an approximation of the derivative number at a point: (Xpr(var+h)-Xpr(var-h))/(2*h) (by default h=0.001). - See also: 1/ avgRC - Ex1:nDeriv(f(x),x,h) - Ex2:nDeriv(x^2,x,0.1) - Ex3:nDeriv(x^2,x) - ''' - return GiacMethods['nDeriv'](self, *args) - - def nInt(self, *args): - r'''From Giac's documentation: - Help for nInt: - nInt(Expr(f(x)),Var(x),Real(a),Real(b)) - Returns the approximate value of integrate(f(x),x,a,b) by Romberg's method. - See also: 1/ integrate 2/ gaussquad - Ex1:nInt(exp(x^2),x,0,1) - Ex2:nInt(x^2,x,0,1) - Ex3:nInt(exp(-x^2),x,-1,1) - ''' - return GiacMethods['nInt'](self, *args) - - def nPr(self, *args): - r'''From Giac's documentation: - Help for nPr: - nPr(Intg(n),Intg(p)) - perm(n,p)=number of arrangements of p objects taken among n : n!/(n-p)! - See also: 1/ comb 2/ factorial - Ex1:nPr(4,2) - ''' - return GiacMethods['nPr'](self, *args) - - def nSolve(self, *args): - r'''From Giac's documentation: - Help for nSolve: - nSolve(Expr,Var,[Guess or Interval],[Method]) - Numerical solution of an equation or a system of equations. - See also: 1/ solve 2/ fsolve 3/ csolve - Ex1:nSolve(cos(x)=x,x) - Ex2:nSolve(cos(x)=x,x=1.3) - ''' - return GiacMethods['nSolve'](self, *args) - - def ncols(self, *args): - r'''From Giac's documentation: - Help for ncols: - ncols(Mtrx) - Number of columns of a matrix. - See also: 1/ rowdim - Ex1:ncols([[1,2,3],[4,5,6]]) - Ex2:ncols([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['ncols'](self, *args) - - def negbinomial(self, *args): - r'''From Giac's documentation: - Help for negbinomial: - negbinomial(Intg(n),Intg(k),Real(p in 0..1)) - Returns comb(n+k-1,k)*p^k*(1-p)^n. - See also: 1/ negbinomial_cdf 2/ negbinomial_icdf 3/ binomial - Ex1:negbinomial(4,0,0.5) - Ex2:negbinomial(4,2,0.6) - Ex3:negbinomial(4,6,0.3) - ''' - return GiacMethods['negbinomial'](self, *args) - - def negbinomial_cdf(self, *args): - r'''From Giac's documentation: - Help for negbinomial_cdf: - negbinomial_cdf(Intg(n),Real(p),Real(x),[Real(y)]) - Returns Proba(X<=x) or Proba(x<=X<=y) when X follows the negbinomial(n,p) law. - See also: 1/ negbinomial 2/ negbinomial_icdf - Ex1:negbinomial_cdf(4,0.5,2) - Ex2:negbinomial_cdf(4,0.1,2) - Ex3:negbinomial_cdf(4,0.5,2,3) - ''' - return GiacMethods['negbinomial_cdf'](self, *args) - - def negbinomial_icdf(self, *args): - r'''From Giac's documentation: - Help for negbinomial_icdf: - negbinomial_icdf(Intg(n),Real(p),Real(t)) - Returns h such as Proba(X<=h)=t when X follows the negbinomial(n,p) law. - See also: 1/ negbinomial 2/ negbinomial_cdf - Ex1:negbinomial_icdf(4,0.5,0.68) - Ex2:negbinomial_icdf(4,0.1,0.95) - ''' - return GiacMethods['negbinomial_icdf'](self, *args) - - def neighbors(self, *args): - r'''From Giac's documentation: - Help for neighbors: - neighbors(Graph(G),[Vrtx(v)]) - Returns the list of vertices adjacent to vertex v of G. If v is omitted, a list of adjacency lists of all vertices in G is returned. - See also: 1/ adjacency_matrix 2/ vertex_degree 3/ in_degree 3/ out_degree - Ex1:neighbors(digraph(trail(1,2,3,4,5,6,4,7,8,2)),4) - ''' - return GiacMethods['neighbors'](self, *args) - - def network_transitivity(self, *args): - r'''From Giac's documentation: - Help for network_transitivity: - network_transitivity(Graph(G)) - Returns the transitivity (also called triangle density or global clustering coefficient) of G. - See also: 1/ clustering_coefficient 2/ number_of_triangles - Ex1:network_transitivity(graph(%{[1,2],[2,3],[2,4],[3,4],[4,1]%})) - ''' - return GiacMethods['network_transitivity'](self, *args) - - def newList(self, *args): - r'''From Giac's documentation: - Help for newList: - newList(Intg(n)) - Returns the list made with n zeros. - See also: 1/ newMat 2/ makelist - Ex1:newList(4) - ''' - return GiacMethods['newList'](self, *args) - - def newMat(self, *args): - r'''From Giac's documentation: - Help for newMat: - newMat(Intg(n),Intg(p)) - Returns the list with n rows and p columns, made with zeros. - See also: 1/ newList 2/ makemat - Ex1:newMat(2,3) - ''' - return GiacMethods['newMat'](self, *args) - - def newton(self, *args): - r'''From Giac's documentation: - Help for newton: - newton(Expr(f(x)),Var(x),[ApproxVal(a),NumIter(p)]) - newton(f(x),x,a,p)=one root of f(x) by Newton method beginning with a and p iterations (by default p=20). - See also: 1/ rootof - Ex1:newton(x^2-2,x) - Ex2:newton(x^2-2,x,2) - Ex3:newton(x^2-2,x,-2) - Ex4:newton(x^2-2,x,2,5,1e-7) - ''' - return GiacMethods['newton'](self, *args) - - def newton_solver(self, *args): - r'''From Giac's documentation: - Help for newton_solver: - newton_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['newton_solver'](self, *args) - - def newtonj_solver(self, *args): - r'''From Giac's documentation: - Help for newtonj_solver: - newtonj_solver(Opt) - Argument for fsolve giving the method for solving a system of numerical equations. - See also: 1/ fsolve - Ex1: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],dnewton_solver) - Ex2: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrid_solver) - Ex3: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybrids_solver) - Ex4: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridj_solver) - Ex5: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],hybridsj_solver) - Ex6: fsolve([x^2+y-2,x+y^2-2],[x,y],[2,2],newtonj_solver) - ''' - return GiacMethods['newtonj_solver'](self, *args) - - def nextperm(self, *args): - r'''From Giac's documentation: - Help for nextperm: - nextperm(Intg(n)) - Returns the next permutation with the lexicographic order. - See also: 1/ prevperm 2/ is_permu - Ex1:nextperm([0,2,1,3]) - Ex2:nextperm([0,3,2,1]) - ''' - return GiacMethods['nextperm'](self, *args) - - def nextprime(self, *args): - r'''From Giac's documentation: - Help for nextprime: - nextprime(Intg(a)) - Next prime or pseudo-prime after a given integer. - See also: 1/ prevprime 2/ is_prime 3/ ithprime - Ex1:nextprime(9856989898990) - Ex2:nextprime(97160249868928888261606009) - ''' - return GiacMethods['nextprime'](self, *args) - - def nlpsolve(self, *args): - r'''From Giac's documentation: - Help for nlpsolve: - nlpsolve(objective, [constr], [bd], [opts]) - Solves a nonlinear programming problem of optimizing the objective obj under constraints constr (list of equations and/or inequations) and within bounds bd (sequence of x=a..b) with optional options (for example setting an initial point). - See also: 1/ lpsolve 2/ fsolve 3/ fMin 4/ fMax - Ex1:nlpsolve((x1-10)^3+(x2-20)^3,[(x1-5)^2+(x2-5)^2>=100,(x2-5)^2+(x1-6)^2<=82.81],nlp_initialpoint=[x1=20.1,x2=5.84]) - Ex2:nlpsolve(sin(x1+x2)+(x1-x2)^2-1.5x1+2.5x2+1,x1=-1.5..4,x2=-3..3) - Ex3:nlpsolve(ln(1+x1^2)-x2,[(1+x1^2)^2+x2^2=4]) - Ex4:nlpsolve(x1,[x2>=exp(x1),x3>=exp(x2)],maximize=true,x1=0..100,x2=0..100,x3=0..10,nlp_initialpoint=[x1=1,x2=1.05,x3=2.9]) - Ex5:nlpsolve(-x1*x2*x3,[72-x1-2x2-2x3>=0],x1=0..20,x2=0..11,x3=0..42) - Ex6:nlpsolve(2-1/120*x1*x2*x3*x4*x5,[x1<=1,x2<=2,x3<=3,x4<=4,x5<=5],assume=nlp_nonnegative) - Ex7:nlpsolve(sin(x)/x,x=1..30) - Ex8:nlpsolve(x^3+2x*y-2y^2,x=-10..10,y=-10..10,nlp_initialpoint=[x=3,y=4],maximize) - Ex9:nlpsolve(w^3*(v-w)^2+(w-x-1)^2+(x-y-2)^2+(y-z-3)^2,[w+x+y+z<=5,3z+2v=3],assume=nlp_nonnegative) - Ex10:nlpsolve(sin(x)*Psi(x),x=1..20,nlp_initialpoint=[x=16]) - ''' - return GiacMethods['nlpsolve'](self, *args) - - def nodisp(self, *args): - r'''From Giac's documentation: - Help for nodisp: - nodisp(Expr) - Displays Done in place of a value. - Ex1:nodisp(A:=ranm(50,50)) - ''' - return GiacMethods['nodisp'](self, *args) - - def non_recursive_normal(self, *args): - r'''From Giac's documentation: - Help for non_recursive_normal: - non_recursive_normal(Expr) - Simplifies the expressions, but without simplification inside of non-rational expressions. - See also: 1/ normal - Ex1:non_recursive_normal(sin(x+x)+sin(2*x)+x+x) - Ex2:non_recursive_normal(sin(2*x)+sin(2*x)+x+x) - ''' - return GiacMethods['non_recursive_normal'](self, *args) - - def nop(self, *args): - r'''From Giac's documentation: - Help for nop: - nop(NULL) - No OPeration instruction. - See also: 1/ - Ex1:nop() - ''' - return GiacMethods['nop'](self, *args) - - def nops(self, *args): - r'''From Giac's documentation: - Help for nops: - nops(Lst or Str or Seq) - Returns the size of a list, a string or a sequence. - See also: 1/ sizes 2/ dim 3/ degree - Ex1:nops([1,2,3]) - Ex2:nops("bonjour") - Ex3:nops(1,2,3) - ''' - return GiacMethods['nops'](self, *args) - - def norm(self, *args): - r'''From Giac's documentation: - Help for norm: - norm(Vect or Mtrx) - Returns the l2 norm of a vector = sqrt(x1^2+x2^2+...xn^2) or matrix norm induced by l2 norm. - See also: 1/ maxnorm 2/ l1norm - Ex1:norm([1,2]) - Ex2:norm([1,2,3,-4]) - Ex3:norm([[1,2],[3,-4]]) - Ex4:norm([[1,2,3],[3,-9,6],[4,5,6]]) - ''' - return GiacMethods['norm'](self, *args) - - def normal(self, *args): - r'''From Giac's documentation: - Help for normal: - normal(Expr) - Simplifies the expression. - See also: 1/ simplify - Ex1:normal(2*x+y=1) - Ex2:normal(2*x*2) - Ex3:normal((2*x+1)^2) - ''' - return GiacMethods['normal'](self, *args) - - def normal_cdf(self, *args): - r'''From Giac's documentation: - Help for normal_cdf: - normal_cdf(Real(mu),Real(sigma),Real(x0),[Real(y0)]) - Returns the probability that a Normal random variable is less than x0 or between x0 and y0 (mu is the mean and sigma the standard deviation). - See also: 1/ UTPN 2/ normal_icdf 3/ normald - Ex1:normal_cdf(1.96) - Ex2:normal_cdf(1,2,2.96*sqrt(2)) - Ex3:normal_cdf(1,2,1.4*sqrt(2),2.96*sqrt(2)) - ''' - return GiacMethods['normal_cdf'](self, *args) - - def normal_icdf(self, *args): - r'''From Giac's documentation: - Help for normal_icdf: - normal_icdf(Real(mu),Real(sigma),Real(p)) - Returns h such as the probability that a Normal random variable is less than h is p (mu is the mean and sigma the standard deviation and 0<=p<=1). - See also: 1/ normal_cdf 2/ normald - Ex1:normal_icdf(0.95) - Ex2:normal_icdf(1,2,0.95) - ''' - return GiacMethods['normal_icdf'](self, *args) - - def normald(self, *args): - r'''From Giac's documentation: - Help for normald: - normald(Real(mu),Real(sigma),Real(x0)) - Returns the probability density of the Normal law (mu is the mean and sigma the standard deviation). - See also: 1/ normal_cdf 2/ normal_icdf 3/ randvector 4/ ranm - Ex1:normald(1) - Ex2:normald(1,2,3.5) - Ex3: randvector(3,normald,1,0.5) - Ex4: ranm(4,3,normald,1,0.5) - ''' - return GiacMethods['normald'](self, *args) - - def normald_cdf(self, *args): - r'''From Giac's documentation: - Help for normald_cdf: - normald_cdf(Real(mu),Real(sigma),Real(x0),[Real(y0)]) - Returns the probability that a Normal random variable is less than x0 or between x0 and y0 (mu is the mean and sigma the standard deviation). - See also: 1/ UTPN 2/ normal_icdf 3/ normald - Ex1:normald_cdf(1.96) - Ex2:normald_cdf(1,2,2.96*sqrt(2)) - Ex3:normald_cdf(1,2,1.4*sqrt(2),2.96*sqrt(2)) - ''' - return GiacMethods['normald_cdf'](self, *args) - - def normald_icdf(self, *args): - r'''From Giac's documentation: - Help for normald_icdf: - normald_icdf(Real(mu),Real(sigma),Real(p)) - Returns h such as the probability that a Normal random variable is less than h is p (mu is the mean and sigma the standard deviation and 0<=p<=1). - See also: 1/ normal_cdf 2/ normald - Ex1:normald_icdf(0.95) - Ex2:normald_icdf(1,2,0.95) - ''' - return GiacMethods['normald_icdf'](self, *args) - - def normalize(self, *args): - r'''From Giac's documentation: - Help for normalize: - normalize(Lst||Cplx) - Returns the vector divided by its l2norm. It is also an option for plotfield. - See also: 1/ l2norm - Ex1:normalize(3+4*i) - Ex2:normalize([3,4]) - Ex3: fieldplot(-t*y,[t,y],normalize) - Ex4: fieldplot(-t*y,[t,y],normalize,xstep=0.5,ystep=0.5) - ''' - return GiacMethods['normalize'](self, *args) - - def normalt(self, *args): - r'''From Giac's documentation: - Help for normalt: - normalt(Lst,Real,[Real],Fnc,[Real]) - Z-Test/normal law: arg1=[success,trial] or [mean,sample size] or data, arg2=proportion or data, arg3 optional if data=sigma, arg4 alternative '!=' or '>' or '<', arg5 optional alpha confidence level. - See also: 1/ studentt 2/ chisquaret 3/ kolmogorovt - Ex1:normalt([10,30],.5,.02,'!=',0.1) - Ex2:normalt([0.48,50],0.5,0.1,'<') - ''' - return GiacMethods['normalt'](self, *args) - - def normalvariate(self, *args): - r'''From Giac's documentation: - Help for normalvariate: - normalvariate(Real(mu),Real(sigma)) - Returns a random real with normal distribution N(mu,sigma). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randpoisson 8/ randgeometric 9/ randmultinomial - Ex1:normalvariate(0,1) - Ex2:normalvariate(2,1) - ''' - return GiacMethods['normalvariate'](self, *args) - - def nprimes(self, *args): - r'''From Giac's documentation: - Help for nprimes: - nprimes(Intg(n)) - Counts the number of primes less than n. - See also: 1/ ithprime 2/ prevprime 3/ nextprime 4/ isprime - Ex1:nprimes(20) - ''' - return GiacMethods['nprimes'](self, *args) - - def nrows(self, *args): - r'''From Giac's documentation: - Help for nrows: - nrows(Mtrx) - Number of rows of a matrix. - See also: 1/ ncols - Ex1:nrows([[1,2,3],[4,5,6]]) - Ex2:nrows([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['nrows'](self, *args) - - def nuage_points(self, *args): - r'''From Giac's documentation: - Help for nuage_points: - nuage_points(Mtrx) - Draws for k=0..nrows, the points (xk,yk) where xk=element row k column 0 and yk=element row k column j (j=1..ncols). - See also: 1/ polygonplot 2/ polygonscatterplot 3/ listplot - Ex1:nuage_points([[1,2,3],[2,0,1],[-1,2,3]]) - ''' - return GiacMethods['nuage_points'](self, *args) - - def nullspace(self, *args): - r'''From Giac's documentation: - Help for nullspace: - nullspace(Mtrx) - Kernel of a linear map with matrix M. - See also: 1/ image 2/ rref 3/ Nullspace - Ex1:nullspace([[1,2],[3,6]]) - Ex2:nullspace([[1,2,3],[1,3,6],[2,5,9]]) - ''' - return GiacMethods['nullspace'](self, *args) - - def number_of_edges(self, *args): - r'''From Giac's documentation: - Help for number_of_edges: - number_of_edges(Graph(G)) - Returns the number of edges/arcs of G. - See also: 1/ edges 2/ number_of_vertices - Ex1:number_of_edges(complete_graph(5)) - ''' - return GiacMethods['number_of_edges'](self, *args) - - def number_of_spanning_trees(self, *args): - r'''From Giac's documentation: - Help for number_of_spanning_trees: - number_of_spanning_trees(Graph(G)) - Returns the number of spanning trees in undirected graph G. - See also: 1/ spanning_tree - Ex1:number_of_spanning_trees(complete_graph(4)) - Ex2:number_of_spanning_trees(graph(trail(1,2,3,4,1,3))) - ''' - return GiacMethods['number_of_spanning_trees'](self, *args) - - def number_of_triangles(self, *args): - r'''From Giac's documentation: - Help for number_of_triangles: - number_of_triangles(Graph(G)) - Returns the number of 3-cliques if G is undirected resp. the number of directed cycles on 3 vertices if G is directed. - See also: 1/ is_clique 2/ maximal_clique - Ex1:number_of_triangles(graph("tetrahedron")) - ''' - return GiacMethods['number_of_triangles'](self, *args) - - def number_of_vertices(self, *args): - r'''From Giac's documentation: - Help for number_of_vertices: - number_of_vertices(Graph(G)) - Returns the number of vertices of G. - See also: 1/ graph_vertices 2/ number_of_edges - Ex1:number_of_vertices(graph("petersen")) - ''' - return GiacMethods['number_of_vertices'](self, *args) - - def numer(self, *args): - r'''From Giac's documentation: - Help for numer: - numer(Frac(a/b) or RatFrac) - Returns the numerator of the simplified fraction. - See also: 1/ getNum 2/ getDenom 3/ denom 4/ f2nd - Ex1:numer(25/15) - Ex2:numer((x^3-1)/(x^2-1)) - Ex3:numer(1+(x^3-1)/x^2) - ''' - return GiacMethods['numer'](self, *args) - - def octahedron(self, *args): - r'''From Giac's documentation: - Help for octahedron: - octahedron(Pnt(A),Pnt(B),Pnt(C)) - Draws an octahedron with center A, vertex B and such that the plane ABC contains 4 vertices. - See also: 1/ icosahedron 2/ dodecahedron 3/ cube 4/ tetrahedron - Ex1:octahedron([0,0,0],[0,0,5],[0,5,0]) - Ex2:octahedron(evalf([0,0,0],[3,2,4],[1,1,0])) - ''' - return GiacMethods['octahedron'](self, *args) - - def odd(self, *args): - r'''From Giac's documentation: - Help for odd: - odd(Intg(n)) - Returns 1 if the integer is odd, else returns 0. - See also: 1/ even - Ex1:odd(6) - Ex2:odd(1251) - ''' - return GiacMethods['odd'](self, *args) - - def odd_girth(self, *args): - r'''From Giac's documentation: - Help for odd_girth: - odd_girth(Graph(G)) - Returns the length of the shortest odd cycle in the undirected unweighted graph G. - See also: 1/ girth - Ex1:odd_girth(graph("petersen")) - Ex2:odd_girth(hypercube_graph(3)) - ''' - return GiacMethods['odd_girth'](self, *args) - - def odd_graph(self, *args): - r'''From Giac's documentation: - Help for odd_graph: - odd_graph(Intg(n)) - Returns the odd graph of order n as Kneser graph K(2n-1,n-1), where n<=8. - See also: 1/ kneser_graph - Ex1:odd_graph(3) - ''' - return GiacMethods['odd_graph'](self, *args) - - def odeplot(self, *args): - r'''From Giac's documentation: - Help for odeplot: - odeplot(Expr,VectVar,VectInitCond) - odeplot(f(t,y),[t,y],[t0,y0]) draws the solution of y'=f(t,y) and y(t0)=y0 or of the system [x'=g(t,x,y),y'=h(t,x,y)] with x(t0)=x0 and y(t0)=y0. - See also: 1/ interactive_plotode 2/ fieldplot 3/ odesolve 4/ desolve - Ex1:odeplot(sin(t*y),[t,y],[0,1]) - Ex2:odeplot(sin(t*y),[t=-10..10,y],[0,1]) - Ex3:odeplot(sin(t*y),[t=-3..3,y],[0,1],tstep=0.1,color=vert) - Ex4:odeplot([x-0.3*x*y, 0.3*x*y-y], [t,x,y],[0,0.3,0.7]) - Ex5:odeplot([x-0.3*x*y, 0.3*x*y-y], [t,x,y],[0,0.3,0.7],plan) - Ex6:odeplot([-y+b,-1+(x-a)^2+(y-b)^2],[t=-3..3,x,y],[0,a+1,b+0.5],plan) - Ex7:odeplot(5*[-y,x],[t=0..1,x,y],[0,0.3,0.7],tstep=0.05,plan) - ''' - return GiacMethods['odeplot'](self, *args) - - def odesolve(self, *args): - r'''From Giac's documentation: - Help for odesolve: - odesolve(Expr,VectVar,VectInitCond,FinalVal,[tstep=Val,curve]) - odesolve(f(t,y),[t,y],[t0,y0],t1)=odesolve(t0..t1,f,y0)=y(t1) for y approx sol of y'=f(t,y) and y(t0)=y0 with y=vector for systems. - See also: 1/ plotode 2/ plotfield 3/ interactive_plotode 4/ desolve - Ex1:odesolve(sin(t*y),[t,y],[0,1],2) - Ex2:odesolve(0..2,(t,y)->sin(t*y),1) - Ex3:odesolve(0..pi,(t,v)->{[-v[1],v[0]]},[0,1]) - Ex4:odesolve(sin(t*y),t=0..2,y,1,tstep=0.5) - Ex5:odesolve(sin(t*y),t=0..2,y,1,tstep=0.5,curve) - ''' - return GiacMethods['odesolve'](self, *args) - - def op(self, *args): - r'''From Giac's documentation: - Help for op: - op(Op or Fnc) - Returns the arguments of an operator as a sequence. - See also: 1/ sommet 2/ quote 3/ makesuite - Ex1:op(quote(gcd(45,126))) - Ex2:op('gcd(45,126)') - Ex3:op('1+2')[1] - Ex4:op([1,2,3]) - Ex5:op(set[1,2,3]) - ''' - return GiacMethods['op'](self, *args) - - def open_polygon(self, *args): - r'''From Giac's documentation: - Help for open_polygon: - open_polygon(LstPnt||LstCplx) - Returns and draws the polygonal line where its vertices are the element of l. - See also: 1/ isopolygon 2/ quadrilateral - Ex1:open_polygon(i,1+i,2-i,-1,-1+i/2) - Ex2:open_polygon(point(0,0,0),point(3,3,3),point(0,0,3),point(3,0,0)) - ''' - return GiacMethods['open_polygon'](self, *args) - - def ord(self, *args): - r'''From Giac's documentation: - Help for ord: - ord(Char||LstChar) - Returns the ASCII code of a character or of the first character of a string. - See also: 1/ asc 2/ char - Ex1:ord("A") - Ex2:ord("ABC") - Ex3:ord(["a","b","c"]) - ''' - return GiacMethods['ord'](self, *args) - - def order(self, *args): - r'''From Giac's documentation: - Help for order: - order(g) - Returns element order of g in (Z/nZ)^* or in a finite field. - Ex1:order(3 % 7) - Ex2: GF(3,5,g); order(g^2+g+1); - ''' - return GiacMethods['order'](self, *args) - - def order_size(self, *args): - r'''From Giac's documentation: - Help for order_size: - order_size(Expr) - Remainder (O term) of a series expansion: limit(x^a*order_size(x),x=0)=0 if a>0. - See also: 1/ series - Ex1:order_size(x) - Ex2: limit(sqrt(x)*order_size(x),x=0) - ''' - return GiacMethods['order_size'](self, *args) - - def ordinate(self, *args): - r'''From Giac's documentation: - Help for ordinate: - ordinate(Pnt or Vect) - Returns the ordinate of a point or a vector. - See also: 1/ abscissa 2/ affix 3/ cote 4/ coordinates - Ex1:ordinate(point(1+2*i)) - Ex2:ordinate(point(i)-point(1+2*i)) - Ex3:ordinate(-1-i) - Ex4:ordinate(point(1,2,3)) - ''' - return GiacMethods['ordinate'](self, *args) - - def orthocenter(self, *args): - r'''From Giac's documentation: - Help for orthocenter: - orthocenter((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - Shows the orthocenter of a triangle or of the triangle made with 3 points. - See also: 1/ altitude 2/ triangle - Ex1:orthocenter(1+i,2,i) - Ex2:orthocenter(point(1+i),point(2),point(i)) - Ex3:orthocenter(triangle(0,1,1+i)) - ''' - return GiacMethods['orthocenter'](self, *args) - - def orthogonal(self, *args): - r'''From Giac's documentation: - Help for orthogonal: - orthogonal((Pnt),(Line or Plan)) - orthogonal(A,line(B,C)) draws the orthogonal plane of line BC through A and orthogonal(A,plane(B,C,D)) draws the orthogonal line of plane(B,C,D) through A. - See also: 1/ altitude 2/ perpendicular - Ex1:orthogonal(point(0,0,0),line(point(1,0,0),point(0,1,0))) - Ex2:orthogonal(point(0,0,0),plane(point(1,0,0),point(0,1,0),point(0,0,1))) - ''' - return GiacMethods['orthogonal'](self, *args) - - def osculating_circle(self, *args): - r'''From Giac's documentation: - Help for osculating_circle: - osculating_circle(Curve,Point) - Osculating circle at point M to the curve C. - See also: 1/ curvature 2/ evolute - Ex1:osculating_circle(plot(x^2),point(1,1)) - Ex2:osculating_circle([5*cos(t),5*sin(t)],t,0) - Ex3:osculating_circle([t,t^2],t) - Ex4:osculating_circle([t,t^2],t,1) - Ex5:osculating_circle([3*exp(t/2)*cos(t),3*exp(t/2)*sin(t)],t) - Ex6:osculating_circle([3*exp(t/2)*cos(t),3*exp(t/2)*sin(t)],t,7) - ''' - return GiacMethods['osculating_circle'](self, *args) - - def p1oc2(self, *args): - r'''From Giac's documentation: - Help for p1oc2: - p1oc2(Permut,Cycle) - Returns the permutation product of p1 and c2. - See also: 1/ c1op2 2/ p1op2 - Ex1:p1oc2([0,2,1],[2,1,3]) - ''' - return GiacMethods['p1oc2'](self, *args) - - def p1op2(self, *args): - r'''From Giac's documentation: - Help for p1op2: - p1op2(Permut,Permut) - Returns the permutation product of p1 and p2. - See also: 1/ c1op2 2/ p1oc2 - Ex1:p1op2([0,2,1],[1,0,3,2]) - ''' - return GiacMethods['p1op2'](self, *args) - - def pa2b2(self, *args): - r'''From Giac's documentation: - Help for pa2b2: - pa2b2(Intg(n)) - Returns [a,b] such that a^2+b^2=n (for n prime and n=1 (mod 4)). - Ex1:pa2b2(17) - Ex2:pa2b2(209) - Ex3:pa2b2(229) - ''' - return GiacMethods['pa2b2'](self, *args) - - def pade(self, *args): - r'''From Giac's documentation: - Help for pade: - pade(Expr(Xpr), Var(x), (Intg(n) || Poly(N)), Intg(p)) - Pade approximation P/Q=Xpr mod x^(n+1) or mod N with degree(P)3,x^20 (by default a=1) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ bartlett_hann_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(poisson_window(randvector(1000,0..1),0.5)) - ''' - return GiacMethods['poisson_window'](self, *args) - - def polar(self, *args): - r'''From Giac's documentation: - Help for polar: - polar(Crcle,Pnt or Cplxe(A)) - Returns the line of the conjugated points of A with respect to the circle. - See also: 1/ pole 2/ is_conjugate - Ex1:polar(circle(0,1),point(1+i)/2) - Ex2:polar(circle(0,1),point(1+i)) - ''' - return GiacMethods['polar'](self, *args) - - def polar_coordinates(self, *args): - r'''From Giac's documentation: - Help for polar_coordinates: - polar_coordinates(Pnt or Cplx or LstRectCoord) - Returns the list of the norm and of the argument of the affix of a point (for 2D) or of a complex number or of the list of rectangular coordinates. - See also: 1/ abscissapoint 2/ ordinate 3/ rectangular_coordinates 4/ polar_point - Ex1:polar_coordinates(point(1+2*i)) - Ex2:polar_coordinates(-1-i) - Ex3:polar_coordinates([-1,2]) - Ex4:polar_coordinates(point(1+2*i)-point(-1+i)) - ''' - return GiacMethods['polar_coordinates'](self, *args) - - def polar_point(self, *args): - r'''From Giac's documentation: - Help for polar_point: - polar_point(Real(r),Real(t)) - Returns the point (for 2D) with the arguments r and t as polar coordinates (i.e. with r*exp(i*t) as affix). - See also: 1/ abscissa 2/ ordinate 3/ polar_coordinates 4/ rectangular_coordinates 5/ point - Ex1:polar_point(1,pi/4) - Ex2:polar_point(2,-pi/3) - ''' - return GiacMethods['polar_point'](self, *args) - - def polarplot(self, *args): - r'''From Giac's documentation: - Help for polarplot: - polarplot(Expr,Var,VarMin,VarMax) - plotpolar(f(x),x,a,b) draws the polar curve r=f(x) for x in [a,b]. - See also: 1/ plotparam 2/ plotfunc 3/ plotpolar - Ex1:polarplot(sin(2*x),x,0,pi) - Ex2:polarplot(sin(2*x),x,0,pi,tstep=0.1) - ''' - return GiacMethods['polarplot'](self, *args) - - def pole(self, *args): - r'''From Giac's documentation: - Help for pole: - pole(Crcle,Line) - Returns the point having the line as polar with respect to the circle . - See also: 1/ polar 2/ is_conjugate - Ex1:pole(circle(0,1),line(i,1)) - Ex2:pole(circle(0,1),line((1+i),2)) - ''' - return GiacMethods['pole'](self, *args) - - def poly2symb(self, *args): - r'''From Giac's documentation: - Help for poly2symb: - poly2symb(Lst,Var) - Gives the polynomial (or its value) : the first argument is the vector of coefficients and the second argument is the variable (by default x). - See also: 1/ e2r 2/ symb2poly - Ex1:poly2symb([1,2,3]) - Ex2:poly2symb([1,2,3],x) - Ex3:poly2symb([1,2,3],-1) - Ex4:poly2symb([1,2,-1],y) - ''' - return GiacMethods['poly2symb'](self, *args) - - def polyEval(self, *args): - r'''From Giac's documentation: - Help for polyEval: - polyEval(Vect,Real(x0)) - Evaluates at a point x0, a polynomial given by its coefficients. - See also: 1/ proot 2/ pcoeff - Ex1:polyEval([1,0,-2],1) - Ex2:polyEval([1,2,-25,-26,120],8) - ''' - return GiacMethods['polyEval'](self, *args) - - def polygon(self, *args): - r'''From Giac's documentation: - Help for polygon: - polygon(LstPnt||LstCplx) - Returns and draws the polygon where its vertices are the elements of l. - See also: 1/ isopolygon 2/ quadrilateral 3/ convexhull 4/ hexagon - Ex1:polygon(i,1+i,2-i,-1,-1+i/2) - Ex2:polygon(point(0,0,0),point(3,3,3),point(0,0,3),point(3,0,0)) - ''' - return GiacMethods['polygon'](self, *args) - - def polygone_rempli(self, *args): - r'''From Giac's documentation: - Help for polygone_rempli: - polygone_rempli(Intg(n)) - The argument is an integer <-1 which gives the number of previous turtle positions drawing a polygon and created this full polygon. - See also: 1/ - Ex1: repete(4,avance 40,tourne_droite);polygone_rempli -8 - Ex2: repete(3,avance 40,tourne_droite 120);polygone_rempli -6 - Ex3: repete(3,avance 40,avance 40,tourne_droite 120);polygone_rempli -9 - ''' - return GiacMethods['polygone_rempli'](self, *args) - - def polygonplot(self, *args): - r'''From Giac's documentation: - Help for polygonplot: - polygonplot(Mtrx) - Draws the polygons joining for j fixed and for k=0..nrows, the points (xk,yk) where xk=element row k column 0 and yk=element row k column j, after the xk are sorted (we obtain ncols-1 polygons). - See also: 1/ scatterplot 2/ listplot 3/ polygonscatterplot - Ex1:polygonplot([[1,2,3],[2,0,1],[-1,2,3]]) - ''' - return GiacMethods['polygonplot'](self, *args) - - def polygonscatterplot(self, *args): - r'''From Giac's documentation: - Help for polygonscatterplot: - polygonscatterplot(Mtrx) - Draws the points (xk,yk) and the polygons joining for j fixed and for k=0..nrows, the points (xk,yk) where xk=element row k column 0 et yk=element row k column j, after the xk are sorted (we obtain ncols-1 polygons). - See also: 1/ scatterplot 2/ polygonplot 3/ listplot - Ex1:polygonscatterplot([[1,2,3],[2,0,1],[-1,2,3]]) - ''' - return GiacMethods['polygonscatterplot'](self, *args) - - def polyhedron(self, *args): - r'''From Giac's documentation: - Help for polyhedron: - polyhedron(SeqPnt(A,B,C...)) - Draws a convex polyhedron with vertices among the arguments. - See also: 1/ cube 2/ parallelepiped - Ex1:polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6]) - ''' - return GiacMethods['polyhedron'](self, *args) - - def polynom(self, *args): - r'''From Giac's documentation: - Help for polynom: - polynom(Opt) - Option of the convert or convertir command and of the taylor and series commands (list=>n-poly or series=>poly). - See also: 1/ poly2symb 2/ taylor 3/ series 4/ convert - Ex1: convert([[10,[3,1]],[12,[2,2]]],polynom) - Ex2: convert(taylor(sin(x)),polynom) - Ex3: convert(series(sin(x),x=0,6),polynom) - Ex4: taylor(sin(x),x=0,5,polynom) - Ex5: series(sin(x),x=0,6,,polynom) - ''' - return GiacMethods['polynom'](self, *args) - - def polynomial_regression(self, *args): - r'''From Giac's documentation: - Help for polynomial_regression: - polynomial_regression(Lst||Mtrx(A),[Lst],Intg(n)) - Returns the coefficients (an,...a1,a0) of y=an*x^n+..a1x+a0 : it is the best polynomial which approx the points where the coordinates are the rows of A (or the 2 lists) (n is the 2nd argument). - See also: 1/ linear_regression 2/ power_regression - Ex1:polynomial_regression([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]],3) - Ex2:polynomial_regression([[0.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]],3) - Ex3:polynomial_regression([0.0,2.0,3.0,4.0],[1.0,4.0,9.0,16.0],3) - ''' - return GiacMethods['polynomial_regression'](self, *args) - - def polynomial_regression_plot(self, *args): - r'''From Giac's documentation: - Help for polynomial_regression_plot: - polynomial_regression_plot(Lst||Mtrx(A),[Lst],Intg(n)) - Returns the plot of y=an*x^n+..a1x+a0 : it is the best polynomial which approx the points where the coordinates are the rows of A (or the 2 lists) (n is the 2nd argument). - See also: 1/ linear_regression_plot 2/ power_regression_plot - Ex1:polynomial_regression_plot([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]],3) - Ex2:polynomial_regression_plot([[0.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]],3) - Ex3:polynomial_regression_plot([0.0,2.0,3.0,4.0],[1.0,4.0,9.0,16.0],3) - ''' - return GiacMethods['polynomial_regression_plot'](self, *args) - - def position(self, *args): - r'''From Giac's documentation: - Help for position: - position(NULL or LstCoord) - Returns the turtle position in pixels or puts the turtle at the position given by the argument with the same direction. - See also: 1/ cap 2/ initialise - Ex1:position() - Ex2:position(50,70) - Ex3:position([50,70]) - ''' - return GiacMethods['position'](self, *args) - - def poslbdLMQ(self, *args): - r'''From Giac's documentation: - Help for poslbdLMQ: - poslbdLMQ(Poly(P)) - Returns a lower bound on the values of the positive roots of P. Akritas-Strzebonski-Vigklas' Local Max Quadratic (LMQ) method is used. - See also: 1/ posubLMQ 2/ VAS_positive 3/ realroot - Ex1:poslbdLMQ(x^3-7*x+7) - ''' - return GiacMethods['poslbdLMQ'](self, *args) - - def posubLMQ(self, *args): - r'''From Giac's documentation: - Help for posubLMQ: - posubLMQ(Poly(P)) - Returns an upper bound on the values of the positive roots of P. Akritas-Strzebonski-Vigklas' Local Max Quadratic (LMQ) method is used. - See also: 1/ poslbdLMQ 2/ VAS_positive 3/ realroot - Ex1:posubLMQ(x^3-7*x+7) - ''' - return GiacMethods['posubLMQ'](self, *args) - - def potential(self, *args): - r'''From Giac's documentation: - Help for potential: - potential(Vect(V),VectVar) - Returns U such that derive(U,Vector_of_variable)=V. - See also: 1/ derive 2/ vpotential - Ex1:potential([2*x*y+3,x^2-4*z,-4*y],[x,y,z]) - ''' - return GiacMethods['potential'](self, *args) - - def pow2exp(self, *args): - r'''From Giac's documentation: - Help for pow2exp: - pow2exp(Expr) - Converts powers to exponentials. - See also: 1/ exp2pow - Ex1:pow2exp(a^b) - ''' - return GiacMethods['pow2exp'](self, *args) - - def power_regression(self, *args): - r'''From Giac's documentation: - Help for power_regression: - power_regression(Lst|Mtrx(A),[Lst]) - Returns the coefficients (m,b) of y=b*x^m : it is the best monomial which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ polynomial_regression 2/ linear_regressiont - Ex1:power_regression([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex2:power_regression([[1.0,2.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex3:power_regression([1.0,2.0,3.0,4.0],[2.0,4.0,9.0,16.0]) - ''' - return GiacMethods['power_regression'](self, *args) - - def power_regression_plot(self, *args): - r'''From Giac's documentation: - Help for power_regression_plot: - power_regression_plot(Lst||Mtrx(A),[Lst]) - Returns the plot of y=b*x^m : it is the best monomial which approx the points where the coordinates are the rows of A (or the 2 lists). - See also: 1/ polynomial_regression_plot 2/ linear_regression_plot - Ex1:power_regression_plot([[1.0,1.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex2:power_regression_plot([[1.0,2.0],[2.0,4.0],[3.0,9.0],[4.0,16.0]]) - Ex3:power_regression_plot([1.0,2.0,3.0,4.0],[2.0,4.0,9.0,16.0]) - ''' - return GiacMethods['power_regression_plot'](self, *args) - - def powermod(self, *args): - r'''From Giac's documentation: - Help for powermod: - powermod(Intg(a),Intg(n),Intg(p),[Expr(P(x))],[Var]) - Computes a^n modulo p or modulo p,P(x) (fast algorithm). - See also: 1/ pow 2/ ^ - Ex1:powermod(17,452,19) - Ex2:powermod(x+1,452,19,x^4+x+1,x) - ''' - return GiacMethods['powermod'](self, *args) - - def powerpc(self, *args): - r'''From Giac's documentation: - Help for powerpc: - powerpc(Cercle,Pnt or Cplx) - Returns the real number d^2-R^2 (d=distance between point and center, R=radius). - See also: 1/ radical_axis - Ex1:powerpc(circle(0,1+i),3+i) - Ex2:powerpc(circle(0,point(1+i)),3+i) - ''' - return GiacMethods['powerpc'](self, *args) - - def powexpand(self, *args): - r'''From Giac's documentation: - Help for powexpand: - powexpand(Expr) - Expands the expression as a function of the exponent. - See also: 1/ - Ex1:powexpand(2^(x+y)) - Ex2:powexpand(3^(2*x)) - ''' - return GiacMethods['powexpand'](self, *args) - - def powmod(self, *args): - r'''From Giac's documentation: - Help for powmod: - powmod(Intg(a),Intg(n),Intg(p),[Expr(P(x))],[Var]) - Computes a^n modulo p or modulo p,P(x) (fast algorithm). - See also: 1/ pow 2/ ^ - Ex1:powmod(17,452,19) - Ex2:powmod(x+1,452,19,x^4+x+1,x) - ''' - return GiacMethods['powmod'](self, *args) - - def prepend(self, *args): - r'''From Giac's documentation: - Help for prepend: - prepend(Lst||Set||Str(L),Elem(n)) - Adds an element to a set or at the beginning of a list or of a string (L:=prepend(L,a) or L.prepend(a)). - See also: 1/ append 2/ concat - Ex1:prepend([1,2],3) - Ex2:prepend(set[1,2],3) - Ex3: L:=[1,2];L:=prepend(L,3) - Ex4: L:=[1,2];L.prepend(L,3) - Ex5: S:=set[1,2];S:=prepend(L,3) - Ex6: S:=set[1,2];S.prepend(L,3) - ''' - return GiacMethods['prepend'](self, *args) - - def preval(self, *args): - r'''From Giac's documentation: - Help for preval: - preval(Expr(F(Var)),Real(a),Real(b),[Var]) - Returns F(b)-F(a). - See also: 1/ subst 2/ int - Ex1:preval(x^2-2,2,3) - Ex2:preval(y^2-2,2,3,y) - Ex3:preval(int(x),0,1) - Ex4:preval(int(y,y),0,1,y) - ''' - return GiacMethods['preval'](self, *args) - - def prevperm(self, *args): - r'''From Giac's documentation: - Help for prevperm: - prevperm(Intg(n)) - Returns the previous permutation with the lexicographic order. - See also: 1/ nextperm 2/ is_permu - Ex1:prevperm([0,1,3,2]) - Ex2:prevperm([0,1,2,3]) - ''' - return GiacMethods['prevperm'](self, *args) - - def prevprime(self, *args): - r'''From Giac's documentation: - Help for prevprime: - prevprime(Intg(a)) - Previous prime or pseudo-prime before a given integer a. - See also: 1/ nextprime 2/ is_prime 3/ ithprime - Ex1:prevprime(9856989898999) - Ex2:prevprime(97160249868928888261606009) - ''' - return GiacMethods['prevprime'](self, *args) - - def primpart(self, *args): - r'''From Giac's documentation: - Help for primpart: - primpart(Poly(P),[Var]) - Returns the polynomial P divided by the gcd of its coefficients. - See also: 1/ content - Ex1:primpart(2x^2+10x+6) - Ex2:primpart(2t^2+10t+6,t) - ''' - return GiacMethods['primpart'](self, *args) - - def printf(self, *args): - r'''From Giac's documentation: - Help for printf: - printf(Expr) - 2d printing. - See also: 1/ print - Ex1:printf(sqrt(2)) - Ex2:printf("%gen+%gen=%gen",a,b,a+b) - ''' - return GiacMethods['printf'](self, *args) - - def prism(self, *args): - r'''From Giac's documentation: - Help for prism: - prism(LstPnt([A,B,C,D]),Pnt(A1)) - Draws a prism with plane base ABCD...and with edges parallel to AA1 (the faces are parallelograms). - See also: 1/ cube 2/ polyhedron - Ex1:prism([[0,0,0],[5,0,0],[0,5,0],[-5,5,0]],[0,0,5]) - ''' - return GiacMethods['prism'](self, *args) - - def prism_graph(self, *args): - r'''From Giac's documentation: - Help for prism_graph: - prism_graph(Intg(n)) - Returns the generalized Petersen graph GP(n,1). - See also: 1/ antiprism_graph 2/ web_graph - Ex1:prism_graph(5) - ''' - return GiacMethods['prism_graph'](self, *args) - - def product(self, *args): - r'''From Giac's documentation: - Help for product: - product(Expr||Lst,[Var||Lst],[Intg(a)],[Intg(b)],[Intg(p)]) - Multiplies the values of the expression when the variable go from a to b with a step p (product(expression,var,begin,end,step) by default p=1) or product of the elements of a list or product element by element of 2 lists or matrices. - See also: 1/ sum - Ex1:product(n,n,1,10,2) - Ex2:product(1/n,n,1,10) - Ex3:product(1/n,n,11,1) - Ex4:product(1/n,n,10,1,1) - Ex5:product([2,3,4,5]) - Ex6:product([2,3,4],[5,6,7]) - Ex7:product([[2,3,4],[5,6,7]],[[2,3,4],[5,6,7]]) - ''' - return GiacMethods['product'](self, *args) - - def projection(self, *args): - r'''From Giac's documentation: - Help for projection: - projection(Curve,Pnt) - projection(C,A) is the orthogonal projection of A on the curve C. - See also: 1/ perpendicular - Ex1: H:=projection(line(i,1-i),1+i) - Ex2: K:=projection(circle(0,1),1+i) - Ex3: J:=projection(circle(0,1),point(1+2*i)) - ''' - return GiacMethods['projection'](self, *args) - - def proot(self, *args): - r'''From Giac's documentation: - Help for proot: - proot(Vect||Poly,[Intg(n)]) - Returns all computed roots of a polynomial given by its coefficients (may not work if roots are not simple). - See also: 1/ pcoeff 2/ peval 3/ realroot 4/ complexroot 5/ rationalroot 6/ crationalroot - Ex1:proot([1,0,-2]) - Ex2:proot(x^2-2) - Ex3:proot([1,2,-25,-26,120]) - Ex4:proot(x^4+5x-3,30) - ''' - return GiacMethods['proot'](self, *args) - - def propFrac(self, *args): - r'''From Giac's documentation: - Help for propFrac: - propFrac(Frac or RatFrac) - Simplifies and writes the fraction (or rational fraction) A/B as Q+R/B with R=0 or, the (-n)th previous question if n<0 (by default n=-1 for the previous question). - See also: 1/ ans - Ex1:quest() - Ex2:quest(2) - Ex3:quest(-2) - ''' - return GiacMethods['quest'](self, *args) - - def quo(self, *args): - r'''From Giac's documentation: - Help for quo: - quo((Vect or Poly),(Vect or Poly),[Var]) - Euclidean quotient of 2 polynomials. - See also: 1/ rem 2/ quorem 3/ Quo 4/ iquo - Ex1:quo([1,2,3,4],[-1,2]) - Ex2:quo(x^3+2x^2+3x+4,-x+2) - Ex3:quo(t^3+2t^2+3t+4,-t+2,t) - ''' - return GiacMethods['quo'](self, *args) - - def quorem(self, *args): - r'''From Giac's documentation: - Help for quorem: - quorem((Vect or Poly),(Vect or Poly),[Var]) - Euclidean quotient and remainder of 2 polynomials. - See also: 1/ rem 2/ quo 3/ iquorem - Ex1:quorem([1,2,3,4],[-1,2]) - Ex2:quorem(x^3+2x^2+3x+4,-x+2) - Ex3:quorem(t^3+2t^2+3t+4,-t+2,t) - Ex4:quorem(t^4-1,(t+1)^2,t) - ''' - return GiacMethods['quorem'](self, *args) - - def quote(self, *args): - r'''From Giac's documentation: - Help for quote: - quote(Expr) - Returns its argument unevaluated (and also a:=quote(a) purges a). - See also: 1/ - Ex1:quote(1+2) - Ex2:quote(1/x+1/(x-1)) - Ex3:quote((x+1)*(x-1)) - ''' - return GiacMethods['quote'](self, *args) - - def r2e(self, *args): - r'''From Giac's documentation: - Help for r2e: - r2e(Lst,Var) - Gives the polynomial (or its value) : the first argument is the vector of coefficients and the second argument is the variable (by default x). - See also: 1/ e2r 2/ symb2poly - Ex1:r2e([1,2,3]) - Ex2:r2e([1,2,3],x) - Ex3:r2e([1,2,3],-1) - Ex4:r2e([1,2,-1],y) - ''' - return GiacMethods['r2e'](self, *args) - - def radical_axis(self, *args): - r'''From Giac's documentation: - Help for radical_axis: - radical_axis(Crcle,Crcle) - Returns the line of points with same powerpc with respect to the 2 circles. - See also: 1/ powerpc - Ex1:radical_axis(circle(0,1+i),circle(1,1+i)) - Ex2:radical_axis(circle(0,point(1+i)),circle(1,point(1+i))) - ''' - return GiacMethods['radical_axis'](self, *args) - - def radius(self, *args): - r'''From Giac's documentation: - Help for radius: - radius(Crcle) - radius(C) gives the radius of the circle C. - See also: 1/ center 2/ circle - Ex1:radius(incircle(-1,1-i,i)) - ''' - return GiacMethods['radius'](self, *args) - - def ramene(self, *args): - r'''From Giac's documentation: - Help for ramene: - ramene(Str(fich_name)) - Reads variables and their values from the file fich_name. - See also: 1/ write 2/ readrgb 3/ readwav 4/ csv2gen - Ex1:ramene("toto") - ''' - return GiacMethods['ramene'](self, *args) - - def rand(self, *args): - r'''From Giac's documentation: - Help for rand: - rand(Intg(n) or Interval(p..n) or NULL,[Intg(b1) or Lst(L)],[Intg(b2)]) - rand(n)=a random integer (resp rand(p,n)=a real or rand(p..n)=a real function) with uniform distribution in 0..n-1 (resp in [p;n])(rand()=rand(0,1)=a real in [0,1[) or rand(n,b1,b2)=n integers between b1 and b2 or rand(n,L)=list of n elements of L. - See also: 1/ srand 2/ randpoly 3/ ranm 4/ randvector 5/ hasard - Ex1:rand(4) - Ex2:rand() - Ex3:rand(0,2) - Ex4:rand(0..2)() - Ex5: f:=rand(0..2) - Ex6:rand(3,1,10) - Ex7:rand(3,["r","r","r","b","n"]) - Ex8: L:=["r","r","r","b","n"];L3:=rand(L,3) - Ex9: L:=["r","r","r","b","n"];L3:=L.rand(3) - Ex10: L:=["r","r","r","b","n"];a:=rand(L) - Ex11: L:=["r","r","r","b","n"];a:=L.rand() - ''' - return GiacMethods['rand'](self, *args) - - def randMat(self, *args): - r'''From Giac's documentation: - Help for randMat: - randMat(Intg(n), [Intg(m)],[Interval or quote(DistribLaw)]) - Returns a list of size n or an n*m matrix that contains random integers in the range -99 through 99 with uniform distribution or contains random numbers according to the law put between quotes. - See also: 1/ idn 2/ randPoly 3/ rand 4/ randvector - Ex1:randMat(3) - Ex2:randMat(3,2) - Ex3:randMat(3,2,6) - Ex4:randMat(3,2,binomial,10,0.3) - Ex5:randMat(3,2,multinomial,[1/2,1/3,1/6]) - Ex6:randMat(3,2,poisson,5.2) - Ex7:randMat(3,2,normald,0,1) - Ex8:randMat(3,2,exp,7/2) - Ex9:randMat(3,2,poisson,1.2) - Ex10:randMat(3,2,'rand(3)') - Ex11:randMat(3,2,1..2) - Ex12:randMat(3,5,multinomial,[1/2,1/3,1/6],["R","V","B"]) - Ex13: GF(2,8,g);ranm(3,3,g) - ''' - return GiacMethods['randMat'](self, *args) - - def randNorm(self, *args): - r'''From Giac's documentation: - Help for randNorm: - randNorm(Real(mu),Real(sigma)) - Returns a random real with normal distribution N(mu,sigma). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randpoisson 8/ randgeometric 9/ randmultinomial - Ex1:randNorm(0,1) - Ex2:randNorm(2,1) - ''' - return GiacMethods['randNorm'](self, *args) - - def randPoly(self, *args): - r'''From Giac's documentation: - Help for randPoly: - randPoly([Var(Var)],Intg(n),[law]) - Returns a polynomial with variable var (or x), of degree n and where the coefficients are random integers in the range -99 through 99 with uniform distribution or according to a law. - See also: 1/ ranm 2/ randvector - Ex1:randPoly(5) - Ex2:randPoly(t,8) - Ex3:randPoly(t,8,-1..1) - Ex4:randPoly([x,y],[10,3]) - Ex5:randPoly([x,y],[10,3],1 mod 7) - Ex6: GF(2,8,g);randpoly(t,8,g);randpoly([x,y],[2,3],g) - ''' - return GiacMethods['randPoly'](self, *args) - - def randbetad(self, *args): - r'''From Giac's documentation: - Help for randbetad: - randbetad(Real(a),Real(b)) - Returns a random real according to the Beta distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:randbetad(1,2) - Ex2:randbetad(1.5,4) - ''' - return GiacMethods['randbetad'](self, *args) - - def randbinomial(self, *args): - r'''From Giac's documentation: - Help for randbinomial: - randbinomial(Intg(n),Real(p)) - Returns a random integer with binomial distribution B(n,p) i.e. the number of successes in n independant tests where for each test, the success of probability is p. - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randnorm 7/ randpoisson 8/ randgeometric 9/ randmultinomial - Ex1:randbinomial(10,0.4) - Ex2:randbinomial(100,0.8) - ''' - return GiacMethods['randbinomial'](self, *args) - - def randchisquare(self, *args): - r'''From Giac's documentation: - Help for randchisquare: - randchisquare(Intg(n)) - Returns a random integer with chi^2 distribution, χ^2(n). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randnorm 8/ randgeometric 9/ randmultinomial - Ex1:randchisquare(5) - Ex2:randchisquare(2) - ''' - return GiacMethods['randchisquare'](self, *args) - - def randexp(self, *args): - r'''From Giac's documentation: - Help for randexp: - randexp(Real(a)) - Returns a random real according to the exponential distribution with parameter a>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:randexp(1) - Ex2:randexp(2) - ''' - return GiacMethods['randexp'](self, *args) - - def randfisher(self, *args): - r'''From Giac's documentation: - Help for randfisher: - randfisher(Intg(n),Intg(m)) - Returns a random integer with Fisher-Snedecor distribution F(n,m). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randnorm 8/ randgeometric 9/ randmultinomial - Ex1:randfisher(5,2) - Ex2:randfisher(2,4) - ''' - return GiacMethods['randfisher'](self, *args) - - def randgammad(self, *args): - r'''From Giac's documentation: - Help for randgammad: - randgammad(Real(a),Real(b)) - Returns a random real according to the Gamma distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:randgammad(1,2) - Ex2:randgammad(1.5,4) - ''' - return GiacMethods['randgammad'](self, *args) - - def randgeometric(self, *args): - r'''From Giac's documentation: - Help for randgeometric: - randgeometric(Real(p)) - Returns a random integer following the geometric distribution with parameter p. - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randnorm 7/ randpoisson 8/ randbinomial 9/ randmultinomial - Ex1:randgeometric(0.4) - ''' - return GiacMethods['randgeometric'](self, *args) - - def randint(self, *args): - r'''From Giac's documentation: - Help for randint: - randint(Intg(n1),Intg(n2)) - randint(n1,n2)=an integer in [n1, n2] or [n2,n1]. - See also: 1/ rand - Ex1:randint(1,10) - Ex2:randint(-1,-10) - ''' - return GiacMethods['randint'](self, *args) - - def randmarkov(self, *args): - r'''From Giac's documentation: - Help for randmarkov: - randmarkov(Mtrx(M) || Vctr(v),Intg(i0),[Intg(n)]) - Returns a random sequence of n states (Markov chain) starting from i0, with probability transition matrix M, or returns a stochastic matrix with p recurrent loops v=[n1,..,np] and i0 transient states. - See also: 1/ markov 2/ plotproba - Ex1:randmarkov([[0,0,1/2,0,1/2],[0,0,1,0,0],[1/4,1/4,0,1/4,1/4],[0,0,1/2,0,1/2],[0,0,0,0,1]],2,20) - Ex2:randmarkov([1,2,1,3],4) - ''' - return GiacMethods['randmarkov'](self, *args) - - def randmatrix(self, *args): - r'''From Giac's documentation: - Help for randmatrix: - randmatrix(Intg(n), [Intg(m)],[Interval or quote(DistribLaw)]) - Returns a list of size n or an n*m matrix that contains random integers in the range -99 through 99 with uniform distribution or contains random numbers according to the law put between quotes. - See also: 1/ idn 2/ randPoly 3/ rand 4/ randvector - Ex1:randmatrix(3) - Ex2:randmatrix(3,2) - Ex3:randmatrix(3,2,6) - Ex4:randmatrix(3,2,binomial,10,0.3) - Ex5:randmatrix(3,2,multinomial,[1/2,1/3,1/6]) - Ex6:randmatrix(3,2,poisson,5.2) - Ex7:randmatrix(3,2,normald,0,1) - Ex8:randmatrix(3,2,exp,7/2) - Ex9:randmatrix(3,2,poisson,1.2) - Ex10:randmatrix(3,2,'rand(3)') - Ex11:randmatrix(3,2,1..2) - Ex12:randmatrix(3,5,multinomial,[1/2,1/3,1/6],["R","V","B"]) - Ex13: GF(2,8,g);ranm(3,3,g) - ''' - return GiacMethods['randmatrix'](self, *args) - - def randmultinomial(self, *args): - r'''From Giac's documentation: - Help for randmultinomial: - randmultinomial(List(P),[List(K)]) - Returns a random index or list element according to a multinomial distribution probability list P. - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randnorm 7/ randpoisson 8/ randgeometric 9/ randbinomial - Ex1:randmultinomial([1/2,1/3,1/6]) - Ex2:randmultinomial([1/2,1/3,1/6],["R","V","B"]) - ''' - return GiacMethods['randmultinomial'](self, *args) - - def randnorm(self, *args): - r'''From Giac's documentation: - Help for randnorm: - randnorm(Real(mu),Real(sigma)) - Returns a random real with normal distribution N(mu,sigma). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randpoisson 8/ randgeometric 9/ randmultinomial - Ex1:randnorm(0,1) - Ex2:randnorm(2,1) - ''' - return GiacMethods['randnorm'](self, *args) - - def random(self, *args): - r'''From Giac's documentation: - Help for random: - random(Intg(n) or Interval(p..n) or NULL,[Intg(b1) or Lst(L)],[Intg(b2)]) - rand(n)=a random integer (resp rand(p,n)=a real or rand(p..n)=a real function) with uniform distribution in 0..n-1 (resp in [p;n])(rand()=rand(0,1)=a real in [0,1[) or rand(n,b1,b2)=n integers between b1 and b2 or rand(n,L)=list of n elements of L. - See also: 1/ srand 2/ randpoly 3/ ranm 4/ randvector 5/ hasard - Ex1:random(4) - Ex2:random() - Ex3:random(0,2) - Ex4:random(0..2)() - Ex5: f:=rand(0..2) - Ex6:random(3,1,10) - Ex7:random(3,["r","r","r","b","n"]) - Ex8: L:=["r","r","r","b","n"];L3:=rand(L,3) - Ex9: L:=["r","r","r","b","n"];L3:=L.rand(3) - Ex10: L:=["r","r","r","b","n"];a:=rand(L) - Ex11: L:=["r","r","r","b","n"];a:=L.rand() - ''' - return GiacMethods['random'](self, *args) - - def random_bipartite_graph(self, *args): - r'''From Giac's documentation: - Help for random_bipartite_graph: - random_bipartite_graph(Intg(n)||Lst(a,b),Real(p)||Intg(m)) - Returns a random undirected unweighted bipartite graph with n vertices where each possible edge is present with probability p or where m edges are created at random. When the first argument is list [a,b] of integers, two groups of vertices with sizes a and b are created. - See also: 1/ random_digraph 2/ random_graph 3/ random_planar_graph 4/ random_regular_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - Ex1:random_bipartite_graph(10,0.5) - Ex2:random_bipartite_graph([2,3],1.0) - ''' - return GiacMethods['random_bipartite_graph'](self, *args) - - def random_digraph(self, *args): - r'''From Giac's documentation: - Help for random_digraph: - random_digraph(Intg(n)||Lst(V),Real(p)||Intg(m)) - Returns a random directed unweighted graph with n vertices (list V of labels may me specified) where two vertices are connected with probability p or where m edges are created at random. - See also: 1/ random_bipartite_graph 2/ random_graph 3/ random_planar_graph 4/ random_regular_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - Ex1:random_digraph(8,0.5) - Ex2:random_digraph(8,10) - ''' - return GiacMethods['random_digraph'](self, *args) - - def random_graph(self, *args): - r'''From Giac's documentation: - Help for random_graph: - random_graph(Intg(n)||Lst(V),Real(p)||Intg(m)) - Returns a random undirected unweighted graph with n vertices (list V of labels may be specified) where two vertices are connected with probability p or where m edges are created at random. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_planar_graph 4/ random_regular_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - Ex1:random_graph(8,0.5) - Ex2:random_graph(8,10) - ''' - return GiacMethods['random_graph'](self, *args) - - def random_network(self, *args): - r'''From Giac's documentation: - Help for random_network: - random_network(Intg(a),Intg(b),[Real(p)],[opts]) - Returns a random network with b grid frames of size a*a in which every edge appears with the probability p (by default 0.5). - See also: 1/ is_network 2/ maxflow - Ex1:random_network(3,3) - Ex2:random_network(3,3,acyclic) - Ex3:random_network(3,4,0.75) - ''' - return GiacMethods['random_network'](self, *args) - - def random_planar_graph(self, *args): - r'''From Giac's documentation: - Help for random_planar_graph: - random_planar_graph(Intg(n)||Lst(V),Real(p),[Intg(c)]) - Returns a random planar graph with n vertices, which can also be specified as a list V of their labels, obtained by trying to remove each edge of a random triangulated graph with probability 0<=p<1 [c is connectivity level : 0 - any, 1 - connected, 2 - biconnected, 3 - triconnected (by default, c=1)]. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_graph 4/ random_regular_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - ''' - return GiacMethods['random_planar_graph'](self, *args) - - def random_regular_graph(self, *args): - r'''From Giac's documentation: - Help for random_regular_graph: - random_regular_graph(Intg(n)||Lst(V),Intg(d),[connected]) - Returns a random d-regular graph with n vertices, which may be specified as list V of their labels. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_graph 4/ random_planar_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - Ex1:random_regular_graph(100,80,connected) - ''' - return GiacMethods['random_regular_graph'](self, *args) - - def random_sequence_graph(self, *args): - r'''From Giac's documentation: - Help for random_sequence_graph: - random_sequence_graph(Lst(L)) - Returns a random undirected graph with degree sequence L. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_graph 4/ random_planar_graph 5/ random_sequence_graph 6/ random_tournament 7/ random_tree - Ex1:random_sequence_graph([1,3,3,2,1,2,2,2,3,3]) - ''' - return GiacMethods['random_sequence_graph'](self, *args) - - def random_tournament(self, *args): - r'''From Giac's documentation: - Help for random_tournament: - random_tournament(Intg(n)||Lst(V)) - Returns a random tournament graph with n vertices, which may be specified as list V of their labels. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_graph 4/ random_planar_graph 5/ random_regular_graph 6/ random_sequence_graph 7/ random_tree - Ex1:random_tournament(5) - ''' - return GiacMethods['random_tournament'](self, *args) - - def random_tree(self, *args): - r'''From Giac's documentation: - Help for random_tree: - random_tree(Intg(n)||Lst(V),[Intg(d)||root[=Vrtx(v)]]) - Returns a random tree graph with n vertices, which may be specified as list V of their labels [with the upper bound d (positive integer) for the degree of graph or 'root' for rooted trees]. - See also: 1/ random_bipartite_graph 2/ random_digraph 3/ random_graph 4/ random_planar_graph 5/ random_regular_graph 6/ random_sequence_graph 7/ random_tournament - ''' - return GiacMethods['random_tree'](self, *args) - - def random_variable(self, *args): - r'''From Giac's documentation: - Help for random_variable: - random_variable(Lst(W)||Mtrx(M)||Fnc(f),[params]) - Returns a random variable from a probability density function f or from list of weights (discrete variable). - See also: 1/ randvector 2/ randmatrix 3/ rand - Ex1:random_variable(fisher,2,3) - Ex2:random_variable([["apple",1/3],["orange",1/4],["pear",1/5],["plum",13/60]]) - Ex3:random_variable(k->1-(k/10)^2,range=-10..10) - Ex4:random_variable([3,1,2,5],[alpha,beta,gamma,delta]) - Ex5:random_variable(gammad,mean=12,variance=4) - Ex6:random_variable(binomial,mean=18,stddev=4) - Ex7:random_variable(multinomial,[1/2,1/3,1/6],[a,b,c]) - Ex8:random_variable(weibull,mean=12.5,variance=1) - Ex9:random_variable(uniform,mean=10,stddev=2) - Ex10:random_variable(uniform,e..pi) - ''' - return GiacMethods['random_variable'](self, *args) - - def randperm(self, *args): - r'''From Giac's documentation: - Help for randperm: - randperm(Intg(n)||Lst(L)) - Returns a random permutation of [0,1,2,..,n-1] or of the list L. - See also: 1/ permu2cycles 2/ is_permu 3/ permu2mat - Ex1:randperm(4) - Ex2:randperm(7) - Ex3:randperm([1,3,5,7,9]) - Ex4: L:=[1,3,5,7,9];L:=randperm(L) - Ex5: L:=[1,3,5,7,9];L.randperm() - ''' - return GiacMethods['randperm'](self, *args) - - def randpoisson(self, *args): - r'''From Giac's documentation: - Help for randpoisson: - randpoisson(Real(λ)) - Returns a random integer with poisson distribution P(λ). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randnorm 8/ randgeometric 9/ randmultinomial - Ex1:randpoisson(5.4) - Ex2:randpoisson(2.8) - ''' - return GiacMethods['randpoisson'](self, *args) - - def randpoly(self, *args): - r'''From Giac's documentation: - Help for randpoly: - randpoly([Var(Var)],Intg(n),[law]) - Returns a polynomial with variable var (or x), of degree n and where the coefficients are random integers in the range -99 through 99 with uniform distribution or according to a law. - See also: 1/ ranm 2/ randvector - Ex1:randpoly(5) - Ex2:randpoly(t,8) - Ex3:randpoly(t,8,-1..1) - Ex4:randpoly([x,y],[10,3]) - Ex5:randpoly([x,y],[10,3],1 mod 7) - Ex6: GF(2,8,g);randpoly(t,8,g);randpoly([x,y],[2,3],g) - ''' - return GiacMethods['randpoly'](self, *args) - - def randseed(self, *args): - r'''From Giac's documentation: - Help for randseed: - randseed() - srand returns an integer and initializes the sequence of random numbers. - See also: 1/ RandSeed - Ex1:randseed(12) - Ex2: srand - ''' - return GiacMethods['randseed'](self, *args) - - def randstudent(self, *args): - r'''From Giac's documentation: - Help for randstudent: - randstudent(Intg(n)) - Returns a random integer with Student distribution S(n). - See also: 1/ rand 2/ randpoly 3/ ranm 4/ randvector 5/ randexp 6/ randbinomial 7/ randnorm 8/ randgeometric 9/ randmultinomial - Ex1:randstudent(5) - Ex2:randstudent(2) - ''' - return GiacMethods['randstudent'](self, *args) - - def randvar(self, *args): - r'''From Giac's documentation: - Help for randvar: - randvar(Lst(W)||Mtrx(M)||Fnc(f),[params]) - Returns a random variable from a probability density function f or from list of weights (discrete variable). - See also: 1/ randvector 2/ randmatrix 3/ rand - Ex1:randvar(fisher,2,3) - Ex2:randvar([["apple",1/3],["orange",1/4],["pear",1/5],["plum",13/60]]) - Ex3:randvar(k->1-(k/10)^2,range=-10..10) - Ex4:randvar([3,1,2,5],[alpha,beta,gamma,delta]) - Ex5:randvar(gammad,mean=12,variance=4) - Ex6:randvar(binomial,mean=18,stddev=4) - Ex7:randvar(multinomial,[1/2,1/3,1/6],[a,b,c]) - Ex8:randvar(weibull,mean=12.5,variance=1) - Ex9:randvar(uniform,mean=10,stddev=2) - Ex10:randvar(uniform,e..pi) - ''' - return GiacMethods['randvar'](self, *args) - - def randvector(self, *args): - r'''From Giac's documentation: - Help for randvector: - randvector(Intg(n), [Intg(m)],[Interval or quote(DistribLaw)]) - Returns a list of size n that contains random integers in the range -99 through 99 (or in 0..m-1) with uniform distribution or contains random numbers according to the law put between quotes. - See also: 1/ idn 2/ randPoly 3/ rand 4/ ranm - Ex1:randvector(3) - Ex2:randvector(3,6) - Ex3:randvector(3,normald,0,1) - Ex4:randvector(3,poisson,1.2) - Ex5:randvector(3,exponentiald,1.2) - Ex6:randvector(3,multinomial,[1/2,1/3,1/6]) - Ex7:randvector(3,multinomial,[1/2,1/3,1/6],[a,b,c]) - Ex8:randvector(3,'rand(3)') - Ex9:randvector(3,1..2) - Ex10: GF(2,8,g);randvector(3,g) - ''' - return GiacMethods['randvector'](self, *args) - - def randweibulld(self, *args): - r'''From Giac's documentation: - Help for randweibulld: - randweibulld(Real(a),Real(b)) - Returns a random real according to the Weibull distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:randweibulld(1,2) - Ex2:randweibulld(1.5,4) - ''' - return GiacMethods['randweibulld'](self, *args) - - def rank(self, *args): - r'''From Giac's documentation: - Help for rank: - rank(Mtrx) - Returns the rank of the matrix. - See also: 1/ det 2/ image - Ex1:rank([[1,1,2],[2,1,3],[3,1,4]]) - Ex2:rank([[1,1,2],[2,1,3],[3,1,5]]) - ''' - return GiacMethods['rank'](self, *args) - - def ranm(self, *args): - r'''From Giac's documentation: - Help for ranm: - ranm(Intg(n), [Intg(m)],[Interval or quote(DistribLaw)]) - Returns a list of size n or an n*m matrix that contains random integers in the range -99 through 99 with uniform distribution or contains random numbers according to the law put between quotes. - See also: 1/ idn 2/ randPoly 3/ rand 4/ randvector - Ex1:ranm(3) - Ex2:ranm(3,2) - Ex3:ranm(3,2,6) - Ex4:ranm(3,2,binomial,10,0.3) - Ex5:ranm(3,2,multinomial,[1/2,1/3,1/6]) - Ex6:ranm(3,2,poisson,5.2) - Ex7:ranm(3,2,normald,0,1) - Ex8:ranm(3,2,exp,7/2) - Ex9:ranm(3,2,poisson,1.2) - Ex10:ranm(3,2,'rand(3)') - Ex11:ranm(3,2,1..2) - Ex12:ranm(3,5,multinomial,[1/2,1/3,1/6],["R","V","B"]) - Ex13: GF(2,8,g);ranm(3,3,g) - ''' - return GiacMethods['ranm'](self, *args) - - def ranv(self, *args): - r'''From Giac's documentation: - Help for ranv: - ranv(Intg(n), [Intg(m)],[Interval or quote(DistribLaw)]) - Returns a list of size n that contains random integers in the range -99 through 99 (or in 0..m-1) with uniform distribution or contains random numbers according to the law put between quotes. - See also: 1/ idn 2/ randPoly 3/ rand 4/ ranm - Ex1:ranv(3) - Ex2:ranv(3,6) - Ex3:ranv(3,normald,0,1) - Ex4:ranv(3,poisson,1.2) - Ex5:ranv(3,exponentiald,1.2) - Ex6:ranv(3,multinomial,[1/2,1/3,1/6]) - Ex7:ranv(3,multinomial,[1/2,1/3,1/6],[a,b,c]) - Ex8:ranv(3,'rand(3)') - Ex9:ranv(3,1..2) - Ex10: GF(2,8,g);randvector(3,g) - ''' - return GiacMethods['ranv'](self, *args) - - def rassembler_trigo(self, *args): - r'''From Giac's documentation: - Help for rassembler_trigo: - rassembler_trigo(Expr) - Collects trigonometric expressions. - See also: 1/ texpand 2/ tlin - Ex1:rassembler_trigo(sin(x)+cos(x)) - ''' - return GiacMethods['rassembler_trigo'](self, *args) - - def rat_jordan(self, *args): - r'''From Giac's documentation: - Help for rat_jordan: - rat_jordan(Mtrx) - Returns the list made by the transition matrix and the rational Jordan form of a matrix. - See also: 1/ egv 2/ egvl 3/ jordan 4/ companion - Ex1:rat_jordan([[0,2],[1,0]]) - Ex2:rat_jordan([[-2,-2,1],[-2,1,-2],[1,-2,-2]]) - Ex3:rat_jordan([[1,1,-1,2,-1],[2,0,1,-4,-1],[0,1,1,1,1],[0,1,2,0,1],[0,0,-3,3,-1]]) - ''' - return GiacMethods['rat_jordan'](self, *args) - - def rational(self, *args): - r'''From Giac's documentation: - Help for rational: - rational(Opt) - DOM_RAT or rational is the type of a rational, as returned by the type command. It is also an option of the assume command. - See also: 1/ type 2/ assume 3/ DOM_INT 4/ DOM_FLOAT - Ex1: assume(a,rational) - Ex2: assume(a,DOM_RAT) - Ex3: a:=1/2;type(a) - ''' - return GiacMethods['rational'](self, *args) - - def rationalroot(self, *args): - r'''From Giac's documentation: - Help for rationalroot: - rationalroot(Poly(P)) - Returns the list of rational roots of P without indicating the multiplicity. - See also: 1/ proot 2/ froot 3/ complexroot 4/ realroot 5/ crationalroot - Ex1:rationalroot(2*x^3-9*x^2+13*x-6) - ''' - return GiacMethods['rationalroot'](self, *args) - - def ratnormal(self, *args): - r'''From Giac's documentation: - Help for ratnormal: - ratnormal(Expr) - Rewrites as an irreducible rational fraction. - See also: 1/ normal 2/ simplify 3/ factor 4/ expand - Ex1:ratnormal((x^2-1)/(x^3-1)) - Ex2:ratnormal(c/d+b/d+a/d) - Ex3:ratnormal((x^2-1)/(x^3-1)+(x-1)/(x^3-1)+1) - ''' - return GiacMethods['ratnormal'](self, *args) - - def rdiv(self, *args): - r'''From Giac's documentation: - Help for rdiv: - rdiv(Expr(a),Expr(b)) - Division of a by b (prefixed version of /). - See also: 1/ / - Ex1:rdiv(3,5) - Ex2:rdiv(3.2,5.4) - ''' - return GiacMethods['rdiv'](self, *args) - - def re(self, *args): - r'''From Giac's documentation: - Help for re: - re(Cplx or LstCplx) - Returns the real part of a complex number. - See also: 1/ im 2/ conj - Ex1:re(1+2*i) - Ex2:re((1+2*i)^2) - Ex3:re([1+2*i,(1+2*i)^2]) - ''' - return GiacMethods['re'](self, *args) - - def read(self, *args): - r'''From Giac's documentation: - Help for read: - read(Str(fich_name)) - Reads variables and their values from the file fich_name. - See also: 1/ write 2/ readrgb 3/ readwav 4/ csv2gen - Ex1:read("toto") - ''' - return GiacMethods['read'](self, *args) - - def readrgb(self, *args): - r'''From Giac's documentation: - Help for readrgb: - readrgb(Str(s),[Intg(w)],[Intg(h)]) - Reads a picture file, using it's natural dimensions, or using specified dimensions. - See also: 1/ writergb 2/ readwav - Ex1:readrgb("image.png") - Ex2:readrgb("image.png",50,50) - ''' - return GiacMethods['readrgb'](self, *args) - - def readwav(self, *args): - r'''From Giac's documentation: - Help for readwav: - readwav(Str(s)) - Reads a WAV sound file. - See also: 1/ writewav 2/ readrgb - Ex1:readwav("pop.wav") - ''' - return GiacMethods['readwav'](self, *args) - - def real(self, *args): - r'''From Giac's documentation: - Help for real: - real(Cplx or LstCplx) - Returns the real part of a complex number. - See also: 1/ im 2/ conj - Ex1:real(1+2*i) - Ex2:real((1+2*i)^2) - Ex3:real([1+2*i,(1+2*i)^2]) - ''' - return GiacMethods['real'](self, *args) - - def realroot(self, *args): - r'''From Giac's documentation: - Help for realroot: - realroot([sturm],Poly(P),[Real(l)],[Cplx(a)],[Cplx(b)]) - Returns the list of intervals of length <=l containing the real roots of P inside a..b with their multiplicity. By default the Vincent-Akritas-Strzebonski (VAS) method is used. realroot(sturm,P) uses Sturm's method. - See also: 1/ proot 2/ froot 3/ complexroot 4/ rationalroot 5/ crationalroot 6/ sturmab 7/ VAS - Ex1:realroot(x^3+7,0.1) - Ex2:realroot(x^3-7*x+7) - Ex3:realroot(sturm,x^3-7*x+7) - Ex4:realroot(x^5-2*x^4+x^3+1) - Ex5:realroot(x^5-2*x^4+x^3+1,0.1) - Ex6:realroot(x^3+x+8,1e-5,-4,4) - ''' - return GiacMethods['realroot'](self, *args) - - def reciprocation(self, *args): - r'''From Giac's documentation: - Help for reciprocation: - reciprocation(Crcle,Lst(Pnt,Line)) - Returns the list where the points (resp lines) are replaced with their polars (resp poles) with respect to the circle C. - See also: 1/ pole 2/ polar - Ex1:reciprocation(circle(0,1),[point((1+i)/2), line(1,-1+i)]) - Ex2:reciprocation(circle(0,1),[line(1+i,2),point(1+i*2)]) - ''' - return GiacMethods['reciprocation'](self, *args) - - def rect(self, *args): - r'''From Giac's documentation: - Help for rect: - rect(Expr(x)) - Returns the value of the rectangle function at x. - See also: 1/ boxcar 2/ tri 3/ Heaviside - Ex1:rect(x/2) - ''' - return GiacMethods['rect'](self, *args) - - def rectangle(self, *args): - r'''From Giac's documentation: - Help for rectangle: - rectangle(Pnt(A)||Cplx,Pnt(B)||Cplx,Real(k)||Pnt(P)||Lst(P,k),[Var(D)],[Var(C)]) - Returns and draws the rectangle ABCD, AD=k*AB; if k>0 ABCD is direct else indirect (in the plane ABP AD=AP or AD=k*AB). - See also: 1/ quadrilateral 2/ square - Ex1:rectangle(-i,1,2) - Ex2:rectangle(-i,1,-2,D,C) - Ex3:rectangle(point(0,0,0),point(3,3,3),point(0,0,3),D,C) - Ex4:rectangle(point(0,0,0),point(3,3,3),2,D,C) - ''' - return GiacMethods['rectangle'](self, *args) - - def rectangle_droit(self, *args): - r'''From Giac's documentation: - Help for rectangle_droit: - rectangle_droit(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['rectangle_droit'](self, *args) - - def rectangle_gauche(self, *args): - r'''From Giac's documentation: - Help for rectangle_gauche: - rectangle_gauche(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['rectangle_gauche'](self, *args) - - def rectangle_plein(self, *args): - r'''From Giac's documentation: - Help for rectangle_plein: - rectangle_plein(Real(a),[Real(b)]) - Draws a full direct rectangle (resp square) with sides a,b (resp a) from the turtle position and on the left (by default b=a). - See also: 1/ triangle_plein - Ex1: rectangle_plein 20 - Ex2:rectangle_plein(20) - Ex3:rectangle_plein(20,40) - ''' - return GiacMethods['rectangle_plein'](self, *args) - - def rectangular_coordinates(self, *args): - r'''From Giac's documentation: - Help for rectangular_coordinates: - rectangular_coordinates(LstPolCoord) - Returns the list of the abscissa and of the ordinate of a point given by the list of its polar coordinates. - See also: 1/ abscissa 2/ ordinate 3/ rectangular_coordinates 4/ polar_point - Ex1:rectangular_coordinates([1,pi/4]) - Ex2:rectangular_coordinates(polar_point(1,pi/4)) - ''' - return GiacMethods['rectangular_coordinates'](self, *args) - - def recule(self, *args): - r'''From Giac's documentation: - Help for recule: - recule(NULL or Real(n)) - The turtle takes n steps back (by default n=10). - See also: 1/ avance 2/ saute - Ex1: recule 30 - Ex2:recule(30) - ''' - return GiacMethods['recule'](self, *args) - - def red(self, *args): - r'''From Giac's documentation: - Help for red: - red(Opt) - Option of the display command to display with color. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),red) - Ex2: F:=display(point(2+1.5*i),point_point+green) - ''' - return GiacMethods['red'](self, *args) - - def reduced_conic(self, *args): - r'''From Giac's documentation: - Help for reduced_conic: - reduced_conic(Expr,[LstVar]) - Returns the origin and the matrix of a base in which the conic given by its equation is reduced, 0 or 1 (0 if the conic is degenerate) and the equation of the conic in this base and also its parametric equation. - See also: 1/ gauss 2/ conic - Ex1:reduced_conic(x^2+2*x-2*y+1) - Ex2:reduced_conic(a*x^2-2*x*y+a*y^2-2*x+2*y+3,[x,y]) - Ex3:reduced_conic(2*u^2+2*u*v+2*v^2+5*u+3,[u,v]) - Ex4:reduced_conic((x+y)^2-2*x+1,x,y) - ''' - return GiacMethods['reduced_conic'](self, *args) - - def reduced_quadric(self, *args): - r'''From Giac's documentation: - Help for reduced_quadric: - reduced_quadric(Expr, [LstVar]) - Returns the origin and the matrix of a basis in which the quadric (given by its equation) is reduced, the list of its eigenvalues, the equation of the quadric in this basis and its parametric equation. - See also: 1/ gauss 2/ quadric - Ex1:reduced_quadric(4*x^2+y^2+z^2-4*x*y+4*x*z-2*y*z+8*x-4*y+4*z+2) - Ex2:reduced_quadric(x^2+3*y^2-3*z^2-8*y*z+2*z*x-4*x*y-1,x,y,z) - Ex3:reduced_quadric((u+v)*(v-w)+3*u-5*v,[u,v,w]) - Ex4:reduced_quadric(7*x^2+4*y^2+4*z^2+4*x*y-4*x*z-2*y*z-4*x+5*y+4*z-18,[x,y,z]) - ''' - return GiacMethods['reduced_quadric'](self, *args) - - def ref(self, *args): - r'''From Giac's documentation: - Help for ref: - ref(Mtrx(M)) - Gaussian reduction of AX=b (M=A|(-b)). - See also: 1/ rref 2/ det - Ex1:ref([[3,1,-2],[3,2,2]]) - Ex2:ref([[2,1,1,-1],[1,1,2,-1],[1,2,1,-4]]) - ''' - return GiacMethods['ref'](self, *args) - - def reflection(self, *args): - r'''From Giac's documentation: - Help for reflection: - reflection((Pnt(A) or Line(D)),(Pnt(C) or Curve(C))) - reflection(D,C) (or reflection(A,C))=symmetry of C with the symmetry-line D (or sym-point A). - See also: 1/ rotation 2/ translation - Ex1:reflection(line(0,1+i),A) - Ex2:reflection(B,A) - Ex3:reflection(line(0,1+i),circle(i,1+i)) - ''' - return GiacMethods['reflection'](self, *args) - - def regroup(self, *args): - r'''From Giac's documentation: - Help for regroup: - regroup(Expr) - Collects terms in an expression. - See also: 1/ simplify 2/ normal - Ex1:regroup(x+3*x+5*4/x) - ''' - return GiacMethods['regroup'](self, *args) - - def relabel_vertices(self, *args): - r'''From Giac's documentation: - Help for relabel_vertices: - relabel_vertices(Graph(G),Lst(L)) - Returns a copy of G with vertex labels changed to those in the list L. - See also: 1/ graph_vertices 2/ isomorphic_copy 3/ permute_vertices - Ex1:relabel_vertices(graph([a,b,c]),["first","second","third"]) - ''' - return GiacMethods['relabel_vertices'](self, *args) - - def reliability_polynomial(self, *args): - r'''From Giac's documentation: - Help for reliability_polynomial: - reliability_polynomial(Graph(G),[Var(p)]) - Returns the reliability polynomial [or its value at point p] of undirected graph G. If G is weighted, all weights must be positive integers and are interpreted as edge multiplicities. - See also: 1/ chromatic_polynomial 2/ flow_polynomial 3/ tutte_polynomial - Ex1:reliability_polynomial(graph("petersen")) - Ex2:reliability_polynomial(graph("petersen"),0.5) - ''' - return GiacMethods['reliability_polynomial'](self, *args) - - def rem(self, *args): - r'''From Giac's documentation: - Help for rem: - rem((Vect or Poly),(Vect or Poly),[Var]) - Euclidean remainder of 2 polynomials. - See also: 1/ quo 2/ quorem 3/ Rem 4/ irem - Ex1:rem([1,2,3,4],[-1,2]) - Ex2:rem(x^3+2x^2+3x+4,-x+2) - Ex3:rem(t^3+2t^2+3t+4,-t+2,t) - ''' - return GiacMethods['rem'](self, *args) - - def remain(self, *args): - r'''From Giac's documentation: - Help for remain: - remain(Intg(a),Intg(b)) - Euclidean remainder of 2 integers. - See also: 1/ iquo 2/ smod 3/ rem 4/ mod - Ex1:remain(125,15) - Ex2:remain(125,41) - Ex3:remain(-7,3) - Ex4:remain(25+12*i,5+7*i) - ''' - return GiacMethods['remain'](self, *args) - - def remove(self, *args): - r'''From Giac's documentation: - Help for remove: - remove(FncBool(f)||a,Lst(l)) - Remove the occurrences a of l or the elements a such that f(a)=true. - See also: 1/ select 2/ suppress - Ex1:remove(x->x>=5,[1,2,6,7]) - Ex2:remove(5,[1,2,5,6,7,5]) - ''' - return GiacMethods['remove'](self, *args) - - def reorder(self, *args): - r'''From Giac's documentation: - Help for reorder: - reorder(Expr, LstVar) - Reorders the variables in E according to the order of the 2nd argument. - See also: 1/ - Ex1:reorder(-2) - Ex2:reorder(x^2+2*x+y^2,[y,x]) - ''' - return GiacMethods['reorder'](self, *args) - - def resample(self, *args): - r'''From Giac's documentation: - Help for resample: - resample(Lst(clip),[Intg(s),[Intg(q)]]) - Returns a copy of the input audio clip resampled to the rate s (by default 44100), optionally with quality level q (from 0 to 4, by default 2). - See also: 1/ samplerate 2/ playsnd 3/ readwav 4/ writewav - Ex1:resample(readwav("/some/file"),48000) - Ex2:resample(readwav("/some/file"),48000,3) - ''' - return GiacMethods['resample'](self, *args) - - def residue(self, *args): - r'''From Giac's documentation: - Help for residue: - residue(Expr,Var(v),Cplx(a)) - Returns the residue in a of the expression with v as variable. - See also: 1/ series - Ex1:residue(1/z,z,0) - Ex2:residue(5/z,z=0) - Ex3:residue(cos(z)/(z*(z-b)),z,0) - Ex4:residue(c/(z*(z-b)),z=b) - ''' - return GiacMethods['residue'](self, *args) - - def resoudre(self, *args): - r'''From Giac's documentation: - Help for resoudre: - resoudre(Expr,[Var]) - Solves a (or a set of) polynomial equation. - See also: 1/ linsolve 2/ proot 3/ fsolve 4/ csolve 5/ nSolve - Ex1:resoudre(x^2-3=1) - Ex2:resoudre(x^3-3*y,y) - Ex3:resoudre([y-z=0,z-x=0,x-y=0,x-1+y+z=0],[x,y,z]) - Ex4:resoudre([x^2-y^2=0,x^2-z^2=0],[x,y,z]) - ''' - return GiacMethods['resoudre'](self, *args) - - def resoudre_dans_C(self, *args): - r'''From Giac's documentation: - Help for resoudre_dans_C: - resoudre_dans_C(LstEq,LstVar) - Returns the list of complex solutions of an equation or a matrix where the rows are ℂ-solutions of a system of polynomial equations. - See also: 1/ cZeros 2/ solve 3/ fslove - Ex1:resoudre_dans_C(x^4-1,x) - Ex2:resoudre_dans_C(x^4-y^4 and x+y=2,[x,y]) - Ex3:resoudre_dans_C(x^4-y^4 and x+y=0 and x^2=2*x,[x,y]) - Ex4:resoudre_dans_C(u*v-u=v and v^2=u,[u,v]) - ''' - return GiacMethods['resoudre_dans_C'](self, *args) - - def resoudre_systeme_lineaire(self, *args): - r'''From Giac's documentation: - Help for resoudre_systeme_lineaire: - resoudre_systeme_lineaire(LstLinEq,LstVar) - Linear equations system solver. - See also: 1/ solve 2/ proot 3/ simult 4/ gaussjord 5/ pivot 6/ ref 7/ conjugate_gradient - Ex1:resoudre_systeme_lineaire([x+y+z=1,x-y=2,2*x-z=3],[x,y,z]) - Ex2:resoudre_systeme_lineaire([m*x+y=a,x+m*y=b],[x,y]) - Ex3:resoudre_systeme_lineaire([x+y-z-1,x-y+1,x-y-z-1]%2,[x,y,z]) - Ex4:resoudre_systeme_lineaire([[3,4],[1,2]],[0,1]) - Ex5: p,l,u:=lu([[3,4],[1,2]]); linsolve(p,l,u,[0,1]) - Ex6:resoudre_systeme_lineaire([2*x+y+z=1,x+y+2*z=1,x+2*y+z=4],[x,y,z]) - Ex7:resoudre_systeme_lineaire([[2,1,1],[1,1,2],[1,2,1]],[1,1,4]) - Ex8: p,l,u:=lu([[2,1,1],[1,1,2],[1,2,1]]);linsolve(p,l,u,[1,1,4]) - Ex9: a:=[[100,2],[2,100]];linsolve(evalf(a),[0,1]); - ''' - return GiacMethods['resoudre_systeme_lineaire'](self, *args) - - def resultant(self, *args): - r'''From Giac's documentation: - Help for resultant: - resultant(Poly,Poly,Var) - Resultant of two polynomials. - See also: 1/ sylvester 2/ gcd - Ex1:resultant(x^2-1,x^3-1,x) - Ex2:resultant(x^3-p*x+q,3*x^2-p,x) - ''' - return GiacMethods['resultant'](self, *args) - - def reverse(self, *args): - r'''From Giac's documentation: - Help for reverse: - reverse(Lst(L)) - Returns the list L in reverse order; L:=revlist(L) or L.revlist(). - Ex1:reverse([1,2,3,4]) - Ex2: L:=[1,2,3,4];L:=revlist(L) - Ex3: L:=[1,2,3,4];L.revlist() - Ex4: L:=[1,2,3,4];L.reverse() - ''' - return GiacMethods['reverse'](self, *args) - - def reverse_graph(self, *args): - r'''From Giac's documentation: - Help for reverse_graph: - reverse_graph(Graph(G)) - Returns the copy of G with the directions of all edges reversed. - See also: 1/ digraph - Ex1:reverse_graph(digraph(%{[1,2],[1,3],[2,3]%})) - ''' - return GiacMethods['reverse_graph'](self, *args) - - def reverse_rsolve(self, *args): - r'''From Giac's documentation: - Help for reverse_rsolve: - reverse_rsolve(Vect(v)) - If v=[v_0 ... v_(2n-1)], returns [b_n,...,b_0] such that b_n*v_{n+k}+...+b_0*v_k=0 for k=0..n-1. - See also: 1/ rsolve - Ex1:reverse_rsolve([1,-1,3,3]) - ''' - return GiacMethods['reverse_rsolve'](self, *args) - - def revert(self, *args): - r'''From Giac's documentation: - Help for revert: - revert(Expr) - Returns the inverse expansion of a series expansion at 0. - See also: 1/ series - Ex1:revert(x+x^2+x^4) - ''' - return GiacMethods['revert'](self, *args) - - def revlex(self, *args): - r'''From Giac's documentation: - Help for revlex: - revlex(Opt) - Option of the gbasis or greduce command to specify an order for monomials (complete degree then inverse lexicographic order). - See also: 1/ gbasis 2/ greduce - ''' - return GiacMethods['revlex'](self, *args) - - def revlist(self, *args): - r'''From Giac's documentation: - Help for revlist: - revlist(Lst(L)) - Returns the list L in reverse order; L:=revlist(L) or L.revlist(). - Ex1:revlist([1,2,3,4]) - Ex2: L:=[1,2,3,4];L:=revlist(L) - Ex3: L:=[1,2,3,4];L.revlist() - Ex4: L:=[1,2,3,4];L.reverse() - ''' - return GiacMethods['revlist'](self, *args) - - def rgb(self, *args): - r'''From Giac's documentation: - Help for rgb: - rgb(Opt) - Option of the display (or affichage) command to defined colors RGB. - See also: 1/ display 2/ filled - Ex1: redcolor:=rgb(0.99,0,0);display(square(0,2+i),filled+redcolor) - Ex2: greencolor:=rgb(0,0.99,0);display(square(0,2+i),filled+greencolor) - Ex3: bluecolor:=rgb(0,0,0.99);display(square(0,2+i),filled+bluecolor) - Ex4: greycolor:=rgb(0.5,0.5,0.5);display(square(0,2+i),filled+greycolor) - Ex5: F:=display(square(0,2+i),filled+rgb(rand(),rand(),rand())) - Ex6: L:=rand(),rand(),rand();affichage(square(0,2+i),rgb(L)+rempli);legend(0.2,[L]) - ''' - return GiacMethods['rgb'](self, *args) - - def rhombus(self, *args): - r'''From Giac's documentation: - Help for rhombus: - rhombus(Pnt(A)||Cplx,Pnt(B)||Cplx,Angle(a)||Pnt(P)||Lst(P,a)),[Var(C)],[Var(D)]) - Returns and draws the rhombus ABCD such that the angle (AB,AD)=a (or in the plane ABP angle(AB,AD)=angle(AB,AP) or such that angle(AB,AD)=a). - See also: 1/ square 2/ quadrilateral - Ex1:rhombus(i,1+i,pi/4) - Ex2:rhombus(i,1+i,pi/4,C,D) - Ex3:rhombus(point(0,0,0),point(3,3,3),[point(0,0,3),pi/4]) - Ex4:rhombus(point(0,0,0),point(3,3,3),point(0,0,3),C,D) - Ex5:rhombus(point(0,0,0),point(3,3,3),[point(0,0,3),pi/4],C,D) - ''' - return GiacMethods['rhombus'](self, *args) - - def rhombus_point(self, *args): - r'''From Giac's documentation: - Help for rhombus_point: - rhombus_point(Opt) - Option of the display command for a point. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),point_point) - Ex2: F:=display(point(2+1.5*i),rhombus_point) - ''' - return GiacMethods['rhombus_point'](self, *args) - - def rhs(self, *args): - r'''From Giac's documentation: - Help for rhs: - rhs(Equal(a=b) or Interval(a..b) or Str,Intg) - Returns the right part of an equality, of an interval, of a list or of a string. - See also: 1/ left 2/ mid 3/ tail 4/ head - Ex1:rhs(a=b) - Ex2:rhs(x^2+1=5) - Ex3:rhs(1..5) - Ex4:rhs("abcdefg",3) - ''' - return GiacMethods['rhs'](self, *args) - - def riemann_window(self, *args): - r'''From Giac's documentation: - Help for riemann_window: - riemann_window(Lst,[Interval(n1..n2)]) - Applies the Riemann windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ bartlett_hann_window 12/ triangle_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(riemann_window(randvector(1000,0..1))) - ''' - return GiacMethods['riemann_window'](self, *args) - - def right(self, *args): - r'''From Giac's documentation: - Help for right: - right(Equal(a=b) or Interval(a..b) or Str,Intg) - Returns the right part of an equality, of an interval, of a list or of a string. - See also: 1/ left 2/ mid 3/ tail 4/ head - Ex1:right(a=b) - Ex2:right(x^2+1=5) - Ex3:right(1..5) - Ex4:right("abcdefg",3) - ''' - return GiacMethods['right'](self, *args) - - def right_rectangle(self, *args): - r'''From Giac's documentation: - Help for right_rectangle: - right_rectangle(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['right_rectangle'](self, *args) - - def right_triangle(self, *args): - r'''From Giac's documentation: - Help for right_triangle: - right_triangle((Pnt(A) or Cplx),(Pnt(B) or Cplx),(Real(k) or Pnt(P) or Lst(P,k)),[Var(C)]) - Draws the A_rectangular triangle ABC with AC=k*AB (or in the plane ABP AC=AP or AC=k*AB). - See also: 1/ triangle - Ex1:right_triangle(1,i,tan(pi/3)) - Ex2:right_triangle(1,i,1/2,C) - Ex3:right_triangle(point(0,0,0),point(3,3,3),point(0,0,3)) - Ex4:right_triangle(point(0,0,0),point(3,3,3),[point(0,0,3),1/2],C) - Ex5:right_triangle(point(0,0,0),point(3,3,3),[point(0,0,3),1/2],C) - ''' - return GiacMethods['right_triangle'](self, *args) - - def risch(self, *args): - r'''From Giac's documentation: - Help for risch: - risch(Expr,[Var]) - Returns a primitive of the expression calculated with the Risch algorithm. - See also: 1/ int - Ex1:risch(ln(x),x) - Ex2:risch(ln(x)) - Ex3:risch(exp(x^2),x) - ''' - return GiacMethods['risch'](self, *args) - - def rm_a_z(self, *args): - r'''From Giac's documentation: - Help for rm_a_z: - rm_a_z(NULL) - Erases all the variable name made up of only one lowercase a..z character. - See also: 1/ rm_all_vars - Ex1:rm_a_z() - ''' - return GiacMethods['rm_a_z'](self, *args) - - def rm_all_vars(self, *args): - r'''From Giac's documentation: - Help for rm_all_vars: - rm_all_vars(NULL) - Erases all the variable names. - See also: 1/ rm_a_z - Ex1:rm_all_vars() - ''' - return GiacMethods['rm_all_vars'](self, *args) - - def rmbreakpoint(self, *args): - r'''From Giac's documentation: - Help for rmbreakpoint: - rmbreakpoint(Intg) - Removes a breakpoint. - See also: 1/ breakpoint - Ex1:rmbreakpoint(1) - ''' - return GiacMethods['rmbreakpoint'](self, *args) - - def rmmod(self, *args): - r'''From Giac's documentation: - Help for rmmod: - rmmod(Str(pwd)) - Removes the installed dynamic libraries. - See also: 1/ lsmod 2/ insmod - Ex1:rmmod("/home/parisse/giac/src/libprogfr.so") - ''' - return GiacMethods['rmmod'](self, *args) - - def rmwatch(self, *args): - r'''From Giac's documentation: - Help for rmwatch: - rmwatch(Var) - Clears a variables from the table of displayed variables in step/step. - See also: 1/ watch - Ex1:rmwatch(a) - ''' - return GiacMethods['rmwatch'](self, *args) - - def romberg(self, *args): - r'''From Giac's documentation: - Help for romberg: - romberg(Expr(f(x)),Var(x),Real(a),Real(b)) - Returns the approximate value of integrate(f(x),x,a,b) by Romberg's method. - See also: 1/ integrate 2/ gaussquad - Ex1:romberg(exp(x^2),x,0,1) - Ex2:romberg(x^2,x,0,1) - Ex3:romberg(exp(-x^2),x,-1,1) - ''' - return GiacMethods['romberg'](self, *args) - - def rombergm(self, *args): - r'''From Giac's documentation: - Help for rombergm: - rombergm(Opt) - Option of the area command. - See also: 1/ area - Ex1: area(x^2,x=0..1,5,simpson) - Ex2: area(x^2,x=0..1,5,rombergt) - Ex3: area(x^2,x=0..1,5,rombergm) - Ex4:rombergm(area(x^2,x=0..1,5,gauss15)) - ''' - return GiacMethods['rombergm'](self, *args) - - def rombergt(self, *args): - r'''From Giac's documentation: - Help for rombergt: - rombergt(Opt) - Option of the area command. - See also: 1/ area - Ex1: area(x^2,x=0..1,5,simpson) - Ex2: area(x^2,x=0..1,5,rombergt) - Ex3: area(x^2,x=0..1,5,rombergm) - Ex4:rombergt(area(x^2,x=0..1,5,gauss15)) - ''' - return GiacMethods['rombergt'](self, *args) - - def rond(self, *args): - r'''From Giac's documentation: - Help for rond: - rond(Real(r),[Real(a)],[Real(b)]) - Draws a circle (resp a arc) with radius r (resp and with angle (0,a) or (a,b)), tangent at the turtle position. - See also: 1/ disque - Ex1: rond 30 - Ex2:rond(40) - Ex3:rond(40,90) - Ex4:rond(40,10,100) - ''' - return GiacMethods['rond'](self, *args) - - def root(self, *args): - r'''From Giac's documentation: - Help for root: - root(Expr(a),Expr(b)) - Returns b^(1/a) (root(2,3)=sqrt(3)). - See also: 1/ - Ex1:root(3,2) - Ex2:root(1/3,2) - Ex3:root(3,1.2) - Ex4:root(3.2,1.2) - ''' - return GiacMethods['root'](self, *args) - - def rootof(self, *args): - r'''From Giac's documentation: - Help for rootof: - rootof(LstPoly(P),LstPoly(Q)) - Polynomial in terms of a root of an irreducible polynomial on Q. Returns P(a) with a the greatest root of Q. - See also: 1/ - Ex1: normal(1/rootof([1,0],[1,0,10,0,1])) - Ex2: normal(1/rootof([1,0,0],[1,1,0,-1])) - Ex3: rootof(x^4+x+1):='j'; normal(j^5); - ''' - return GiacMethods['rootof'](self, *args) - - def roots(self, *args): - r'''From Giac's documentation: - Help for roots: - roots(Poly,[Var]) - Returns a matrix having 2 columns and where the rows are the roots of the polynomial with their multiplicity (for 1 variable). - See also: 1/ proot 2/ cZeros - Ex1:roots(t^3-1,t) - Ex2:roots(x^5-2*x^4+x^3) - ''' - return GiacMethods['roots'](self, *args) - - def rotate(self, *args): - r'''From Giac's documentation: - Help for rotate: - rotate(Lst||Str(L),[Intg(n)]) - Returns the list where the last element [or the tail beginning with the n-th element] is moved to the first element (by default n=-1);L:=rotate(L,n) or L.rotate(n). - See also: 1/ tail 2/ mid 3/ shift - Ex1:rotate([0,1,2,3],2) - Ex2:rotate([[1,2,3],[4,5,6],[7,8,9]]) - Ex3:rotate([0,1,2,3,4]) - Ex4:rotate([0,1,2,3,4],-1) - Ex5: L:=[0,1,2,3,4];L:=rotate([0,1,2,3,4]) - Ex6: L:=[0,1,2,3,4];L.rotate() - Ex7: L:=[0,1,2,3];L:=rotate([0,1,2,3],2) - Ex8: L:=[0,1,2,3];L.rotate(2) - ''' - return GiacMethods['rotate'](self, *args) - - def rotation(self, *args): - r'''From Giac's documentation: - Help for rotation: - rotation((Pnt(B) or Cplx or Dr3),Angle(a1),(Pnt(A) or Curve)) - rotation(B,a1,A) (resp rotation(d,a1,A)) is the transformation of A by rotation with center B (resp of axis d) and angle a1. - See also: 1/ translation 2/ reflection - Ex1:rotation(point(1+i),pi/2,point(i)) - Ex2:rotation(1+i,pi/3,line(i,1)) - Ex3:rotation(line(x=y,y=z),pi/2,point(1,-1,2)) - Ex4: r:=rotation(1+i,pi/2);r(i) - Ex5: r:=rotation(line(x=y,y=z),pi/2);r(point(1,-1,2)) - ''' - return GiacMethods['rotation'](self, *args) - - def round(self, *args): - r'''From Giac's documentation: - Help for round: - round(Real or Cplx,[Intg(n)]) - Rounds the real or complex to the nearest integer (resp the nearest decimal number) or to the nearest element of ℤ[i], (resp with n decimals). - See also: 1/ floor 2/ ceil - Ex1:round(2.5) - Ex2:round(-2.4) - Ex3:round(-2.5+i*2.4) - Ex4:round(1.237,2) - Ex5:round(sqrt(2)+i*sqrt(5),4) - ''' - return GiacMethods['round'](self, *args) - - def row(self, *args): - r'''From Giac's documentation: - Help for row: - row(Mtrx(A),Intg(n)||Interval(n1..n2)) - Returns row n or the sequence of the rows n1..n2 of the matrix A, or optional argument of count,count_eq,count_inf,count_sup. - See also: 1/ col 2/ count 3/ count_eq 4/ count_inf 5/ count_sup - Ex1:row([[1,2,3],[4,5,6],[7,8,9]],1) - Ex2:row([[1,2,3],[4,5,6],[7,8,9]],0..1) - Ex3: count_eq(3,[[1,2,3],[4,3,2],[3,2,1]],row) - ''' - return GiacMethods['row'](self, *args) - - def rowAdd(self, *args): - r'''From Giac's documentation: - Help for rowAdd: - rowAdd(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by replacing the n2-th row by the sum of the n1-th and n2-th rows. - See also: 1/ rowSwap - Ex1:rowAdd([[1,2],[3,4],[5,6]],1,2) - ''' - return GiacMethods['rowAdd'](self, *args) - - def rowDim(self, *args): - r'''From Giac's documentation: - Help for rowDim: - rowDim(Mtrx) - Number of rows of a matrix. - See also: 1/ ncols - Ex1:rowDim([[1,2,3],[4,5,6]]) - Ex2:rowDim([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['rowDim'](self, *args) - - def rowNorm(self, *args): - r'''From Giac's documentation: - Help for rowNorm: - rowNorm(Vect or Mtrx) - Returns the max of the l1_norm of the rows of a matrix: rowNorm(a_{j,k})=max_j(sum_k(|a_{j,k}|)). - See also: 1/ norm - Ex1:rowNorm([[1,2],[3,-4]]) - Ex2:rowNorm([[1,2,3,-4],[-5,3,2,1]]) - ''' - return GiacMethods['rowNorm'](self, *args) - - def rowSwap(self, *args): - r'''From Giac's documentation: - Help for rowSwap: - rowSwap(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by swapping the n1-th row and the n2-th row. - See also: 1/ rowAdd 2/ colSwap - Ex1:rowSwap([[1,2],[3,4],[5,6]],1,2) - ''' - return GiacMethods['rowSwap'](self, *args) - - def rowdim(self, *args): - r'''From Giac's documentation: - Help for rowdim: - rowdim(Mtrx) - Number of rows of a matrix. - See also: 1/ ncols - Ex1:rowdim([[1,2,3],[4,5,6]]) - Ex2:rowdim([[1,2],[3,4],[5,6]]) - ''' - return GiacMethods['rowdim'](self, *args) - - def rownorm(self, *args): - r'''From Giac's documentation: - Help for rownorm: - rownorm(Vect or Mtrx) - Returns the max of the l1_norm of the rows of a matrix: rowNorm(a_{j,k})=max_j(sum_k(|a_{j,k}|)). - See also: 1/ norm - Ex1:rownorm([[1,2],[3,-4]]) - Ex2:rownorm([[1,2,3,-4],[-5,3,2,1]]) - ''' - return GiacMethods['rownorm'](self, *args) - - def rowspace(self, *args): - r'''From Giac's documentation: - Help for rowspace: - rowspace(Mtrx(A), [Var(d)]) - Returns a matrix where the rows are a basis of the vector space generated by the rows of the matrix A [d is the dimension of this space]. - See also: 1/ colspace - Ex1:rowspace([[1,2,3],[1,2,3],[1,2,4],[1,2,5]]) - Ex2:rowspace([[1,2,3],[1,3,6],[2,5,9]],d) - ''' - return GiacMethods['rowspace'](self, *args) - - def rowswap(self, *args): - r'''From Giac's documentation: - Help for rowswap: - rowswap(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by swapping the n1-th row and the n2-th row. - See also: 1/ rowAdd 2/ colSwap - Ex1:rowswap([[1,2],[3,4],[5,6]],1,2) - ''' - return GiacMethods['rowswap'](self, *args) - - def rref(self, *args): - r'''From Giac's documentation: - Help for rref: - rref(Mtrx(M),[Intg(k)]||Opt) - Row reduction to echelon form of AX=b (M=A|(-b)) [Reduction on columns 0..k-1]. - See also: 1/ ker 2/ image 3/ det 4/ Rref 5/ pivot 6/ ref 7/ keep_pivot - Ex1:rref([[3,1,-2],[3,2,2]]) - Ex2:rref([[2,1,1,-1],[1,1,2,-1],[1,2,1,-4]]) - Ex3:rref([[2,1,1,-1],[1,1,2,-1],[1,2,1,-4]],2) - Ex4:rref([[1,1,0,0,-a1],[0,1,1,0,-a2],[0,0,1,1,-a3],[1,0,0,1,-a4]],keep_pivot) - ''' - return GiacMethods['rref'](self, *args) - - def rsolve(self, *args): - r'''From Giac's documentation: - Help for rsolve: - rsolve((Expr or LstExpr),(Var or LstVar),(InitVal or LstInitVal)) - Gives the value of a recurrent sequence or of a system of recurrent sequences. - See also: 1/ seqsolve 2/ plotseq 3/ tableseq 4/ reverse_rsolve - Ex1:rsolve(u(n+1)=2*u(n)+n,u(n),u(0)=1) - Ex2:rsolve(u(n+1)=2*u(n)+n,u(n),u(1)^2=1) - Ex3:rsolve(u(n+1)=(u(n)-1)/(u(n)-2),u(n),u(0)=4) - Ex4:rsolve(u(n+2)=u(n)+2*u(n+1)+n+1,u(n),[u(0)=0,u(1)=1]) - Ex5:rsolve([u(n+1)=3*v(n)+u(n),v(n+1)=v(n)+u(n)],[u(n),v(n)],[u(0)=1,v(0)=2]) - ''' - return GiacMethods['rsolve'](self, *args) - - def same(self, *args): - r'''From Giac's documentation: - Help for same: - same(Expr,Expr) - Equality test. - See also: 1/ - Ex1:same(a,b) - Ex2:same((2-1)^2,2^2-2*2+1) - ''' - return GiacMethods['same'](self, *args) - - def sample(self, *args): - r'''From Giac's documentation: - Help for sample: - sample(Lst(L),Intg(n)) - sample(L,n)= rand(n,L)=list of the n extracted elements of L without replacement. - See also: 1/ rand - Ex1:sample([1,2,3,4,5,6],6) - Ex2:sample([1,2,3,4,5,6],3) - Ex3:sample(["r","r","r","b","n"],3) - Ex4: L:=[1,2,3,4,5,6];L:=sample(L,3) - Ex5: L:=[1,2,3,4,5,6];L.sample(3) - Ex6: - ''' - return GiacMethods['sample'](self, *args) - - def samplerate(self, *args): - r'''From Giac's documentation: - Help for samplerate: - samplerate(Lst(clip)) - Returns the sampling rate of an audio clip, in Hertz. - See also: 1/ bit_depth 2/ channels 3/ channel_data 4/ duration - Ex1:samplerate(readwav("/some/file")) - ''' - return GiacMethods['samplerate'](self, *args) - - def sans_factoriser(self, *args): - r'''From Giac's documentation: - Help for sans_factoriser: - sans_factoriser(Opt.) - Option of the plotimplicit command. - See also: 1/ plotimplicit - Ex1: plotimplicit(x^2+y^2-1,x,y,unfactored) - Ex2: plotimplicit(x^2+y^2-1,[x,y],unfactored) - Ex3: plotimplicit(x^2+y^2+z^2-1,x,y,z,xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - Ex4: plotimplicit(x^2+y^2+z^2-1,[x,y,z],xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - Ex5: plotimplicit(x^2+y^2+z^2-1,x=0..1,y=0..1,z=0..1,xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - ''' - return GiacMethods['sans_factoriser'](self, *args) - - def saute(self, *args): - r'''From Giac's documentation: - Help for saute: - saute(NULL or Real(n)) - The turtle takes n steps forward without traces (by default n=10). - See also: 1/ avance 2/ recule - Ex1: saute 30 - Ex2:saute(30) - ''' - return GiacMethods['saute'](self, *args) - - def scalarProduct(self, *args): - r'''From Giac's documentation: - Help for scalarProduct: - scalarProduct(Vect(v1),Vect(v2)) - Scalar product. - See also: 1/ * 2/ cross 3/ .* 4/ hadamard - Ex1:scalarProduct([1,2],[3,4]) - Ex2:scalarProduct([3,2,4],[3,2,4]) - Ex3:scalarProduct([[1,2],[3,4]],[[3,2],[4,5]]) - ''' - return GiacMethods['scalarProduct'](self, *args) - - def scalar_product(self, *args): - r'''From Giac's documentation: - Help for scalar_product: - scalar_product(Vect(v1),Vect(v2)) - Scalar product. - See also: 1/ * 2/ cross 3/ .* 4/ hadamard - Ex1:scalar_product([1,2],[3,4]) - Ex2:scalar_product([3,2,4],[3,2,4]) - Ex3:scalar_product([[1,2],[3,4]],[[3,2],[4,5]]) - ''' - return GiacMethods['scalar_product'](self, *args) - - def scatterplot(self, *args): - r'''From Giac's documentation: - Help for scatterplot: - scatterplot(Mtrx) - Draws for k=0..nrows, the points (xk,yk) where xk=element row k column 0 and yk=element row k column j (j=1..ncols). - See also: 1/ polygonplot 2/ polygonscatterplot 3/ listplot - Ex1:scatterplot([[1,2,3],[2,0,1],[-1,2,3]]) - ''' - return GiacMethods['scatterplot'](self, *args) - - def schur(self, *args): - r'''From Giac's documentation: - Help for schur: - schur(Mtrx(A)) - Matrix reduction to Hessenberg form. Returns [P,B] such that B=inv(P)*A*P:SCHUR(A)=hessenberg(A,-1). - See also: 1/ hessenberg - Ex1:schur([[1,2,3],[4,5,6],[7,8,1]]) - Ex2:schur([[1,2,3,4],[4,5,6,7],[7,8,9,0],[0,1,2,3]]) - ''' - return GiacMethods['schur'](self, *args) - - def sec(self, *args): - r'''From Giac's documentation: - Help for sec: - sec(Expr) - Secant: sec(x)=1/cos(x). - See also: 1/ cos 2/ asec - Ex1:sec(pi/3) - ''' - return GiacMethods['sec'](self, *args) - - def secant_solver(self, *args): - r'''From Giac's documentation: - Help for secant_solver: - secant_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['secant_solver'](self, *args) - - def segment(self, *args): - r'''From Giac's documentation: - Help for segment: - segment((Pnt or Cplx or Lst([xM,yM])),(Pnt or Cplx or Lst([xN,yN]),[Var],[Var] or Opt) - segment(A,B) draws the segment AB and segment([xM,yM],[xN,yN]) draws the vector as origin [xM,yM], of coordinates [xN,yN] (i.e draws segment(M,point(M+N)) or Option of the arc command. - See also: 1/ line 2/ arc - Ex1:segment(i,1+i) - Ex2:segment(point(i),point(1+i)) - Ex3:segment(point(i),point(1+i),A,B) - Ex4:segment([-1,0],[2,1]) - Ex5:segment([-1,0],point([-1,0]+[2,1])) - Ex6: arc(i,1,pi/4,segment) - Ex7: affichage( arc(i,1,pi/4,segment),1+rempli) - ''' - return GiacMethods['segment'](self, *args) - - def seidel_spectrum(self, *args): - r'''From Giac's documentation: - Help for seidel_spectrum: - seidel_spectrum(Graph(G)) - Returns the Seidel spectrum of G as a list of lists with two elements, each containing an eigenvalue and its multiplicity. - See also: 1/ graph_spectrum - Ex1:seidel_spectrum(graph("clebsch")) - ''' - return GiacMethods['seidel_spectrum'](self, *args) - - def seidel_switch(self, *args): - r'''From Giac's documentation: - Help for seidel_switch: - seidel_switch(Graph(G),Lst(V)) - Returns a copy of G in which the edges between vertices in list V and vertices not in V are inverted (replaced with a set of edges from V to other vertices which are not present in G). - See also: 1/ neighbors 2/ graph_complement - Ex1:seidel_switch(cycle_graph(5),[1,2]) - ''' - return GiacMethods['seidel_switch'](self, *args) - - def select(self, *args): - r'''From Giac's documentation: - Help for select: - select(FncBool(f),Lst(l)) - Selects the elements e of l such that f(e)=true. - See also: 1/ remove 2/ range - Ex1:select(x->x>=5,[1,2,6,7]) - Ex2:select(x->isprime(x),range(20)).^2 - ''' - return GiacMethods['select'](self, *args) - - def semi_augment(self, *args): - r'''From Giac's documentation: - Help for semi_augment: - semi_augment(Mtrx(A),Mtrx(B)) - Returns a matrix made with A and B, with n1+n2 rows and p columns if dim(A)=[n1,p] and dim(B)=[n2,p]. - See also: 1/ augment - Ex1:semi_augment([[68,-21],[56,59],[1,2]],[[68,-21],[56,59]]) - ''' - return GiacMethods['semi_augment'](self, *args) - - def seq(self, *args): - r'''From Giac's documentation: - Help for seq: - seq(Expr(Xpr),Var(Var)=Int(a..b),[Real(p)]||Expr(Xpr),Var(Var),Real(a),Real(b),[Real(p)]) - Returns the sequence (2 or 3 arg) or the list (4 or 5 arg) obtained when var goes from a to b (step p) in Xpr (or the Xpr is repeated n times or returns the sequence of reals from a to b (step p)). And also seq(expression,variable,list) is equivalent to map(list,unapply(expression,variable)) - See also: 1/ $ 2/ makelist 3/ range 4/ map 5/ unapply - Ex1:seq(0.3,4) - Ex2:seq(t,4) - Ex3:seq(0,0) - Ex4:seq(2^k,k=0..8) - Ex5:seq(2^k,k=0..8,2) - Ex6:seq(0.3..2,0.2) - Ex7:seq(2^k,k,0,8) - Ex8:seq(2^k,k,0,8,2) - Ex9:seq(x^3,x,[1,2,3]) - Ex10: [seq(0.3..2,0.2)] - Ex11: a:=(1,2,3);eval(seq(a,4)) - ''' - return GiacMethods['seq'](self, *args) - - def seqplot(self, *args): - r'''From Giac's documentation: - Help for seqplot: - seqplot(Expr(f(Var)),Var=[a,xm,xM],Intg(p)) - For seeing the pth terms of the sequence u(0)=a,u(n)=f(u(n-1)). - See also: 1/ seqsolve 2/ rsolve - Ex1:seqplot(sqrt(2+x),6,5) - Ex2:seqplot(sqrt(2+t),t=6,5) - Ex3:seqplot(sqrt(2+x),x=[6,1,7],5,affichage=epaisseur_ligne_2) - ''' - return GiacMethods['seqplot'](self, *args) - - def seqsolve(self, *args): - r'''From Giac's documentation: - Help for seqsolve: - seqsolve((Expr or LstExpr),(Var or LstVar),(InitVal or LstInitVal)) - Gives the value of a recurrent sequence (u_{n+1}=f(u_n) or u_{n+k}=f(u_n,u_{n+1}...u_{n+k-1})) or of a system of recurrent sequences. - See also: 1/ rsolve 2/ plotseq 3/ tableseq - Ex1:seqsolve(2x+n,[x,n],1) - Ex2:seqsolve(2x+n*3^n,[x,n],1) - Ex3:seqsolve(x+y,[x,y,n],[1,1]) - Ex4:seqsolve(x+2*y+n+1,[x,y,n],[0,1]) - Ex5:seqsolve([x+2*y,n+1+x],[x,y,n],[0,1]) - Ex6:seqsolve([x+2*y+n+1,x],[x,y,n],[0,1]) - ''' - return GiacMethods['seqsolve'](self, *args) - - def sequence_graph(self, *args): - r'''From Giac's documentation: - Help for sequence_graph: - sequence_graph(Lst(L)) - Returns an undirected graph with the degree sequence equal to the list L. - See also: 1/ degree_sequence 2/ is_graphic_sequence - Ex1:sequence_graph(degree_sequence(sequence_graph([3,2,4,2,3,4,5,7]))) - ''' - return GiacMethods['sequence_graph'](self, *args) - - def series(self, *args): - r'''From Giac's documentation: - Help for series: - series(Expr,Equal(var=limit_point),[Order],[Dir(1,0,-1)]) - Series expansion at finite or infinite points. - See also: 1/ limit 2/ taylor 3/ pad 4/ polynom 5/ truncate - Ex1:series(sin(x)/x,x=0) - Ex2:series(sin(x),x=0,6,polynom) - Ex3:series(ln(x+x^2)-ln(x),x=0,1) - Ex4:series((x^4+x+2)/(x^2+1),x=0,5) - Ex5: series("h",8); ln(1+h); - Ex6:series(1/(1+x+y),[x,y],[0,0],5) - Ex7:series(sin(x*y),[x,y],[1,pi/2],3) - Ex8:series(sin((1+h*t)*(pi/2+k*t)),t=0,3,polynom)(t=1) - Ex9:series(y^2/x^3,[x,y],[1,-1],3) - Ex10:series((-1+k*t)^2/(1+h*t)^3,t=0,3,polynom)(t=1) - Ex11:series(subst(sin(x+y)+cos(y*x),[x,y],h*[x,y]),h=0,6,polynom) - Ex12:series(subst(sin(x+y)+cos(y*x),[x,y],h*[x,y]),h=0,6,polynom)(h=1) - Ex13: truncate(series(sin(x),x=0,6),6) - ''' - return GiacMethods['series'](self, *args) - - def set_edge_attribute(self, *args): - r'''From Giac's documentation: - Help for set_edge_attribute: - set_edge_attribute(Graph(G),Edge(e),Seq(tag1=value1,tag2=value2,..)) - Stores the attributes to edge e and returns the modified copy of G. - See also: 1/ discard_edge_attribute 2/ get_edge_attribute 3/ list_edge_attributes - Ex1:set_edge_attribute(cycle_graph(3),[1,2],"cost"=12.4) - ''' - return GiacMethods['set_edge_attribute'](self, *args) - - def set_edge_weight(self, *args): - r'''From Giac's documentation: - Help for set_edge_weight: - set_edge_weight(Graph(G),Edge(e),Real(w)) - Sets the weight of the edge e in the weighted graph G to w and returns the modified copy of G. - See also: 1/ is_weighted 2/ get_edge_weight 3/ make_weighted 4/ weight_matrix - Ex1:set_edge_weight(graph(%{[1,2],[2,3]%}),[1,2],5) - ''' - return GiacMethods['set_edge_weight'](self, *args) - - def set_graph_attribute(self, *args): - r'''From Giac's documentation: - Help for set_graph_attribute: - set_graph_attribute(Graph(G),Seq(tag1=value1,tag2=value2,..)) - Stores the attributes where each tag is a string, and returns the modified copy of G. - See also: 1/ discard_graph_attribute 2/ get_graph_attribute 3/ list_graph_attributes - Ex1:set_graph_attribute(cycle_graph(3),"name"="cycle graph") - ''' - return GiacMethods['set_graph_attribute'](self, *args) - - def set_pixel(self, *args): - r'''From Giac's documentation: - Help for set_pixel: - set_pixel(Intg(x),Intg(y),Intg(col)) - Pixel on and adds to the list of pixels. Run show_pixels() to display - See also: 1/ clear 2/ show_pixels 3/ draw_line 4/ draw_rectangle 5/ draw_polygon - Ex1: clear(); set_pixel(4); draw_pixel(1,2,red); show_pixels(); - ''' - return GiacMethods['set_pixel'](self, *args) - - def set_vertex_attribute(self, *args): - r'''From Giac's documentation: - Help for set_vertex_attribute: - set_vertex_attribute(Graph(G),Vrtx(v),Seq(tag1=value1,tag2=value2,..)) - Stores the attributes to vertex v and returns the modified copy of G. - See also: 1/ discard_vertex_attribute 2/ get_vertex_attribute 3/ list_vertex_attributes - Ex1:set_vertex_attribute(cycle_graph(3),1,"supply"=27) - ''' - return GiacMethods['set_vertex_attribute'](self, *args) - - def set_vertex_positions(self, *args): - r'''From Giac's documentation: - Help for set_vertex_positions: - set_vertex_positions(Graph(G),Lst(vp)) - Sets the coordinates, given in the list vp, to the vertices of G and returns the modified copy of G. - See also: 1/ draw_graph - Ex1: G:=graph([1,2,3,4,5,6],%{[1,2],[1,4],[4,5],[2,5],[2,3],[3,6],[5,6]%}); G:=set_vertex_positions(G,[[0,0],[0.5,0],[1,0],[0,0.5],[0.5,0.5],[1,0.5]]) - ''' - return GiacMethods['set_vertex_positions'](self, *args) - - def shift(self, *args): - r'''From Giac's documentation: - Help for shift: - shift(Lst,[Intg(n)]) - Returns the list where the last element [or the tail beginning with the n-th element] is moved to the first element and then completed with 0s (by default n=-1);L:=shift(L,2) o L.shift(2). - See also: 1/ rotate 2/ tail - Ex1:shift([0,1,2,3],2) - Ex2:shift([0,1,2,3]) - Ex3:shift([0,1,2,3,4]) - Ex4: L:=[0,1,2,3];L:=shift(L,2) - Ex5: L:=[0,1,2,3];L.shift(2) - ''' - return GiacMethods['shift'](self, *args) - - def shift_phase(self, *args): - r'''From Giac's documentation: - Help for shift_phase: - shift_phase(Expr) - shift_phase returns the expressions where the phase of the evaluated trigonometric expressions is increased by pi/2. - See also: 1/ series - Ex1:shift_phase(sin(x)) - Ex2:shift_phase('sin(x+pi/2)') - Ex3:shift_phase(x+sin(x)) - Ex4:shift_phase(x+sin(x)) - Ex5:shift_phase(cos(t)) - Ex6:shift_phase(tan(u)) - ''' - return GiacMethods['shift_phase'](self, *args) - - def shortest_path(self, *args): - r'''From Giac's documentation: - Help for shortest_path: - shortest_path(Graph(G),Vrtx(s),Vrtx(t)||Lst(T)) - Returns the shortest path from vertex s to vertex t in G. If such path does not exist, returns an empty list. If vector T of vertices from G is given, the list of shortest paths from s to each t int T is returned. - See also: 1/ dijkstra 2/ vertex_distance - Ex1:shortest_path(cycle_graph(6),1,5) - ''' - return GiacMethods['shortest_path'](self, *args) - - def show_pixels(self, *args): - r'''From Giac's documentation: - Help for show_pixels: - show_pixels(NULL) - Displays the list of pixels. - See also: 1/ set_pixel 2/ clear - Ex1:show_pixels() - ''' - return GiacMethods['show_pixels'](self, *args) - - def shuffle(self, *args): - r'''From Giac's documentation: - Help for shuffle: - shuffle(Intg(n)||Lst(L)) - Returns a random permutation of [0,1,2,..,n-1] or of the list L. - See also: 1/ permu2cycles 2/ is_permu 3/ permu2mat - Ex1:shuffle(4) - Ex2:shuffle(7) - Ex3:shuffle([1,3,5,7,9]) - Ex4: L:=[1,3,5,7,9];L:=randperm(L) - Ex5: L:=[1,3,5,7,9];L.randperm() - ''' - return GiacMethods['shuffle'](self, *args) - - def sierpinski_graph(self, *args): - r'''From Giac's documentation: - Help for sierpinski_graph: - sierpinski_graph(Intg(n),Intg(k),[triangle]) - Returns Sierpiński (triangle) graph S(n,k) (resp. ST(n,k)). - See also: 1/ graph - Ex1:sierpinski_graph(2,4) - Ex2:sierpinski_graph(4,3) - Ex3:sierpinski_graph(3,4) - Ex4:sierpinski_graph(3,2) - Ex5:sierpinski_graph(3,3,at_triangle) - ''' - return GiacMethods['sierpinski_graph'](self, *args) - - def sign(self, *args): - r'''From Giac's documentation: - Help for sign: - sign(Expr) - Returns the sign (-1,0,+1) of its argument. - See also: 1/ abs - Ex1:sign(-4) - Ex2:sign(4-5) - ''' - return GiacMethods['sign'](self, *args) - - def signature(self, *args): - r'''From Giac's documentation: - Help for signature: - signature(Permut) - Returns the signature of a permutation. - See also: 1/ permu2cycles 2/ is_permu - Ex1:signature([1,0,3,4,2]) - ''' - return GiacMethods['signature'](self, *args) - - def signe(self, *args): - r'''From Giac's documentation: - Help for signe: - signe(Str(s)) - Writes the string s with the font 20 at the point [10,10]. - See also: 1/ ecris - Ex1:signe("Thomas") - Ex2:signe(Thomas) - ''' - return GiacMethods['signe'](self, *args) - - def similarity(self, *args): - r'''From Giac's documentation: - Help for similarity: - similarity(Pnt or Dr3,Real,Angle,Pnt) - similarity(B,k,a1,A)=transformation of A in the similarity (center B or axis d, coeff k,angle a1) (or also homothety(B,k*exp(i*a1),A)). - See also: 1/ homothety - Ex1:similarity(1+i,2,pi/3,i) - Ex2:similarity(line(x=y,y=z),2,pi/3,point(-1,2,1)) - Ex3: s:=similarity(1+i,2,pi/3);s(i) - Ex4: s:=similarity(line(x=y,y=z),2,pi/3),s(point(-1,2,1)) - ''' - return GiacMethods['similarity'](self, *args) - - def simp2(self, *args): - r'''From Giac's documentation: - Help for simp2: - simp2(Intg(A) or Poly(A),Intg(B) or Poly(B)) - Returns the list [A/gcd(A,B),B/gcd(A,B)]. - See also: 1/ gcd - Ex1:simp2(12,18) - Ex2:simp2(x^3-1,x^2-1) - ''' - return GiacMethods['simp2'](self, *args) - - def simplex_reduce(self, *args): - r'''From Giac's documentation: - Help for simplex_reduce: - simplex_reduce(Mtrx(A), Vect(b), Vect(c)) - Reduction by simplex algorithm to find max(c.x) under A.x<=b and x>=0, b>=0. Returns the maximum, the augmented solution x and the reduced matrix. Accepts also [[A|I|b],[-c|*|0]] as argument. - Ex1:simplex_reduce([[3,2,2],[1,1,1]],[3,4],[1,2,3]) - Ex2:simplex_reduce([[3,2,2,1,0,3],[1,1,1,0,1,4],[-1,-2,-3,0,0,0]]) - Ex3:simplex_reduce([[-3,2],[1,1]],[3,4],[1,2]) - Ex4:simplex_reduce([[-3,2,1,0,3],[1,1,0,1,4],[-1,-2,0,0,0]]) - Ex5:simplex_reduce([[2,1,1,1,0,0,2],[1,2,3,0,1,0,5],[2,2,1,0,0,1,6],[-3,-1,-3,1,-1,2,0]]) - ''' - return GiacMethods['simplex_reduce'](self, *args) - - def simplifier(self, *args): - r'''From Giac's documentation: - Help for simplifier: - simplifier(Expr) - Simplifies an expression. - See also: 1/ normal - Ex1:simplifier(4*atan(1/5)-atan(1/239)) - Ex2:simplifier(texpand((sin(3*x)+sin(7*x))/sin(5*x))) - Ex3:simplifier(texpand((cos(3*x)+cos(7*x))/cos(5*x))) - ''' - return GiacMethods['simplifier'](self, *args) - - def simplify(self, *args): - r'''From Giac's documentation: - Help for simplify: - simplify(Expr) - Simplifies an expression. - See also: 1/ normal - Ex1:simplify(4*atan(1/5)-atan(1/239)) - Ex2:simplify(texpand((sin(3*x)+sin(7*x))/sin(5*x))) - Ex3:simplify(texpand((cos(3*x)+cos(7*x))/cos(5*x))) - ''' - return GiacMethods['simplify'](self, *args) - - def simpson(self, *args): - r'''From Giac's documentation: - Help for simpson: - simpson(Opt) - Option of the area command. - See also: 1/ area - Ex1: area(x^2,x=0..1,5,simpson) - Ex2: area(x^2,x=0..1,5,rombergt) - Ex3: area(x^2,x=0..1,5,rombergm) - Ex4:simpson(area(x^2,x=0..1,5,gauss15)) - ''' - return GiacMethods['simpson'](self, *args) - - def simult(self, *args): - r'''From Giac's documentation: - Help for simult: - simult(Mtrx(A),Mtrx(B)) - Returns the matrix where the column of index k is solution of A*X=column of index k of B (=B[0..nr-1,k..k] with nr=number of rows of B). - See also: 1/ rref 2/ linsolve - Ex1:simult([[3,1],[3,2]],[[-2],[2]]) - Ex2:simult([[3,1],[3,2]],[[-2,1],[2,-1]]) - ''' - return GiacMethods['simult'](self, *args) - - def sin(self, *args): - r'''From Giac's documentation: - Help for sin: - sin(Expr or Opt) - Sine or option of the convert or convertir command (id trigsin). - See also: 1/ asin 2/ convert 3/ trigsin - Ex1:sin(0) - Ex2: convert(cos(x)^4+sin(x)^2,sin) - ''' - return GiacMethods['sin'](self, *args) - - def sin2costan(self, *args): - r'''From Giac's documentation: - Help for sin2costan: - sin2costan(Expr) - Replaces sin(x) by cos(x)*tan(x) in the argument. - See also: 1/ tan2sincos 2/ cos2sintan 3/ tan2sincos2 4/ tan2cossin2 - Ex1:sin2costan(sin(x)) - ''' - return GiacMethods['sin2costan'](self, *args) - - def sinc(self, *args): - r'''From Giac's documentation: - Help for sinc: - sinc(Expr(x)) - Returns the value of the cardinal sine function at x. - See also: 1/ sin - Ex1:sinc(pi*x) - ''' - return GiacMethods['sinc'](self, *args) - - def sincos(self, *args): - r'''From Giac's documentation: - Help for sincos: - sincos(Expr or Opt) - Transforms the complex exponential into sine and cosine (id exp2trig) or option of the convert or convertir command (id sincos). - See also: 1/ trig2trig 2/ trig2exp 3/ atrig2ln 4/ convert - Ex1:sincos(exp(i*x)) - Ex2:sincos(exp(-i*x)) - Ex3: convert(exp(i*x),sincos) - ''' - return GiacMethods['sincos'](self, *args) - - def single_inter(self, *args): - r'''From Giac's documentation: - Help for single_inter: - single_inter(Curve,Curve,[Pnt(A)||LstPnt(L)]) - Gives one of the points of intersection of 2 curves or surfaces (or the intersection near A or not in L). - See also: 1/ intersect 2/ head - Ex1:single_inter(line(i,1-i),line(0,1)) - Ex2:single_inter(line(i,1-i),circle(0,1)) - Ex3:single_inter(line(i,1+2*i),circle(0,1),[point(i)]) - Ex4:single_inter(line(-1-i,1+2*i),circle(0,1),point(-1)) - Ex5:single_inter(circle(1,sqrt(2)),circle(0,1)) - Ex6:single_inter(plane(x=y),plane(y=z)) - Ex7:single_inter(line(x=y+1,y=2*z),plane(y=z)) - ''' - return GiacMethods['single_inter'](self, *args) - - def sinh(self, *args): - r'''From Giac's documentation: - Help for sinh: - sinh(Expr) - Hyperbolic sine. - See also: 1/ asinh - Ex1:sinh(0) - ''' - return GiacMethods['sinh'](self, *args) - - def sizes(self, *args): - r'''From Giac's documentation: - Help for sizes: - sizes(Lst or Str or Seq) - Returns the list of sizes of a list of lists. - See also: 1/ size 2/ dim - Ex1:sizes([[1,2,3],[1,2],[1]]) - ''' - return GiacMethods['sizes'](self, *args) - - def slope(self, *args): - r'''From Giac's documentation: - Help for slope: - slope(Line||Pnt||Cplx,[Pnt||Cplx]) - Returns the slope of the line defined in the argument or is an attribute of line. - See also: 1/ line 2/ tangent 3/ LinTan 4/ slopeatraw 5/ slopeat - Ex1:slope(line(1,2i)) - Ex2:slope(segment(1,2i)) - Ex3:slope(1,2i) - Ex4:slope(line(y+2x=2)) - Ex5:slope(point(1),point(2i)) - Ex6:slope(tangent(plotfunc(sin(x)),pi/4)) - Ex7:slope(LineTan(sin(x),pi/4)) - Ex8: line(point(1,2),slope=-1) - ''' - return GiacMethods['slope'](self, *args) - - def slopeat(self, *args): - r'''From Giac's documentation: - Help for slopeat: - slopeat(Line, Pnt||Cplx(z0)) - slopeat(d,z0) displays at the point(z0), with a legend, the value of the slope of the line or segment d. - See also: 1/ slope 2/ slopeatraw - Ex1: A:=point(0);B:=point(1+i);slopeat(droite(A,B),(1+i)/2) - Ex2: s:=segment(1-i,i);slopeat(s,point(0.4)) - Ex3: t:=tangent(plotfunc(sin(x)),pi/4);slopeat(t,0) - ''' - return GiacMethods['slopeat'](self, *args) - - def slopeatraw(self, *args): - r'''From Giac's documentation: - Help for slopeatraw: - slopeatraw(Line, Pnt||Cplx(z0)) - slopeatraw(d,z0) displays at point(z0), the value of the slope of the line or segment d. - See also: 1/ slope 2/ slopeat - Ex1: A:=point(0);B:=point(1+i);slopeatraw(droite(A,B),(1+i)/2) - Ex2: s:=segment(1-i,i);slopeatraw(s,point(0.4)) - Ex3:slopeatraw(tangent(plotfunc(sin(x)),pi/4),0) - Ex4:slopeatraw((LineTan sin(x),pi/4),i) - ''' - return GiacMethods['slopeatraw'](self, *args) - - def smith(self, *args): - r'''From Giac's documentation: - Help for smith: - smith(Mtrx(A)) - Smith normal form of a matrix with polynomial coefficients : returns U,D,V such that U and V are invertible, D is diagonal, and U*A*V=D. - See also: 1/ hermite 2/ ismith 3/ ihermite - Ex1: n:=10; A:=ranm(n,n) % 17; U,D,V:=smith(x*idn(n)-A);normal(U*(x*idn(n)-A)*V-D); diag(D); - Ex2: GF(3,5,g); n:=3; A:=ranm(n,n,g); U,D,V:=smith(x*idn(n)-A);normal(U*(x*idn(n)-A)*V-D); diag(D); - ''' - return GiacMethods['smith'](self, *args) - - def smod(self, *args): - r'''From Giac's documentation: - Help for smod: - smod(Intg,Intg) - Returns the Euclidean symmetric remainder of two integers. - See also: 1/ irem 2/ iquo 3/ mod 4/ fracmod - Ex1:smod(8,3) - Ex2:smod(10,4) - Ex3:smod(11,7) - ''' - return GiacMethods['smod'](self, *args) - - def snedecor(self, *args): - r'''From Giac's documentation: - Help for snedecor: - snedecor(Intg(n),Intg(m),Real(x0)) - Returns the probability density of the Fisher-Snedecor law (n and m are the numbers of degrees of freedom). - See also: 1/ fisher_cdf 2/ fisher_icdf 3/ randvector 4/ ranm - Ex1:snedecor(4,10,2.1) - Ex2:snedecor(4,4,2.1) - Ex3: randvector(5,fisher,4,6) - Ex4: ranm(2,3,fisher,4,6) - ''' - return GiacMethods['snedecor'](self, *args) - - def snedecor_cdf(self, *args): - r'''From Giac's documentation: - Help for snedecor_cdf: - snedecor_cdf(Intg(n),Intg(m),Real(x0)) - Returns the probability that a Fisher-Snedecor random variable is less than x0 (n and m are the numbers of degrees of freedom). - See also: 1/ UTPF 2/ fisher_icdf 3/ fisherd - Ex1:snedecor_cdf(4,4,2.1) - Ex2:snedecor_cdf(4,10,3.5) - ''' - return GiacMethods['snedecor_cdf'](self, *args) - - def snedecor_icdf(self, *args): - r'''From Giac's documentation: - Help for snedecor_icdf: - snedecor_icdf(Intg(n),Intg(m),Real(p)) - Returns h such as the probability that a Fisher-Snedecor random variable is less than h is p (n and m are the numbers of degrees of freedom and 0<=p<=1). - See also: 1/ fisher_cdf 2/ fisherd - Ex1:snedecor_icdf(4,10,0.95) - Ex2:snedecor_icdf(4,10,0.05) - ''' - return GiacMethods['snedecor_icdf'](self, *args) - - def snedecord(self, *args): - r'''From Giac's documentation: - Help for snedecord: - snedecord(Intg(n),Intg(m),Real(x0)) - Returns the probability density of the Fisher-Snedecor law (n and m are the numbers of degrees of freedom). - See also: 1/ fisher_cdf 2/ fisher_icdf 3/ randvector 4/ ranm - Ex1:snedecord(4,10,2.1) - Ex2:snedecord(4,4,2.1) - Ex3: randvector(5,fisher,4,6) - Ex4: ranm(2,3,fisher,4,6) - ''' - return GiacMethods['snedecord'](self, *args) - - def snedecord_cdf(self, *args): - r'''From Giac's documentation: - Help for snedecord_cdf: - snedecord_cdf(Intg(n),Intg(m),Real(x0)) - Returns the probability that a Fisher-Snedecor random variable is less than x0 (n and m are the numbers of degrees of freedom). - See also: 1/ UTPF 2/ fisher_icdf 3/ fisherd - Ex1:snedecord_cdf(4,4,2.1) - Ex2:snedecord_cdf(4,10,3.5) - ''' - return GiacMethods['snedecord_cdf'](self, *args) - - def snedecord_icdf(self, *args): - r'''From Giac's documentation: - Help for snedecord_icdf: - snedecord_icdf(Intg(n),Intg(m),Real(p)) - Returns h such as the probability that a Fisher-Snedecor random variable is less than h is p (n and m are the numbers of degrees of freedom and 0<=p<=1). - See also: 1/ fisher_cdf 2/ fisherd - Ex1:snedecord_icdf(4,10,0.95) - Ex2:snedecord_icdf(4,10,0.05) - ''' - return GiacMethods['snedecord_icdf'](self, *args) - - def solid_line(self, *args): - r'''From Giac's documentation: - Help for solid_line: - solid_line(Opt) - Option of the display command for a line. - See also: 1/ display - Ex1: display(line(y=x),green+dash_line+line_width_2) - Ex2: d:=display(line(2+i,1),cap_round_line) - ''' - return GiacMethods['solid_line'](self, *args) - - def solve(self, *args): - r'''From Giac's documentation: - Help for solve: - solve(Expr,[Var]) - Solves a (or a set of) polynomial equation. - See also: 1/ linsolve 2/ proot 3/ fsolve 4/ csolve 5/ nSolve - Ex1:solve(x^2-3=1) - Ex2:solve(x^3-3*y,y) - Ex3:solve([y-z=0,z-x=0,x-y=0,x-1+y+z=0],[x,y,z]) - Ex4:solve([x^2-y^2=0,x^2-z^2=0],[x,y,z]) - ''' - return GiacMethods['solve'](self, *args) - - def somme(self, *args): - r'''From Giac's documentation: - Help for somme: - somme(Expr,Var,VarMin(a),VarMax(b),[VarStep(p)]) - Discrete sum (with 2 or 4 arguments return then sum from a to b if a<=b or of the opposite of the sum from b+1 to a-1 if a>b+1 or 0 if a=b+1) or the discrete primitive or sum of the elements of a list or a sequence. - See also: 1/ + - Ex1:somme(1/n^2,n,1,17) - Ex2:somme(1/n^2,n=1..17) - Ex3:somme(1/n^2,n,17,1) - Ex4:somme(1/n^2,n=17..1) - Ex5:somme(1/n^2,n,17,1,1) - Ex6:somme(1/n^2,n,1,17,2) - Ex7:somme(1,2,3,4) - Ex8:somme([[1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,9]]) - Ex9:somme(1/(x*(x+1)),x) - Ex10:somme(cos(n*x),n) - ''' - return GiacMethods['somme'](self, *args) - - def sommet(self, *args): - r'''From Giac's documentation: - Help for sommet: - sommet(Op or Fnct) - Returns the top of an operator. - See also: 1/ feuille 2/ quote - Ex1:sommet(quote(gcd(45,123))) - Ex2:sommet('gcd(45,123)') - ''' - return GiacMethods['sommet'](self, *args) - - def sort(self, *args): - r'''From Giac's documentation: - Help for sort: - sort(LstReal or Seq [Fnc]) - Returns the sorted list (or sequence) with increasing order according to the second argument which defines a weak strict ordering or sorts and collects equal terms in sums and products. - See also: 1/ SortA 2/ SortD - Ex1:sort([3,2,2,4,1,0]) - Ex2:sort(3,2.1,2,4,1,0) - Ex3:sort([3,4,2],(x,y)->x>y) - Ex4:sort([[1,2],[2,3],[4,3]],(x,y)->when(x[1]==y[1],x[0]>y[0],x[1]>y[1])) - Ex5:sort(y*x*2+x*y) - ''' - return GiacMethods['sort'](self, *args) - - def sorta(self, *args): - r'''From Giac's documentation: - Help for sorta: - sorta(LstReal||Seq×||Mtrx) - Sorts the list in increasing order or sorts the columns of the matrix so the first row is in increasing order. - See also: 1/ SortA 2/ sortd 3/ sort - Ex1:sorta(3,4,2) - Ex2:sorta([3,4,2]) - Ex3:sorta([[3,4,2],[6,4,5]]) - ''' - return GiacMethods['sorta'](self, *args) - - def sortd(self, *args): - r'''From Giac's documentation: - Help for sortd: - sortd(LstReal||Seq||Mtrx) - Sorts the list in decreasing order or sorts the columns of the matrix so the first row is in decreasing order. - See also: 1/ SortD 2/ sorta 3/ sort - Ex1:sortd(3,4,2) - Ex2:sortd([3,4,2]) - Ex3:sortd([[3,4,2],[6,4,5]]) - ''' - return GiacMethods['sortd'](self, *args) - - def sorted(self, *args): - r'''From Giac's documentation: - Help for sorted: - sorted(LstReal or Seq [Fnc]) - Returns the sorted list (or sequence) with increasing order according to the second argument which defines a weak strict ordering or sorts and collects equal terms in sums and products. - See also: 1/ SortA 2/ SortD - Ex1:sorted([3,2,2,4,1,0]) - Ex2:sorted(3,2.1,2,4,1,0) - Ex3:sorted([3,4,2],(x,y)->x>y) - Ex4:sorted([[1,2],[2,3],[4,3]],(x,y)->when(x[1]==y[1],x[0]>y[0],x[1]>y[1])) - Ex5:sorted(y*x*2+x*y) - ''' - return GiacMethods['sorted'](self, *args) - - def soundsec(self, *args): - r'''From Giac's documentation: - Help for soundsec: - soundsec(Intg(n),[Intg(N)]) - Generates a vector coding n seconds of time/N (default N=44100). - See also: 1/ readwav 2/ writewav 3/ playsnd - Ex1:soundsec(1) - Ex2:soundsec(1,22100) - ''' - return GiacMethods['soundsec'](self, *args) - - def spanning_tree(self, *args): - r'''From Giac's documentation: - Help for spanning_tree: - spanning_tree(Graph(G),[Vrtx(r)]) - Returns a spanning tree of undirected graph G [with the vertex r as the root node]. - See also: 1/ number_of_spanning_trees 2/ minimal_spanning_tree - Ex1:spanning_tree(graph("petersen")) - Ex2:spanning_tree(graph("petersen"),5) - ''' - return GiacMethods['spanning_tree'](self, *args) - - def sphere(self, *args): - r'''From Giac's documentation: - Help for sphere: - sphere((Pnt or Vect),(Pnt or Real)) - sphere(A,B) (resp sphere(A,r)) draws the sphere with diameter AB (resp center A and radius r) in 3D space. - See also: 1/ circle - Ex1:sphere([0,0,0],[2,2,2]) - Ex2:sphere([1,1,1],1) - ''' - return GiacMethods['sphere'](self, *args) - - def spline(self, *args): - r'''From Giac's documentation: - Help for spline: - spline(Lst(lx),Lst(ly),Var(x),Intg(d)) - Natural spline through the points given by the lx and ly lists, variable x, degree d. - See also: 1/ lagrange - Ex1:spline([0,1,2],[1,3,0],x,3) - ''' - return GiacMethods['spline'](self, *args) - - def split(self, *args): - r'''From Giac's documentation: - Help for split: - split(Expr(Xpr),Lst(var1,var2)) - Splits the two variables var1,var2 of the expression Xpr (without denominator) or returns [0]. - See also: 1/ factor - Ex1:split(x^3*y^2-y^2+x^3-1,[x,y]) - Ex2:split(x^3*y^2-y^2+x^3+1,[x,y]) - ''' - return GiacMethods['split'](self, *args) - - def spring(self, *args): - r'''From Giac's documentation: - Help for spring: - spring(Opt) - Option for the draw_graph command. - See also: 1/ planar 2/ tree 3/ draw_graph - ''' - return GiacMethods['spring'](self, *args) - - def sq(self, *args): - r'''From Giac's documentation: - Help for sq: - sq(Seq) - Is the name of the function (ℝ^n -> ℝ)=sum of the squares of the arguments. - See also: 1/ sqrt - Ex1:sq(5) - Ex2:sq(1,2,3) - ''' - return GiacMethods['sq'](self, *args) - - def sqrfree(self, *args): - r'''From Giac's documentation: - Help for sqrfree: - sqrfree(Expr) - Factorization of the its argument gathering the terms with the same exponent. - See also: 1/ factor - Ex1:sqrfree(x^4-2*x^2+1) - Ex2:sqrfree((x-2)^7*(x+2)^7*(x^4-2*x^2+1)) - ''' - return GiacMethods['sqrfree'](self, *args) - - def sqrt(self, *args): - r'''From Giac's documentation: - Help for sqrt: - sqrt(Expr) - Square root. - See also: 1/ surd 2/ ^ - Ex1:sqrt(50) - Ex2:sqrt(x^2) - ''' - return GiacMethods['sqrt'](self, *args) - - def square(self, *args): - r'''From Giac's documentation: - Help for square: - square((Pnt(A) or Cplx),(Pnt(B) or Cplx),[Pnt(P),Var(C),Var(D)]) - Returns and draws the square of side AB (ABCD is direct) (in the plane ABP). - See also: 1/ rhombus 2/ quadrilateral - Ex1:square(i,1+i) - Ex2:square(i,1+i,C,D) - Ex3:square(point(0,0,0),point(3,3,3),point(0,0,3)) - Ex4:square(point(0,0,0),point(3,3,3),point(0,0,3),C,D) - ''' - return GiacMethods['square'](self, *args) - - def square_point(self, *args): - r'''From Giac's documentation: - Help for square_point: - square_point(Opt) - Option of the display command for a point. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),point_point) - Ex2: F:=display(point(2+1.5*i),rhombus_point) - ''' - return GiacMethods['square_point'](self, *args) - - def srand(self, *args): - r'''From Giac's documentation: - Help for srand: - srand() - srand returns an integer and initializes the sequence of random numbers. - See also: 1/ RandSeed - Ex1:srand(12) - Ex2: srand - ''' - return GiacMethods['srand'](self, *args) - - def sst(self, *args): - r'''From Giac's documentation: - Help for sst: - sst(NULL) - Steps 1 instruction. - See also: 1/ - Ex1:sst() - ''' - return GiacMethods['sst'](self, *args) - - def sst_in(self, *args): - r'''From Giac's documentation: - Help for sst_in: - sst_in(NULL) - Enters into a function in step-by-step mode. - See also: 1/ - Ex1:sst_in() - ''' - return GiacMethods['sst_in'](self, *args) - - def st_ordering(self, *args): - r'''From Giac's documentation: - Help for st_ordering: - st_ordering(Graph(G),Vrtx(s),Vrtx(t)) - Returns ST numbering for the biconnected graph G with source s and sink (target) t. - See also: 1/ is_biconnected - Ex1:st_ordering(graph("petersen"),1,2) - ''' - return GiacMethods['st_ordering'](self, *args) - - def star_graph(self, *args): - r'''From Giac's documentation: - Help for star_graph: - star_graph(Intg(n)) - Returns the complete bipartite graph complete_graph(1,n). - See also: 1/ complete_graph 2/ wheel_graph - Ex1:star_graph(5) - ''' - return GiacMethods['star_graph'](self, *args) - - def star_point(self, *args): - r'''From Giac's documentation: - Help for star_point: - star_point(Opt) - Option of the display command for a point. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),point_point) - Ex2: F:=display(point(2+1.5*i),rhombus_point) - ''' - return GiacMethods['star_point'](self, *args) - - def stdDev(self, *args): - r'''From Giac's documentation: - Help for stdDev: - stdDev(Lst||Mtrx,[Lst]) - Returns an unbiased estimate of the population standard deviation of the sample (first argument) with an optional list of weight as second argument. - See also: 1/ mean 2/ stddev - Ex1:stdDev([1,2,3]) - Ex2:stdDev([1,2,3],[1,2,1]) - Ex3:stdDev([[1,2,3],[5,6,7]]) - ''' - return GiacMethods['stdDev'](self, *args) - - def stddev(self, *args): - r'''From Giac's documentation: - Help for stddev: - stddev(Lst||Mtrx,[Lst]) - Returns the standard deviation of the elements of its argument with an optional second argument as weight or the list of standard deviations of the columns of a matrix. - See also: 1/ mean 2/ variance 3/ stddevp - Ex1:stddev([1,2,3]) - Ex2:stddev([1,2,3],[1,2,1]) - Ex3:stddev([[1,2,3],[5,6,7]]) - ''' - return GiacMethods['stddev'](self, *args) - - def stddevp(self, *args): - r'''From Giac's documentation: - Help for stddevp: - stddevp(Lst||Mtrx,[Lst]) - Returns an unbiased estimate of the population standard deviation of the sample (first argument) with an optional list of weight as second argument. - See also: 1/ mean 2/ stddev - Ex1:stddevp([1,2,3]) - Ex2:stddevp([1,2,3],[1,2,1]) - Ex3:stddevp([[1,2,3],[5,6,7]]) - ''' - return GiacMethods['stddevp'](self, *args) - - def steffenson_solver(self, *args): - r'''From Giac's documentation: - Help for steffenson_solver: - steffenson_solver(Opt) - Argument for fsolve giving the method for solving a numerical equation. - See also: 1/ fsolve - Ex1: fsolve(cos(x)=x,x,0..1,bisection_solver) - Ex2: fsolve(cos(x)=x,x,0..1,brent_solver) - Ex3: fsolve(cos(x)=x,x,0..1,falsepos_solver) - Ex4: fsolve(cos(x)=x,x,0,newton_solver) - Ex5: fsolve(cos(x)=x,x,0,secant_solver) - Ex6: fsolve(cos(x)=x,x,0,steffenson_solver) - ''' - return GiacMethods['steffenson_solver'](self, *args) - - def stereo2mono(self, *args): - r'''From Giac's documentation: - Help for stereo2mono: - stereo2mono(Lst(clip)) - Returns an audio clip with all channels in the input clip downmixed to a single one. - See also: 1/ channel_data 2/ mean 3/ createwav - Ex1:stereo2mono(readwav("/some/file")) - ''' - return GiacMethods['stereo2mono'](self, *args) - - def str(self, *args): - r'''From Giac's documentation: - Help for str: - str(Expr or Opt) - Returns the evaluated expression as a string or is an option of the convert or convertir command (id string). - See also: 1/ expr 2/ format 3/ convert - Ex1:str(1.23) - Ex2:str(a:=12) - Ex3:str(quote(a:=12)) - Ex4: convert(quote(a:=12),string) - ''' - return GiacMethods['str'](self, *args) - - def strongly_connected_components(self, *args): - r'''From Giac's documentation: - Help for strongly_connected_components: - strongly_connected_components(Graph(G)) - Returns the list of strongly connected components in digraph G. - See also: 1/ connected_components 2/ is_connected 3/ is_strongly_connected - Ex1:strongly_connected_components(digraph([1,2,3],%{[1,2],[1,3],[2,3],[3,2]%})) - ''' - return GiacMethods['strongly_connected_components'](self, *args) - - def student(self, *args): - r'''From Giac's documentation: - Help for student: - student(Intg(n),Real(x0)) - Returns the probability density of the Student law (n is the number of degrees of freedom). - See also: 1/ student_cdf 2/ student_icdf - Ex1:student(3,5.2) - Ex2:student(1,5.2) - ''' - return GiacMethods['student'](self, *args) - - def student_cdf(self, *args): - r'''From Giac's documentation: - Help for student_cdf: - student_cdf(Intg(n),Real(x0)) - Returns the probability that a Student random variable is less than x0 (n is the number of degrees of freedom). - See also: 1/ UTPT 2/ student_icdf 3/ studentd - Ex1:student_cdf(3,2.35) - Ex2:student_cdf(3,-3.2) - ''' - return GiacMethods['student_cdf'](self, *args) - - def student_icdf(self, *args): - r'''From Giac's documentation: - Help for student_icdf: - student_icdf(Intg(n),Real(p)) - Returns h such as the probability that a Student random variable is less than h is p (n is the number of degrees of freedom and 0<=p<=1). - See also: 1/ student_cdf 2/ studentd - Ex1:student_icdf(3,0.95) - Ex2:student_icdf(3,0.05) - ''' - return GiacMethods['student_icdf'](self, *args) - - def studentd(self, *args): - r'''From Giac's documentation: - Help for studentd: - studentd(Intg(n),Real(x0)) - Returns the probability density of the Student law (n is the number of degrees of freedom). - See also: 1/ student_cdf 2/ student_icdf - Ex1:studentd(3,5.2) - Ex2:studentd(1,5.2) - ''' - return GiacMethods['studentd'](self, *args) - - def studentt(self, *args): - r'''From Giac's documentation: - Help for studentt: - studentt(Lst,Real,[Real],Fnc,[Real]) - T-Test/Student law: arg1=[success,trial] or [mean,sample size] or data, arg2=proportion or data, arg3 optional if data=sigma, arg4 alternative '!=' or '>' or '<', arg5 optional alpha confidence level. - See also: 1/ normalt 2/ chisquaret 3/ kolmogorovt - Ex1:studentt([10,20],.5,.02,'!=',0.1) - Ex2:studentt([0.48,20],0.5,0.1,'<') - ''' - return GiacMethods['studentt'](self, *args) - - def sturm(self, *args): - r'''From Giac's documentation: - Help for sturm: - sturm(Poly,[Var],[Cplx(a)],[Cplx(b)]) - Sturm sequence corresponding to a polynomial or number of sign changes of this polynomial in ]a;b]. - See also: 1/ sturmseq 2/ sturmab - Ex1:sturm(x^3-1,x) - Ex2:sturm(x^5-x^3,x) - Ex3:sturm((x^5-x^3)/(x+2),x) - Ex4:sturm(x^5-x^3,x,-2,5) - Ex5:sturm(x^3-1,x,-2-i,5+3i) - ''' - return GiacMethods['sturm'](self, *args) - - def sturmab(self, *args): - r'''From Giac's documentation: - Help for sturmab: - sturmab(Poly,Var,Cplx(a),Cplx(b)) - Number of sign changes of a polynomial in ]a;b] or of complex roots in a..b if a or b is non-real. - See also: 1/ sturm 2/ sturmseq 3/ realroot - Ex1:sturmab(x^3-1,x,-2,5) - Ex2:sturmab(x^3-1,x,-2-i,5+3i) - ''' - return GiacMethods['sturmab'](self, *args) - - def sturmseq(self, *args): - r'''From Giac's documentation: - Help for sturmseq: - sturmseq(Poly,[Var]) - Sturm sequence corresponding to a polynomial or to a rational fraction. - See also: 1/ sturm 2/ sturmab - Ex1:sturmseq(x^3-1,x) - Ex2:sturmseq(x^5-x^3,x) - Ex3:sturmseq((x^5-x^3)/(x+2),x) - ''' - return GiacMethods['sturmseq'](self, *args) - - def style(self, *args): - r'''From Giac's documentation: - Help for style: - style(Opt) - Local option (Maple compatibility) of a graphic command to plot a line with dots with style=point. - See also: 1/ line_width - Ex1: segment(0,point(1,1),style=point) - Ex2: line(y=x,style=point,display=green+line_width_2) - ''' - return GiacMethods['style'](self, *args) - - def subMat(self, *args): - r'''From Giac's documentation: - Help for subMat: - subMat(Mtrx(A),Intg(n1),Intg(n2),Intg(n3),Intg(n4).) - Extracts a sub matrix with first element=A[n1,n2] and last element=A[n3,n4]. - See also: 1/ mid - Ex1:subMat([[1,2],[3,4],[5,6]],1,0,2,1) - ''' - return GiacMethods['subMat'](self, *args) - - def subdivide_edges(self, *args): - r'''From Giac's documentation: - Help for subdivide_edges: - subdivide_edges(Graph(G),Lst(E),[Intg(r)]) - Inserts r (by default 1) new vertices to each edge/arc in G contained in the list E (which may be a single edge/arc) and returns the modified copy of G. New vertices are labelled with smallest available integers. - See also: 1/ edges - Ex1:subdivide_edges(complete_graph(2,3),[[1,3],[1,4]],2) - ''' - return GiacMethods['subdivide_edges'](self, *args) - - def subgraph(self, *args): - r'''From Giac's documentation: - Help for subgraph: - subgraph(Graph(G),Lst(E)) - Returns the subgraph of G defined by the edges in list E. - See also: 1/ induced_subgraph 2/ highlight_subgraph - Ex1:subgraph(complete_graph(5),[[1,2],[2,3],[3,4],[4,1]]) - ''' - return GiacMethods['subgraph'](self, *args) - - def subs(self, *args): - r'''From Giac's documentation: - Help for subs: - subs(Expr or Var=value,Var=value or Expr) - Equivalent of subst except in maple_mode where the arguments are switched over, in maple_mode choose the second example. - See also: 1/ subst 2/ maple_mode 3/ algsubs 4/ () - Ex1:subs(1/(4+x^2),x=2) - Ex2:subs(x=2,1/(4+x^2)) - Ex3: f:=1/(4+x^2);f(x=2) - ''' - return GiacMethods['subs'](self, *args) - - def subsop(self, *args): - r'''From Giac's documentation: - Help for subsop: - subsop(Lst||Mtrx,Intg(n)=Expr) - Replaces in the list (or the matrix) the element of index n with the expression (with Maple the arguments are switched over)(NULL remove this element). - Ex1:subsop([0,1,2,3,4],2=sqrt(2)+1) - Ex2:subsop([[1,2],[3,4]],[1,1]=5) - Ex3:subsop([[1,2],[3,4]],1=[10,8]) - Ex4:subsop([0,1,2,3],'1=NULL') - ''' - return GiacMethods['subsop'](self, *args) - - def subst(self, *args): - r'''From Giac's documentation: - Help for subst: - subst(Expr,Var(v)=value(a)) - Substitutes a value for a variable in an expression. - See also: 1/ eval 2/ algsubs 3/ subs 4/ () - Ex1:subst(1/(4+x^2),x=2) - Ex2:subst(1/(x^2+y^2),x=2,y=3) - Ex3:subst(1/(x^2+y^2+z^2),[x=2,y=3,z=1]) - Ex4:subst(x-2/(4+x^2),x=1) - Ex5:subst('integrate(sin(x^2)*x,x)',x=sqrt(t)) - Ex6:subst('sum(x^(n+1)/((n+p+1)*(n+1)),n,0,inf)',n=k-1) - Ex7: f:=1/(x^2+y^2;f(x=2,y=3) - ''' - return GiacMethods['subst'](self, *args) - - def substituer(self, *args): - r'''From Giac's documentation: - Help for substituer: - substituer(Expr,Var(v)=value(a)) - Substitutes a value for a variable in an expression. - See also: 1/ eval 2/ algsubs 3/ subs 4/ () - Ex1:substituer(1/(4+x^2),x=2) - Ex2:substituer(1/(x^2+y^2),x=2,y=3) - Ex3:substituer(1/(x^2+y^2+z^2),[x=2,y=3,z=1]) - Ex4:substituer(x-2/(4+x^2),x=1) - Ex5:substituer('integrate(sin(x^2)*x,x)',x=sqrt(t)) - Ex6:substituer('sum(x^(n+1)/((n+p+1)*(n+1)),n,0,inf)',n=k-1) - Ex7: f:=1/(x^2+y^2;f(x=2,y=3) - ''' - return GiacMethods['substituer'](self, *args) - - def subtype(self, *args): - r'''From Giac's documentation: - Help for subtype: - subtype(Expr) - Returns 1 for a sequence,2 for a set, 10 for a polynomial and 0 otherwise. - See also: 1/ DOM_LIST 2/ type - Ex1:subtype(1,2,3) - Ex2:subtype(set[1,2,3]) - Ex3:subtype(poly1[1,2,3]) - Ex4:subtype([1,2,3]) - ''' - return GiacMethods['subtype'](self, *args) - - def sum(self, *args): - r'''From Giac's documentation: - Help for sum: - sum(Expr,Var,VarMin(a),VarMax(b),[VarStep(p)]) - Discrete sum (with 2 or 4 arguments return then sum from a to b if a<=b or of the opposite of the sum from b+1 to a-1 if a>b+1 or 0 if a=b+1) or the discrete primitive or sum of the elements of a list or a sequence. - See also: 1/ + - Ex1:sum(1/n^2,n,1,17) - Ex2:sum(1/n^2,n=1..17) - Ex3:sum(1/n^2,n,17,1) - Ex4:sum(1/n^2,n=17..1) - Ex5:sum(1/n^2,n,17,1,1) - Ex6:sum(1/n^2,n,1,17,2) - Ex7:sum(1,2,3,4) - Ex8:sum([[1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,9]]) - Ex9:sum(1/(x*(x+1)),x) - Ex10:sum(cos(n*x),n) - ''' - return GiacMethods['sum'](self, *args) - - def sum_riemann(self, *args): - r'''From Giac's documentation: - Help for sum_riemann: - sum_riemann(Expr(Xpr),Lst(var1,var2)) - Returns an equivalent when var1=+infinity of the sum of Xpr(var1,var2) for var2 from 1 to var1 when the sum is a Riemann sum. - See also: 1/ - Ex1:sum_riemann(1/(n+k),[n,k]) - Ex2:sum_riemann(n/(n^2+k),[n,k]) - Ex3:sum_riemann(n/(n^2+k^2),[n,k]) - ''' - return GiacMethods['sum_riemann'](self, *args) - - def suppress(self, *args): - r'''From Giac's documentation: - Help for suppress: - suppress(Vect(L)||Str(l),Intg(n)) - Returns L without the element of index n; L:=suppress(L,n) or L.suppress(n). - See also: 1/ tail 2/ mid 3/ remove 4/ insert - Ex1:suppress([0,1,2,3],2) - Ex2:suppress("0123",2) - Ex3: L:=[0,1,2,3];L:=suppress(L,2) - Ex4: L:=[0,1,2,3];L.suppress(2) - ''' - return GiacMethods['suppress'](self, *args) - - def surd(self, *args): - r'''From Giac's documentation: - Help for surd: - surd(Expr,Intg(n)) - Power 1/n. - See also: 1/ sqrt 2/ ^ - Ex1:surd(8,3) - Ex2:surd(-8,3) - ''' - return GiacMethods['surd'](self, *args) - - def svd(self, *args): - r'''From Giac's documentation: - Help for svd: - svd(Mtrx(A)) - For a square numerical real matrix A, returns U orthogonal, S vector of singular values, Q orthogonal such that A=U*diag(S)*tran(Q). - Ex1:svd([[1,2],[3,4]]) - ''' - return GiacMethods['svd'](self, *args) - - def swapcol(self, *args): - r'''From Giac's documentation: - Help for swapcol: - swapcol(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by swapping the n1-th column and the n2-th column. - See also: 1/ rowSwap - Ex1:swapcol([[1,2],[3,4],[5,6]],0,1) - ''' - return GiacMethods['swapcol'](self, *args) - - def swaprow(self, *args): - r'''From Giac's documentation: - Help for swaprow: - swaprow(Mtrx(A),Intg(n1),Intg(n2)) - Returns the matrix obtained from A by swapping the n1-th row and the n2-th row. - See also: 1/ rowAdd 2/ colSwap - Ex1:swaprow([[1,2],[3,4],[5,6]],1,2) - ''' - return GiacMethods['swaprow'](self, *args) - - def switch_axes(self, *args): - r'''From Giac's documentation: - Help for switch_axes: - switch_axes([Intg(0 or 1)]) - switch_axes() puts or erases the axes of the graphic-screen. - See also: 1/ gl_showaxes 2/ axes - Ex1:switch_axes() - Ex2:switch_axes(0) - Ex3:switch_axes(1) - ''' - return GiacMethods['switch_axes'](self, *args) - - def sylvester(self, *args): - r'''From Giac's documentation: - Help for sylvester: - sylvester(Poly,Poly,Var) - Sylvester matrix of two polynomials. - See also: 1/ resultant - Ex1:sylvester(x^2-1,x^3-1,x) - Ex2:sylvester(x^3-p*x+q,3*x^2-p,x) - ''' - return GiacMethods['sylvester'](self, *args) - - def symb2poly(self, *args): - r'''From Giac's documentation: - Help for symb2poly: - symb2poly(Expr, LstVar or [Var]) - Returns the coefficients of a polynomial with respect to the 2nd argument or if the second argument is a list the internal format of the polynomial. - See also: 1/ poly2symb 2/ r2e - Ex1:symb2poly(x*3+2.1) - Ex2:symb2poly(3*x*y+2*y+1,y) - Ex3:symb2poly(3*x*y+2*y+1,x,y) - Ex4:symb2poly(3*x*y+2*y+1,[x,y]) - Ex5:symb2poly(-x^4+x*3*y+2+y^2*z,[x,y,z]) - Ex6:symb2poly(-x^4+x*3*y+2+y^2*z,[x,y,z]) - ''' - return GiacMethods['symb2poly'](self, *args) - - def symbol(self, *args): - r'''From Giac's documentation: - Help for symbol: - symbol(Opt) - DOM_SYMBOLIC or symbol is the type of a symbolic variable, as returned by the type command. It is also an option of the assume command. - See also: 1/ type 2/ assume 3/ DOM_INT 4/ DOM_FLOAT - Ex1: assume(a,symbol) - Ex2: assume(a,DOM_SYMBOLIC) - Ex3: a:=sqrt(2);type(a) - Ex4: type(2x+1) - ''' - return GiacMethods['symbol'](self, *args) - - def syst2mat(self, *args): - r'''From Giac's documentation: - Help for syst2mat: - syst2mat(LstLinEq,LstVar) - Returns the matrix M=A|(-b) associate to the system Y=AX+b. - See also: 1/ linsolve 2/ rref - Ex1:syst2mat([x-y=1,x+2*y],[x,y]) - ''' - return GiacMethods['syst2mat'](self, *args) - - def tCollect(self, *args): - r'''From Giac's documentation: - Help for tCollect: - tCollect(Expr) - Collects trigonometric expressions. - See also: 1/ texpand 2/ tlin - Ex1:tCollect(sin(x)+cos(x)) - ''' - return GiacMethods['tCollect'](self, *args) - - def tExpand(self, *args): - r'''From Giac's documentation: - Help for tExpand: - tExpand(Expr) - Expands transcendental expressions. - See also: 1/ tcollect 2/ tlin 3/ lin 4/ trigexpand - Ex1:tExpand(sin(2*x)+exp(x+y)) - Ex2:tExpand(cos(x+y)) - Ex3:tExpand(cos(3*x)) - ''' - return GiacMethods['tExpand'](self, *args) - - def table(self, *args): - r'''From Giac's documentation: - Help for table: - table(SeqEqual(index=value)) - Defines an array where the indices are strings or real numbers or defines a table with a matrix. - See also: 1/ matrix 2/ convert 3/ array - Ex1:table(3=-10,"a"=10,"b"=20,"c"=30,"d"=40) - Ex2: A:=[[0,1],[2,3]];table(A) - Ex3: B:=table([1,2]=12,[2,5]=25);matrix(B) - ''' - return GiacMethods['table'](self, *args) - - def tablefunc(self, *args): - r'''From Giac's documentation: - Help for tablefunc: - tablefunc(Expr,Var) - Table of values of a function : you must be in a spreadsheet. - See also: 1/ tabvar 2/ tableseq - Ex1:tablefunc(sin(x),x) - Ex2:tablefunc(x^2-x-2,x) - ''' - return GiacMethods['tablefunc'](self, *args) - - def tableseq(self, *args): - r'''From Giac's documentation: - Help for tableseq: - tableseq(Expr,(Var or LstVar),(InitVal or LstInitVal)) - Table of values of a sequence (in a spreadsheet.) - See also: 1/ tablefunc - Ex1:tableseq(cos(x),x,0.0) - Ex2:tableseq(x+y,[x,y],[1,1]) - ''' - return GiacMethods['tableseq'](self, *args) - - def tabsign(self, *args): - r'''From Giac's documentation: - Help for tabsign: - tabsign(Expr,Var) - Table of signs of a function. - See also: 1/ tablefunc 2/ tabvar - Ex1:tabsign(x^2-x,x) - Ex2:tabsign(x^2,x,-3,5) - Ex3:tabsign(sin(x),x) - ''' - return GiacMethods['tabsign'](self, *args) - - def tabvar(self, *args): - r'''From Giac's documentation: - Help for tabvar: - tabvar(Expr,Var) - Table of variations of a function with its graph on DispG. - See also: 1/ tablefunc 2/ tabsign - Ex1:tabvar(sin(x),x) - Ex2:tabvar(1/ln(x^2-1),x,diff=1) - Ex3:tabvar(x^2+x+1,x) - Ex4:tabvar(x^2,x,-3,5) - Ex5:tabvar([sin(2t),cos(3t)]) - ''' - return GiacMethods['tabvar'](self, *args) - - def tail(self, *args): - r'''From Giac's documentation: - Help for tail: - tail(Lst or Seq or Str) - Returns the list (or sequence or string) without its first element. - See also: 1/ head 2/ mid 3/ left 4/ right 5/ back - Ex1:tail([3,2,4,1,0]) - Ex2:tail(3,2,4,1,0) - Ex3:tail("bonjour") - ''' - return GiacMethods['tail'](self, *args) - - def tan(self, *args): - r'''From Giac's documentation: - Help for tan: - tan(Expr) - Tangent or option of the convert or convertir command (id halftan). - See also: 1/ atan or Opt 2/ convert 3/ halftan - Ex1:tan(0) - Ex2:tan(pi/4) - Ex3: convert(tan(x),tan) - ''' - return GiacMethods['tan'](self, *args) - - def tan2cossin2(self, *args): - r'''From Giac's documentation: - Help for tan2cossin2: - tan2cossin2(Expr) - Replaces tan(x) by (1-cos(2*x))/sin(2*x) in the argument. - See also: 1/ tan2sincos2 2/ tan2sincos 3/ sin2costan 4/ cos2sintan - Ex1:tan2cossin2(tan(x)) - ''' - return GiacMethods['tan2cossin2'](self, *args) - - def tan2sincos(self, *args): - r'''From Giac's documentation: - Help for tan2sincos: - tan2sincos(Expr) - Replaces tan(x) by sin(x)/cos(x) in the argument. - See also: 1/ sin2costan 2/ cos2sintan 3/ tan2sincos2 4/ tan2cossin2 - Ex1:tan2sincos(tan(x)) - ''' - return GiacMethods['tan2sincos'](self, *args) - - def tan2sincos2(self, *args): - r'''From Giac's documentation: - Help for tan2sincos2: - tan2sincos2(Expr) - Replaces tan(x) by sin(2*x)/(1+cos(2*x)) in the argument. - See also: 1/ tan2cossin2 2/ tan2sincos 3/ sin2costan 4/ cos2sintan - Ex1:tan2sincos2(tan(x)) - ''' - return GiacMethods['tan2sincos2'](self, *args) - - def tangent(self, *args): - r'''From Giac's documentation: - Help for tangent: - tangent(Curve(C),Pnt(A)) - tangent(C,A) draws the tangents (line or plane) to C through A. - See also: 1/ LineTan 2/ droite_tangente - Ex1:tangent(circle(i,1+i),A) - Ex2:tangent(plotfunc(sin(x)),3*pi/4) - Ex3:tangent(plotfunc(sin(x)),point(3*pi/4+i*sqrt(2)/2)) - Ex4:tangent(plotfunc(x^2+y^2,[x,y]),[2,2]) - Ex5:tangent(plotfunc(x^2+y^2,[x,y]),point([2,2,8])) - Ex6:tangent(plotparam(3*exp(t/2)*exp(i*t),t),7) - Ex7:tangent(plotpolar(3*exp(t/2),t),7) - Ex8: equation(tangente([2*cos(t),2*sin(t),3*t],t)) - ''' - return GiacMethods['tangent'](self, *args) - - def tangente(self, *args): - r'''From Giac's documentation: - Help for tangente: - tangente(Curve(C),Pnt(A)) - tangent(C,A) draws the tangents (line or plane) to C through A. - See also: 1/ LineTan 2/ droite_tangente - Ex1:tangente(circle(i,1+i),A) - Ex2:tangente(plotfunc(sin(x)),3*pi/4) - Ex3:tangente(plotfunc(sin(x)),point(3*pi/4+i*sqrt(2)/2)) - Ex4:tangente(plotfunc(x^2+y^2,[x,y]),[2,2]) - Ex5:tangente(plotfunc(x^2+y^2,[x,y]),point([2,2,8])) - Ex6:tangente(plotparam(3*exp(t/2)*exp(i*t),t),7) - Ex7:tangente(plotpolar(3*exp(t/2),t),7) - Ex8: equation(tangente([2*cos(t),2*sin(t),3*t],t)) - ''' - return GiacMethods['tangente'](self, *args) - - def tanh(self, *args): - r'''From Giac's documentation: - Help for tanh: - tanh(Expr) - Hyperbolic tangent. - See also: 1/ atanh 2/ hyp2exp - Ex1:tanh(0) - Ex2:tanh(hyp2exp(tanh(1))) - ''' - return GiacMethods['tanh'](self, *args) - - def taux_accroissement(self, *args): - r'''From Giac's documentation: - Help for taux_accroissement: - taux_accroissement(Expr,Var,Val1,(Val1+Var or Val2)) - Returns the rate of change of an expression when the variable goes from Val1 to Val2 (by default Var=x). - See also: 1/ diff 2/ limit - Ex1:taux_accroissement(x^2,1,1+h) - Ex2:taux_accroissement(x^2,1,2) - Ex3:taux_accroissement(a^2,a,1,1+h) - ''' - return GiacMethods['taux_accroissement'](self, *args) - - def taylor(self, *args): - r'''From Giac's documentation: - Help for taylor: - taylor(Expr,[Var=limit_point],[Order]) - Series expansion at finite or infinite points (by default x=0, and relative order=5). - See also: 1/ series 2/ limit 3/ pade 4/ polynom - Ex1:taylor(sin(x)/x,x,0) - Ex2:taylor(sin(x),x=0,5,polynom) - Ex3:taylor(ln(y+y^2)-ln(y),y) - Ex4:taylor(ln(x+x^2)-ln(x),x,2) - Ex5:taylor(ln(x+x^2)-ln(x),x=0,2) - Ex6:taylor(ln(x+x^2)-ln(x),x=1,2) - Ex7:taylor((x^4+x+2)/(x^2+1),x,5) - Ex8:taylor(sin(t*x+t*y)+cos(t*x*t*y),t=0,6,polynom)(h=1) - Ex9:taylor(sin((1+h*t)*(pi/2+k*t)),t=0,3,polynom)(t=1) - Ex10:taylor((-1+k*t)^2/(1+h*t)^3,t=0,3,polynom)(t=1) - ''' - return GiacMethods['taylor'](self, *args) - - def tchebyshev1(self, *args): - r'''From Giac's documentation: - Help for tchebyshev1: - tchebyshev1(Intg(n)) - Returns the n-th Tchebyshev polynomial of first kind. - See also: 1/ tchebyshev2 2/ hermite - Ex1:tchebyshev1(3) - ''' - return GiacMethods['tchebyshev1'](self, *args) - - def tchebyshev2(self, *args): - r'''From Giac's documentation: - Help for tchebyshev2: - tchebyshev2(Intg(n)) - Returns the nt-h Tchebyshev polynomial of second kind. - See also: 1/ tchebyshev1 2/ hermite - Ex1:tchebyshev2(3) - ''' - return GiacMethods['tchebyshev2'](self, *args) - - def tcoeff(self, *args): - r'''From Giac's documentation: - Help for tcoeff: - tcoeff(Poly||Lst) - Returns the coefficient of the term of lowest degree of a polynomial (t=trailing). - See also: 1/ lcoeff - Ex1:tcoeff(-2*x^3+x^2+7*x) - Ex2:tcoeff([-2,1,7,0]) - ''' - return GiacMethods['tcoeff'](self, *args) - - def tcollect(self, *args): - r'''From Giac's documentation: - Help for tcollect: - tcollect(Expr) - Collects trigonometric expressions. - See also: 1/ texpand 2/ tlin - Ex1:tcollect(sin(x)+cos(x)) - ''' - return GiacMethods['tcollect'](self, *args) - - def tdeg(self, *args): - r'''From Giac's documentation: - Help for tdeg: - tdeg(Opt) - Option of the gbasis or greduce command to specify an order for monomials (complete degree then lexicographic order). - See also: 1/ gbasis 2/ greduce - ''' - return GiacMethods['tdeg'](self, *args) - - def tensor_product(self, *args): - r'''From Giac's documentation: - Help for tensor_product: - tensor_product(Seq(G1,G2,..)) - Returns the tensor product of the input graphs G1, G2, ... with vertices labelled as "u:v:..." where u, v, ... are vertices from G1, G2, ..., respectively. - See also: 1/ cartesian_product - Ex1:tensor_product(graph(trail(1,2,3,4,5,2)),star_graph(3)) - ''' - return GiacMethods['tensor_product'](self, *args) - - def tetrahedron(self, *args): - r'''From Giac's documentation: - Help for tetrahedron: - tetrahedron(Pnt(A),Pnt(B),Pnt(C),[Pnt(D)]) - Draws the regular direct pyramid ABCD with vertices A,B and a face in the plane (A,B,C) when there is 3 arguments and the pyramid ABCD when there are 4 arguments. - See also: 1/ cube 2/ cylinder 3/ icosahedron 4/ dodecahedron 5/ octahedron - Ex1:tetrahedron([0,0,0],[3,0,0],[0,1,0]) - Ex2:tetrahedron([0,0,0],[3,0,0],[0,3,0],[0,0,4]) - ''' - return GiacMethods['tetrahedron'](self, *args) - - def texpand(self, *args): - r'''From Giac's documentation: - Help for texpand: - texpand(Expr) - Expands transcendental expressions. - See also: 1/ tcollect 2/ tlin 3/ lin 4/ trigexpand - Ex1:texpand(sin(2*x)+exp(x+y)) - Ex2:texpand(cos(x+y)) - Ex3:texpand(cos(3*x)) - ''' - return GiacMethods['texpand'](self, *args) - - def thickness(self, *args): - r'''From Giac's documentation: - Help for thickness: - thickness(Opt) - Option (Maple compatibility) of a graphic command to define the thickness of lines. - See also: 1/ line_width - Ex1: segment(0,point(1,1),thickness=5) - Ex2: segment(0,point(1,1),epaisseur=5) - ''' - return GiacMethods['thickness'](self, *args) - - def threshold(self, *args): - r'''From Giac's documentation: - Help for threshold: - threshold(Lst,Real(bound)[=Expr(repl)] or Lst[Real(lower)[=Expr(rl)],Real(upper)[=Expr(ru)]],[Fnc(compare)],[abs[=true or false]]) - Performs thresholding operations on a list of real or complex numbers. - See also: 1/ max 2/ min - Ex1:threshold([1,3,2,4,5,4,3,2,3,1],3,'>=') - Ex2:threshold([-10,-5,0,5,10],7=a,abs=true) - ''' - return GiacMethods['threshold'](self, *args) - - def throw(self, *args): - r'''From Giac's documentation: - Help for throw: - throw(Str) - Generates the display of an error in a program. - See also: 1/ try 2/ catch - Ex1:throw("Argument should be integer") - Ex2:throw("je provoque une erreur") - ''' - return GiacMethods['throw'](self, *args) - - def title(self, *args): - r'''From Giac's documentation: - Help for title: - title(Opt) - Global option of a graphic command to put a title in a graphic. - See also: 1/ line_width - Ex1: title="segment";segment(0,point(1,1),epaisseur=5) - Ex2: titre="segment";segment(0,point(1,1),epaisseur=5) - ''' - return GiacMethods['title'](self, *args) - - def titre(self, *args): - r'''From Giac's documentation: - Help for titre: - titre(Opt) - Global option of a graphic command to put a title in a graphic. - See also: 1/ line_width - Ex1: title="segment";segment(0,point(1,1),epaisseur=5) - Ex2: titre="segment";segment(0,point(1,1),epaisseur=5) - ''' - return GiacMethods['titre'](self, *args) - - def tlin(self, *args): - r'''From Giac's documentation: - Help for tlin: - tlin(ExprTrig) - Trigonometric linearization. - See also: 1/ texpand 2/ lin - Ex1:tlin(sin(x)^3) - Ex2:tlin(cos(x)*cos(y)) - ''' - return GiacMethods['tlin'](self, *args) - - def tonnetz(self, *args): - r'''From Giac's documentation: - Help for tonnetz: - tonnetz(Intg(a),Intg(b),Intg(c),[Intg(d)]) - Returns the graph corresponding to the [a,b,c] resp. [a,b,c,d] tone network (tonnetz) used in neo-Riemannian music theory. - See also: 1/ is_regular 2/ clique_stats - Ex1:tonnetz(3,4,5) - Ex2:tonnetz(2,3,3,4) - ''' - return GiacMethods['tonnetz'](self, *args) - - def topologic_sort(self, *args): - r'''From Giac's documentation: - Help for topologic_sort: - topologic_sort(Graph(G)) - Returns the list of vertices sorted according to the topological ordering in the directed acyclic graph G. - See also: 1/ digraph 2/ is_acyclic - Ex1:topologic_sort(digraph(%{[c,a],[c,b],[c,d],[a,d],[b,d],[a,b]%})) - ''' - return GiacMethods['topologic_sort'](self, *args) - - def topological_sort(self, *args): - r'''From Giac's documentation: - Help for topological_sort: - topological_sort(Graph(G)) - Returns the list of vertices sorted according to the topological ordering in the directed acyclic graph G. - See also: 1/ digraph 2/ is_acyclic - Ex1:topological_sort(digraph(%{[c,a],[c,b],[c,d],[a,d],[b,d],[a,b]%})) - ''' - return GiacMethods['topological_sort'](self, *args) - - def torus_grid_graph(self, *args): - r'''From Giac's documentation: - Help for torus_grid_graph: - torus_grid_graph(Intg(m),Intg(n)) - Returns a torus grid graph on m*n vertices, where m,n>=3. - See also: 1/ grid_graph - Ex1:torus_grid_graph(6,12) - ''' - return GiacMethods['torus_grid_graph'](self, *args) - - def total_degree(self, *args): - r'''From Giac's documentation: - Help for total_degree: - total_degree(Poly(P),Lst(Vars)) - Total degree of the polynomial P with respect to the second argument. - See also: 1/ valuation 2/ size 3/ degree - Ex1:total_degree(x^3*y+x*y,[x,y]) - ''' - return GiacMethods['total_degree'](self, *args) - - def tourne_droite(self, *args): - r'''From Giac's documentation: - Help for tourne_droite: - tourne_droite(NULL or Real(n)) - The turtle turns right by n degrees (by default n=90). - See also: 1/ tourne_gauche 2/ pas_de_cote - Ex1: tourne_droite 60 - Ex2:tourne_droite(60) - ''' - return GiacMethods['tourne_droite'](self, *args) - - def tourne_gauche(self, *args): - r'''From Giac's documentation: - Help for tourne_gauche: - tourne_gauche(NULL or Real(n)) - The turtle turns left by n degrees (by defaults n=90). - See also: 1/ tourne_droite - Ex1: tourne_gauche 60 - Ex2:tourne_gauche(60) - ''' - return GiacMethods['tourne_gauche'](self, *args) - - def tpsolve(self, *args): - r'''From Giac's documentation: - Help for tpsolve: - tpsolve(supply,demand,cost_matrix) - Solves a transportation problem using MODI method. - Ex1:tpsolve([12,17,11],[10,10,10,10],[[500,750,300,450],[650,800,400,600],[400,700,500,550]]) - Ex2:tpsolve([7,10,8,8,9,6],[9,6,12,8,10],[[36,40,32,43,29],[28,27,29,40,38],[34,35,41,29,31],[41,42,35,27,36],[25,28,40,34,38],[31,30,43,38,40]]) - Ex3:tpsolve([95,70,165,165],[195,150,30,45,75],[[15,M,45,M,0],[12,40,M,M,0],[0,15,25,25,0],[M,0,M,12,0]]) - Ex4:tpsolve([1,1,1,1],[1,1,1,1],[[10,12,9,11],[5,10,7,8],[12,14,13,11],[8,15,11,9]]) - ''' - return GiacMethods['tpsolve'](self, *args) - - def trace(self, *args): - r'''From Giac's documentation: - Help for trace: - trace(Mtrx or GeoObj) - Returns the trace of a square matrix or draws the trace of a geometric object when the parameter changes (see Trace in Menu button of a geometric level and write only one instruction on each line). - See also: 1/ det 2/ lieu - Ex1:trace([[1,2,3],[1,3,6],[2,5,7]]) - Ex2:trace([[1+i,2,3],[1,3,6],[2,5,9-i]]) - Ex3: assume(a=[0.7,-5,5,0.1]);trace(point(a-i*a)) - Ex4: assume(a=[0.7,-5,5,0.1]);trace(inter_unique(droite(y=a*x+a),droite(y=2*a*x+1))) - ''' - return GiacMethods['trace'](self, *args) - - def trail(self, *args): - r'''From Giac's documentation: - Help for trail: - trail(Seq(V)) - Returns a trail of vertices from V (inert command). - See also: 1/ graph 2/ digraph - Ex1:trail(1,2,3,4,1) - ''' - return GiacMethods['trail'](self, *args) - - def trail2edges(self, *args): - r'''From Giac's documentation: - Help for trail2edges: - trail2edges(Trail(T)) - Converts a trail T to the list of its edges. - See also: 1/ subgraph 2/ trail - Ex1:trail2edges(trail(1,2,3,4,1,3)) - Ex2:trail2edges([1,2,3,4,1,3]) - ''' - return GiacMethods['trail2edges'](self, *args) - - def trames(self, *args): - r'''From Giac's documentation: - Help for trames: - trames(Opt) - Option of animate and animate3d commands to give the number of pictures. - See also: 1/ animate 2/ animate3d - Ex1: animate(sin(x*t),x=-pi..pi,t=-3..3,frames=30) - Ex2: animate3d(x^2+t*y^2,[x=-2..2,y=-2..2],t=-3..3,frames=10) - ''' - return GiacMethods['trames'](self, *args) - - def tran(self, *args): - r'''From Giac's documentation: - Help for tran: - tran(Mtrx) - Transposes a matrix (without conjugation). - See also: 1/ conj 2/ trn - Ex1:tran([[1,2,3],[1,3,6],[2,5,7]]) - Ex2:tran([[1+i,2,3],[1,3,6],[2,5,9-i]]) - Ex3:tran(conj([[1+i,2,3],[1,3,6],[2,5,9-i]])) - ''' - return GiacMethods['tran'](self, *args) - - def transitive_closure(self, *args): - r'''From Giac's documentation: - Help for transitive_closure: - transitive_closure(Graph(G),[weighted[=true||false]]) - Returns the [weighted, by default false] transitive closure of G. - See also: 1/ allpairs_distance 2/ is_connected 3/ shortest_path 4/ vertex_distance - Ex1:transitive_closure(digraph([1,2,3,4,5,6],%{[1,2],[2,3],[2,4],[4,5],[3,5]%}),weighted) - ''' - return GiacMethods['transitive_closure'](self, *args) - - def translation(self, *args): - r'''From Giac's documentation: - Help for translation: - translation(Vect, Pnt(C)) - translation(B-A,C) (resp translation([a,b,c],C)) is the translation of C in the translation of vector AB (resp [a,b,c]). - See also: 1/ rotation 2/ reflection - Ex1:translation(1+i,i) - Ex2:translation([1,1,1],point([1,2,3])) - Ex3: t:=translation(1+i);t(i) - Ex4: t:=translation([1,1,1]);t(point([1,2,3])) - ''' - return GiacMethods['translation'](self, *args) - - def transpose(self, *args): - r'''From Giac's documentation: - Help for transpose: - transpose(Mtrx) - Transposes a matrix (without conjugation). - See also: 1/ conj 2/ trn - Ex1:transpose([[1,2,3],[1,3,6],[2,5,7]]) - Ex2:transpose([[1+i,2,3],[1,3,6],[2,5,9-i]]) - Ex3:transpose(conj([[1+i,2,3],[1,3,6],[2,5,9-i]])) - ''' - return GiacMethods['transpose'](self, *args) - - def trapeze(self, *args): - r'''From Giac's documentation: - Help for trapeze: - trapeze(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['trapeze'](self, *args) - - def trapezoid(self, *args): - r'''From Giac's documentation: - Help for trapezoid: - trapezoid(Opt) - Option of the plotarea command and of the area command. - See also: 1/ plotarea 2/ area - Ex1: plotarea(x^2,x=0..1,5,trapezoid) - Ex2: plotarea(x^2,x=0..1,5,middle_point) - Ex3: plotarea(x^2,x=0..1,5,right_rectangle) - Ex4: plotarea(x^2,x=0..1,5,left_rectangle) - Ex5: area(x^2,x=0..1,5,middle_point) - Ex6: area(x^2,x=0..1,5,trapezoid) - ''' - return GiacMethods['trapezoid'](self, *args) - - def traveling_salesman(self, *args): - r'''From Giac's documentation: - Help for traveling_salesman: - traveling_salesman(Graph(G),[Mtrx(M)],[opts]) - Returns a Hamiltonian cycle in an unweighted graph G or solves traveling salesman problem if G is weighted (matrix M can be used for edge weights), returning a sequence containing the minimal cost and the corresponding Hamiltonian cycle. - See also: 1/ is_hamiltonian - Ex1:traveling_salesman(hypercube_graph(5)) - Ex2:traveling_salesman(digraph(%{[[1,2],1],[[1,3],2],[[2,3],2],[[2,4],3],[[3,2],3],[[3,4],2],[[4,1],1]%})) - Ex3: M:=randmatrix(4,4,99); traveling_salesman(graph("tetrahedron"),M) - Ex4: G:=set_vertex_positions(complete_graph(42),[randvector(2,1000)$(k=1..42)]); traveling_salesman(G,vertex_distance) - Ex5: G:=set_vertex_positions(complete_graph(120),[randvector(2,1000)$(k=1..120)]); c,T:=traveling_salesman(G,vertex_distance,approx) - ''' - return GiacMethods['traveling_salesman'](self, *args) - - def tree(self, *args): - r'''From Giac's documentation: - Help for tree: - tree(Opt) - Option for the draw_graph command. - See also: 1/ planar 2/ spring 3/ draw_graph - ''' - return GiacMethods['tree'](self, *args) - - def tree_height(self, *args): - r'''From Giac's documentation: - Help for tree_height: - tree_height(Graph(T),Vrtx(r)) - Returns the height of the tree graph T with r as the root node. - See also: 1/ is_tree 2/ random_tree - Ex1:tree_height(graph(%{[1,2],[2,3],[2,4],[4,5]%}),1) - ''' - return GiacMethods['tree_height'](self, *args) - - def tri(self, *args): - r'''From Giac's documentation: - Help for tri: - tri(Expr(x)) - Returns the value of the triangle function at x. - See also: 1/ rect 2/ Heaviside - Ex1:tri(x-1) - ''' - return GiacMethods['tri'](self, *args) - - def triangle(self, *args): - r'''From Giac's documentation: - Help for triangle: - triangle((Pnt or Cplx),(Pnt or Cplx),(Pnt or Cplx)) - triangle(A,B,C) draws the triangle ABC. - See also: 1/ equilateral_triangle 2/ isosceles_triangle 3/ right_triangle - Ex1:triangle(point(1+i),1,0) - Ex2:triangle(0,1,1+i) - Ex3:triangle(point(0,0,0),point(3,3,3),point(0,3,3)) - ''' - return GiacMethods['triangle'](self, *args) - - def triangle_paper(self, *args): - r'''From Giac's documentation: - Help for triangle_paper: - triangle_paper(Real(ux),Real(t),Real(uy),[x=xmin..xmax,y=ymin..ymax]) - Draws in the rectangle [xmin..xmax]*[ymin..ymax], the lines of the grid formed by the lines y=n*uy, the lines, the lines x=n*ux at an angle of t!=0, and the lines ux*y+uy*x=n*ux*uy. - Ex1:triangle_paper(1,pi/3,sqrt(3)/2) - Ex2:triangle_paper(1,pi/3,sqrt(3)/2,x=-1..4,y=-2..2) - Ex3:triangle_paper(papier_triangule(1,pi/3,sqrt(3)/2,x=-2..6,y=-4*sqrt(3)..4*sqrt(3))) - Ex4:triangle_paper(0.5,3*pi/4,0.5) - ''' - return GiacMethods['triangle_paper'](self, *args) - - def triangle_plein(self, *args): - r'''From Giac's documentation: - Help for triangle_plein: - triangle_plein(Real(a),[Real(b)],[Real(t)]) - Draws a full direct triangle with sides a,b and with angle t, from the turtle position (by default t=90 or (b=a and t=60)). - See also: 1/ rectangle_plein - Ex1: triangle_plein 30 - Ex2:triangle_plein(30) - Ex3:triangle_plein(30,40) - Ex4:triangle_plein(30,40,60) - ''' - return GiacMethods['triangle_plein'](self, *args) - - def triangle_point(self, *args): - r'''From Giac's documentation: - Help for triangle_point: - triangle_point(Opt) - Option of the display command for a point. - See also: 1/ display - Ex1: F:=display(point(2+1.5*i),point_point) - Ex2: F:=display(point(2+1.5*i),rhombus_point) - ''' - return GiacMethods['triangle_point'](self, *args) - - def triangle_window(self, *args): - r'''From Giac's documentation: - Help for triangle_window: - triangle_window(Lst,[Intg(d)],[Interval(n1..n2)]) - Applies the triangular windowing function with parameter d in {-1,0,1} (by default L=0) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ bartlett_hann_window 13/ tukey_window 14/ welch_window - Ex1: scatterplot(triangle_window(randvector(1000,0..1),1)) - ''' - return GiacMethods['triangle_window'](self, *args) - - def trig2exp(self, *args): - r'''From Giac's documentation: - Help for trig2exp: - trig2exp(Expr) - Replaces in the argument the trigonometric functions by complex exponentials without linearisation. - See also: 1/ exp2trig 2/ atrig2ln - Ex1:trig2exp(sin(x)) - ''' - return GiacMethods['trig2exp'](self, *args) - - def trigcos(self, *args): - r'''From Giac's documentation: - Help for trigcos: - trigcos(Expr) - Simplifies the argument with the formulas sin(x)^2+cos(x)^2=1 and tan(x)=sin(x)/cos(x) privileging cosine. - See also: 1/ trigsin 2/ trigtan - Ex1:trigcos(sin(x)^4+sin(x)^2) - ''' - return GiacMethods['trigcos'](self, *args) - - def trigexpand(self, *args): - r'''From Giac's documentation: - Help for trigexpand: - trigexpand(Expr) - Expands trigonometric functions. - See also: 1/ texpand 2/ lnexpand 3/ expexpand - Ex1:trigexpand(sin(3*x)) - ''' - return GiacMethods['trigexpand'](self, *args) - - def triginterp(self, *args): - r'''From Giac's documentation: - Help for triginterp: - triginterp(List,Var=xmin..xmax) - Returns a trigonometric polynomial interpolation (with respect to variable x) of the points with ordinate given in list y and the abscissa equally spaced between a and b. - See also: 1/ lagrange 2/ thiele - Ex1:triginterp([11,10,17,24,32,26,23,19],x=0..21) - Ex2:triginterp([11,10,17,24,32,26,23,19],0,21,x) - ''' - return GiacMethods['triginterp'](self, *args) - - def trigsimplify(self, *args): - r'''From Giac's documentation: - Help for trigsimplify: - trigsimplify(Expr) - Simplifies a trigonometric expression. - See also: 1/ simplify - Ex1:trigsimplify(3*sin(x)-4*sin(x)^3) - ''' - return GiacMethods['trigsimplify'](self, *args) - - def trigsin(self, *args): - r'''From Giac's documentation: - Help for trigsin: - trigsin(Expr) - Simplifies the argument with the formulas sin(x)^2+cos(x)^2=1 and tan(x)=sin(x)/cos(x) privileging sine. - See also: 1/ trigcos 2/ trigtan - Ex1:trigsin(cos(x)^4+sin(x)^2) - ''' - return GiacMethods['trigsin'](self, *args) - - def trigtan(self, *args): - r'''From Giac's documentation: - Help for trigtan: - trigtan(Expr) - Simplifies the argument with the formulas sin(x)^2+cos(x)^2=1 and tan(x)=sin(x)/cos(x) privileging tangent. - See also: 1/ trigsin 2/ trigcos - Ex1:trigtan(cos(x)^4+sin(x)^2) - ''' - return GiacMethods['trigtan'](self, *args) - - def trn(self, *args): - r'''From Giac's documentation: - Help for trn: - trn(Mtrx) - Returns the adjoint matrix of A =tran(conj(A)). - See also: 1/ tran 2/ conj - Ex1:trn([[1,2+i],[3,4]]) - ''' - return GiacMethods['trn'](self, *args) - - def true(self, *args): - r'''From Giac's documentation: - Help for true: - true() - Boolean equal to true or 1. - See also: 1/ false - Ex1: a:=true - ''' - return GiacMethods['true'](self, *args) - - def trunc(self, *args): - r'''From Giac's documentation: - Help for trunc: - trunc(Real||LstReal,Int(n)) - Truncates value to n decimal places (by default n=0). Accepts complex numbers.(type=DOM_COMPLEX or DOM_FLOAT). - See also: 1/ fPart 2/ floor 3/ iPart - Ex1:trunc(4.3) - Ex2:trunc(sqrt(2),3) - Ex3:trunc([4.3333,sqrt(2)]) - Ex4:trunc([4.3333,sqrt(2)],2) - Ex5:trunc(sqrt(2)+i*sqrt(5),4) - ''' - return GiacMethods['trunc'](self, *args) - - def truncate(self, *args): - r'''From Giac's documentation: - Help for truncate: - truncate(Poly(P),Intg(n)) - Truncates the polynomial P at order n. - See also: 1/ series - Ex1:truncate((x^2+x)^2,3) - ''' - return GiacMethods['truncate'](self, *args) - - def truncate_graph(self, *args): - r'''From Giac's documentation: - Help for truncate_graph: - truncate_graph(Graph(G)) - Returns the graph obtained by truncating the biconnected planar graph G. - See also: 1/ is_biconnected 2/ is_planar 3/ plane_dual - Ex1:truncate_graph(graph("tetrahedron")) - ''' - return GiacMethods['truncate_graph'](self, *args) - - def tsimplify(self, *args): - r'''From Giac's documentation: - Help for tsimplify: - tsimplify(Expr) - Lowers the number of non rational variables. - See also: 1/ simplify - Ex1:tsimplify(exp(2*x)+exp(x)) - ''' - return GiacMethods['tsimplify'](self, *args) - - def tuer(self, *args): - r'''From Giac's documentation: - Help for tuer: - tuer(NULL) - Stop step-by-step execution of a program (with debug). - See also: 1/ - Ex1:tuer() - ''' - return GiacMethods['tuer'](self, *args) - - def tukey_window(self, *args): - r'''From Giac's documentation: - Help for tukey_window: - tukey_window(Lst,[Real(a)],[Interval(n1..n2)]) - Applies the Tukey windowing function with parameter a in [0,1] (by default a=0.5) to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ bartlett_hann_window 14/ welch_window - Ex1: scatterplot(tukey_window(randvector(1000,0..1),0.4)) - ''' - return GiacMethods['tukey_window'](self, *args) - - def tutte_polynomial(self, *args): - r'''From Giac's documentation: - Help for tutte_polynomial: - tutte_polynomial(Graph(G),[Var(x),Var(y)]) - Returns the Tutte polynomial [or its value at point (x,y)] of undirected graph G. If G is weighted, all weights must be positive integers and are interpreted as edge multiplicities. - See also: 1/ chromatic_polynomial 2/ flow_polynomial 3/ reliability_polynomial 4/ delete_edge 5/ contract_edge - Ex1:tutte_polynomial(graph("tetrahedron")) - Ex2:tutte_polynomial(graph("tetrahedron"),1,1) - ''' - return GiacMethods['tutte_polynomial'](self, *args) - - def two_edge_connected_components(self, *args): - r'''From Giac's documentation: - Help for two_edge_connected_components: - two_edge_connected_components(Graph(G)) - Returns the list of two-edge-connected components of undirected graph G, each of them represented by the list of its vertices. - See also: 1/ is_two_edge_connected 2/ connected_components - Ex1:two_edge_connected_components(graph(trail(1,2,3,4,5,3,1),trail(5,6,7,8,6))) - ''' - return GiacMethods['two_edge_connected_components'](self, *args) - - def ufactor(self, *args): - r'''From Giac's documentation: - Help for ufactor: - ufactor(Unit,Unit) - Factors a unit in a unit object. - See also: 1/ convert 2/ mksa 3/ usimplify - Ex1:ufactor(100_C,1_A) - ''' - return GiacMethods['ufactor'](self, *args) - - def ugamma(self, *args): - r'''From Giac's documentation: - Help for ugamma: - ugamma(Real(a),Real(x),[1]) - Calculates ugamma function at a point (a,x):if a and x>=0 ugamma(a,x)=int(e^{-t}*t^{a-1},t=x..inf),(ugamma(a,x)+igamma(a,x)=Gamma(a)). - See also: 1/ Psi 2/ Beta 3/ Gamma 4/ igamma - Ex1:ugamma(5.0,2.0) - Ex2:ugamma(-5.1,2.1) - ''' - return GiacMethods['ugamma'](self, *args) - - def unapply(self, *args): - r'''From Giac's documentation: - Help for unapply: - unapply(Expr,Var) - Returns a function defined by an expression. - See also: 1/ apply - Ex1:unapply(2*x^2,x) - Ex2: f(x):=x*exp(x);g:=unapply(diff(f(x),x),x) - ''' - return GiacMethods['unapply'](self, *args) - - def unarchive(self, *args): - r'''From Giac's documentation: - Help for unarchive: - unarchive(Str(namefich),Seq(Var)) - Reads the value of a variable or of a list of variables which are in the file given as argument (file created with archive). - See also: 1/ archive 2/ Archive 3/ Unarchiv - Ex1:unarchive("toto") - Ex2:unarchive("aa.txt") - ''' - return GiacMethods['unarchive'](self, *args) - - def underlying_graph(self, *args): - r'''From Giac's documentation: - Help for underlying_graph: - underlying_graph(Graph(G)) - Returns the graph obtained by stripping directions and weights from arcs (pairs of arcs connecting the same vertices are merged to a single edge). - See also: 1/ is_directed 2/ is_weighted 3/ make_directed 4/ make_weighted - Ex1:underlying_graph(digraph(trail(1,2,3,4,1))) - ''' - return GiacMethods['underlying_graph'](self, *args) - - def unfactored(self, *args): - r'''From Giac's documentation: - Help for unfactored: - unfactored(Opt.) - Option of the plotimplicit command. - See also: 1/ plotimplicit - Ex1: plotimplicit(x^2+y^2-1,x,y,unfactored) - Ex2: plotimplicit(x^2+y^2-1,[x,y],unfactored) - Ex3: plotimplicit(x^2+y^2+z^2-1,x,y,z,xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - Ex4: plotimplicit(x^2+y^2+z^2-1,[x,y,z],xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - Ex5: plotimplicit(x^2+y^2+z^2-1,x=0..1,y=0..1,z=0..1,xstep=0.2,ystep=0.2,zstep=0.2,unfactored) - ''' - return GiacMethods['unfactored'](self, *args) - - def uniform(self, *args): - r'''From Giac's documentation: - Help for uniform: - uniform(Real(a),Real(b),Real(x)) - Returns the probability density at x of the uniform law on [a,b]. - See also: 1/ uniform_cdf 2/ uniform_icdf 3/ randvector 4/ ranm - Ex1:uniform(2,5,4) - Ex2:uniform(1.2,3.5,3) - Ex3: randvector(3,uniform,1.2,3.5) - Ex4: ranm(4,3,uniform,1.2,3.5) - ''' - return GiacMethods['uniform'](self, *args) - - def uniform_cdf(self, *args): - r'''From Giac's documentation: - Help for uniform_cdf: - uniform_cdf(Real(a),Real(b),Real(x0),[Real(y0)]) - Returns the probability that a uniform random variable on [a,b] is less than x0 (or between x0 and y0). - See also: 1/ uniformd 2/ uniform_icdf - Ex1:uniform_cdf(3.2,5.7,4.4) - Ex2:uniform_cdf(3.2,5.7,4.4,5.4) - ''' - return GiacMethods['uniform_cdf'](self, *args) - - def uniform_icdf(self, *args): - r'''From Giac's documentation: - Help for uniform_icdf: - uniform_icdf(Real(a),Real(b),Real(p)) - Returns h such that the probability that a uniform random variable on [a,b] is less than h is p (0<=p<=1). - See also: 1/ uniform_cdf 2/ uniformd - Ex1:uniform_icdf(4.2,10.3,0.95) - Ex2:uniform_icdf(3.2,5.7,0.48) - ''' - return GiacMethods['uniform_icdf'](self, *args) - - def uniformd(self, *args): - r'''From Giac's documentation: - Help for uniformd: - uniformd(Real(a),Real(b),Real(x)) - Returns the probability density at x of the uniform law on [a,b]. - See also: 1/ uniform_cdf 2/ uniform_icdf 3/ randvector 4/ ranm - Ex1:uniformd(2,5,4) - Ex2:uniformd(1.2,3.5,3) - Ex3: randvector(3,uniform,1.2,3.5) - Ex4: ranm(4,3,uniform,1.2,3.5) - ''' - return GiacMethods['uniformd'](self, *args) - - def uniformd_cdf(self, *args): - r'''From Giac's documentation: - Help for uniformd_cdf: - uniformd_cdf(Real(a),Real(b),Real(x0),[Real(y0)]) - Returns the probability that a uniform random variable on [a,b] is less than x0 (or between x0 and y0). - See also: 1/ uniformd 2/ uniform_icdf - Ex1:uniformd_cdf(3.2,5.7,4.4) - Ex2:uniformd_cdf(3.2,5.7,4.4,5.4) - ''' - return GiacMethods['uniformd_cdf'](self, *args) - - def uniformd_icdf(self, *args): - r'''From Giac's documentation: - Help for uniformd_icdf: - uniformd_icdf(Real(a),Real(b),Real(p)) - Returns h such that the probability that a uniform random variable on [a,b] is less than h is p (0<=p<=1). - See also: 1/ uniform_cdf 2/ uniformd - Ex1:uniformd_icdf(4.2,10.3,0.95) - Ex2:uniformd_icdf(3.2,5.7,0.48) - ''' - return GiacMethods['uniformd_icdf'](self, *args) - - def unitV(self, *args): - r'''From Giac's documentation: - Help for unitV: - unitV(Lst||Cplx) - Returns the vector divided by its l2norm. It is also an option for plotfield. - See also: 1/ l2norm - Ex1:unitV(3+4*i) - Ex2:unitV([3,4]) - Ex3: fieldplot(-t*y,[t,y],normalize) - Ex4: fieldplot(-t*y,[t,y],normalize,xstep=0.5,ystep=0.5) - ''' - return GiacMethods['unitV'](self, *args) - - def unquote(self, *args): - r'''From Giac's documentation: - Help for unquote: - unquote(Expr) - Evaluates a quoted expression (for example purge(c);a:=c;unquote(a):=3; put 3 in the variables a and c). - See also: 1/ quote - Ex1:unquote(a) - ''' - return GiacMethods['unquote'](self, *args) - - def upper(self, *args): - r'''From Giac's documentation: - Help for upper: - upper(Mtrx||Strng) - Returns the upper triangular matrix (over the diagonal, included) or writes a string in uppercase. - See also: 1/ diag 2/ lower - Ex1:upper([[1,2,3],[4,5,6],[7,8,9]]) - Ex2:upper("hello") - ''' - return GiacMethods['upper'](self, *args) - - def user_operator(self, *args): - r'''From Giac's documentation: - Help for user_operator: - user_operator(Str(R),Fnc(f),Opt(Binary||Unary||Delete)) - Defines a binary operator and returns 0 (failure) or 1(success). - See also: 1/ - Ex1:user_operator("R",(x,y)->x*y+x+y,Binary) - Ex2:user_operator("R",(x,y)->x*y+x+y,Delete) - ''' - return GiacMethods['user_operator'](self, *args) - - def usimplify(self, *args): - r'''From Giac's documentation: - Help for usimplify: - usimplify(Unit) - Simplifies a unit in a unit object. - See also: 1/ convert 2/ mksa 3/ ufactor - Ex1:usimplify(100_(W*s)) - ''' - return GiacMethods['usimplify'](self, *args) - - def valuation(self, *args): - r'''From Giac's documentation: - Help for valuation: - valuation(Poly(P)) - Returns the valuation (degree of the term of lowest degree) of the polynomial P. - See also: 1/ degree 2/ tcoeff - Ex1:valuation(x^4+x^3) - Ex2:valuation([1,1,0,0,0]) - Ex3:valuation(x^5+3*x^2) - Ex4:valuation([5,0,0,3,0,0]) - ''' - return GiacMethods['valuation'](self, *args) - - def vandermonde(self, *args): - r'''From Giac's documentation: - Help for vandermonde: - vandermonde(Vect(V)) - Returns the Vandermonde matrix=[V^0,V^1,..]. - See also: 1/ det - Ex1:vandermonde([1,2,a]) - ''' - return GiacMethods['vandermonde'](self, *args) - - def variables_are_files(self, *args): - r'''From Giac's documentation: - Help for variables_are_files: - variables_are_files(:=Intg(0 or 1)) - Pseudo-variable to specify if you want to save the variables as file "nameofthevariable.cas". - See also: 1/ cas_setup - Ex1: variables_are_files:=1 - Ex2: variables_are_files:=0 - ''' - return GiacMethods['variables_are_files'](self, *args) - - def variance(self, *args): - r'''From Giac's documentation: - Help for variance: - variance(Lst||Mtrx,[Lst]) - Returns the variance of a list with the second argument as weights or the list of variances of the columns of a matrix. - See also: 1/ stddev 2/ mean - Ex1:variance([3,4,2]) - Ex2:variance([1,2,3],[1,2,1]) - Ex3:variance([[1,2,3],[5,6,7]]) - ''' - return GiacMethods['variance'](self, *args) - - def version(self, *args): - r'''From Giac's documentation: - Help for version: - version(NULL) - Returns the giac version number; for example, you are using : giac 0.4.0 - See also: 1/ - Ex1:version() - ''' - return GiacMethods['version'](self, *args) - - def vertex_connectivity(self, *args): - r'''From Giac's documentation: - Help for vertex_connectivity: - vertex_connectivity(Graph(G)) - Returns the largest integer k such that undirected connected graph G remains connected when fewer than k vertices are removed. - See also: 1/ edge_connectivity 2/ is_connected - Ex1:vertex_connectivity(graph("petersen")) - Ex2:vertex_connectivity(graph("clebsch")) - Ex3:vertex_connectivity(complete_graph(5)) - ''' - return GiacMethods['vertex_connectivity'](self, *args) - - def vertex_degree(self, *args): - r'''From Giac's documentation: - Help for vertex_degree: - vertex_degree(Graph(G),Vrtx(v)) - Returns the degree of the vertex v in G (i.e. the number of edges incident to v). - See also: 1/ degree_sequence 2/ is_regular 3/ maximum_degree 4/ minimum_degree 5/ vertex_in_degree 6/ vertex_out_degree - Ex1:vertex_degree(digraph(trail(1,2,3,4,2)),2) - ''' - return GiacMethods['vertex_degree'](self, *args) - - def vertex_distance(self, *args): - r'''From Giac's documentation: - Help for vertex_distance: - vertex_distance(Graph(G),Vrtx(s),Vrtx(t)||Lst(T)) - Returns the number of edges in the shortest path from vertex s to vertex t in G. If such path does not exist, returns +infinity. For vector T of vertices from G returns the list of distances from s to each vertex t in T. - See also: 1/ allpairs_distance 2/ graph_diameter 3/ dijkstra 4/ shortest_path - Ex1:vertex_distance(graph("petersen"),1,4) - Ex2:vertex_distance(graph("petersen"),1,[2,4]) - ''' - return GiacMethods['vertex_distance'](self, *args) - - def vertex_in_degree(self, *args): - r'''From Giac's documentation: - Help for vertex_in_degree: - vertex_in_degree(Graph(G),Vrtx(v)) - Returns the number of arcs in G entering the vertex v. - See also: 1/ degree_sequence 2/ is_regular 3/ maximum_degree 4/ minimum_degree 5/ vertex_degree 6/ vertex_out_degree - Ex1:vertex_in_degree(digraph(trail(1,2,3,4,2)),2) - ''' - return GiacMethods['vertex_in_degree'](self, *args) - - def vertex_out_degree(self, *args): - r'''From Giac's documentation: - Help for vertex_out_degree: - vertex_out_degree(Graph(G),Vrtx(v)) - Returns the number of arcs in G emanating from the vertex v. - See also: 1/ degree_sequence 2/ is_regular 3/ maximum_degree 4/ minimum_degree 5/ vertex_degree 6/ vertex_in_degree - Ex1:vertex_out_degree(digraph(trail(1,2,3,4,2)),2) - ''' - return GiacMethods['vertex_out_degree'](self, *args) - - def vertices(self, *args): - r'''From Giac's documentation: - Help for vertices: - vertices(Polygon or Polyedr(P)) - Returns the list of the vertices of the polygon or polyhedron P. - See also: 1/ isosceles_triangle 2/ polyhedron - Ex1:vertices(isosceles_triangle(0,1,pi/4)) - Ex2:vertices(polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6])) - Ex3:vertices(isosceles_triangle(0,1,pi/4))[2] - ''' - return GiacMethods['vertices'](self, *args) - - def vertices_abc(self, *args): - r'''From Giac's documentation: - Help for vertices_abc: - vertices_abc(Polygon or Polyedr(P)) - Returns the list of the vertices of the polygon or polyhedron P. - See also: 1/ isosceles_triangle 2/ polyhedron - Ex1:vertices_abc(isosceles_triangle(0,1,pi/4)) - Ex2:vertices_abc(polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6])) - Ex3:vertices_abc(isosceles_triangle(0,1,pi/4))[2] - ''' - return GiacMethods['vertices_abc'](self, *args) - - def vertices_abca(self, *args): - r'''From Giac's documentation: - Help for vertices_abca: - vertices_abca(Polygon or Polyedr(P)) - Returns the closed list [A,B,...A] of the vertices of the polygon or polyhedron P. - See also: 1/ isosceles_triangle 2/ polyhedron - Ex1:vertices_abca(isosceles_triangle(0,1,pi/4)) - Ex2:vertices_abca(polyhedron([0,0,0],[0,5,0],[0,0,5],[1,2,6])) - Ex3:vertices_abca(isosceles_triangle(0,1,pi/4))[2] - ''' - return GiacMethods['vertices_abca'](self, *args) - - def vpotential(self, *args): - r'''From Giac's documentation: - Help for vpotential: - vpotential(Vect(V),LstVar) - Returns U such that curl(U)=V. - See also: 1/ curl 2/ potential - Ex1:vpotential([2*x*y+3,x^2-4*z,-2*y*z],[x,y,z]) - ''' - return GiacMethods['vpotential'](self, *args) - - def web_graph(self, *args): - r'''From Giac's documentation: - Help for web_graph: - web_graph(Intg(a),Intg(b)) - Returns a web graph on a*b vertices, where a>=3 and b>=2. - See also: 1/ prism_graph 2/ wheel_graph - Ex1:web_graph(5,3) - ''' - return GiacMethods['web_graph'](self, *args) - - def weibull(self, *args): - r'''From Giac's documentation: - Help for weibull: - weibull(Real(k),Real(lambda),Real(theta),Real(x)) - Returns the density of probability at x of the Weibull law with parameters k, lambda, theta (by default theta=0). - See also: 1/ weibull_cdf 2/ weibull_icdf - Ex1:weibull(2.1,1.2,1.3) - Ex2:weibull(2.1,1.2,0.0,1.3) - Ex3:weibull(2.1,1.2,0.5,1.8) - ''' - return GiacMethods['weibull'](self, *args) - - def weibull_cdf(self, *args): - r'''From Giac's documentation: - Help for weibull_cdf: - weibull_cdf(Real(k),Real(lambda),Real(theta),Real(x0)) - Returns the probability that a Weibull random variable of parameters k, lambda, theta is less than x0. - See also: 1/ weibulld 2/ weibull_icdf - Ex1:weibull_cdf(2.1,1.2,1.9) - Ex2:weibull_cdf(2.1,1.2,0.0,1.9) - Ex3:weibull_cdf(2.2,1.5,0.4,1.9) - Ex4:weibull_cdf(2.2,1.5,0.4,1.2) - Ex5:weibull_cdf(2.2,1.5,0.4,1.2,1.9) - ''' - return GiacMethods['weibull_cdf'](self, *args) - - def weibull_icdf(self, *args): - r'''From Giac's documentation: - Help for weibull_icdf: - weibull_icdf(Real(k),Real(lambda),Real(theta),Real(p)) - Returns h such that the probability that a Weibull random variable of parameters k, lambda, theta is less than h is p (0<=p<=1). - See also: 1/ weibull_cdf 2/ weibull - Ex1:weibull_icdf(4.2,1.3,0.0,0.95) - Ex2:weibull_icdf(2.2,1.5,0.4,0.632) - ''' - return GiacMethods['weibull_icdf'](self, *args) - - def weibulld(self, *args): - r'''From Giac's documentation: - Help for weibulld: - weibulld(Real(k),Real(lambda),Real(theta),Real(x)) - Returns the density of probability at x of the Weibull law with parameters k, lambda, theta (by default theta=0). - See also: 1/ weibull_cdf 2/ weibull_icdf - Ex1:weibulld(2.1,1.2,1.3) - Ex2:weibulld(2.1,1.2,0.0,1.3) - Ex3:weibulld(2.1,1.2,0.5,1.8) - ''' - return GiacMethods['weibulld'](self, *args) - - def weibulld_cdf(self, *args): - r'''From Giac's documentation: - Help for weibulld_cdf: - weibulld_cdf(Real(k),Real(lambda),Real(theta),Real(x0)) - Returns the probability that a Weibull random variable of parameters k, lambda, theta is less than x0. - See also: 1/ weibulld 2/ weibull_icdf - Ex1:weibulld_cdf(2.1,1.2,1.9) - Ex2:weibulld_cdf(2.1,1.2,0.0,1.9) - Ex3:weibulld_cdf(2.2,1.5,0.4,1.9) - Ex4:weibulld_cdf(2.2,1.5,0.4,1.2) - Ex5:weibulld_cdf(2.2,1.5,0.4,1.2,1.9) - ''' - return GiacMethods['weibulld_cdf'](self, *args) - - def weibulld_icdf(self, *args): - r'''From Giac's documentation: - Help for weibulld_icdf: - weibulld_icdf(Real(k),Real(lambda),Real(theta),Real(p)) - Returns h such that the probability that a Weibull random variable of parameters k, lambda, theta is less than h is p (0<=p<=1). - See also: 1/ weibull_cdf 2/ weibull - Ex1:weibulld_icdf(4.2,1.3,0.0,0.95) - Ex2:weibulld_icdf(2.2,1.5,0.4,0.632) - ''' - return GiacMethods['weibulld_icdf'](self, *args) - - def weibullvariate(self, *args): - r'''From Giac's documentation: - Help for weibullvariate: - weibullvariate(Real(a),Real(b)) - Returns a random real according to the Weibull distribution with parameters a>0 and b>0. - See also: 1/ rand 2/ randpoly 3/ randnorm 4/ randvector - Ex1:weibullvariate(1,2) - Ex2:weibullvariate(1.5,4) - ''' - return GiacMethods['weibullvariate'](self, *args) - - def weight_matrix(self, *args): - r'''From Giac's documentation: - Help for weight_matrix: - weight_matrix(Graph(G)) - Returns the weight matrix of G. - See also: 1/ set_edge_weight 2/ get_edge_weights 3/ is_weighted 4/ make_weighted 5/ assign_edge_weights - Ex1:weight_matrix(graph(%{[[1,2],2],[[2,3],1]%}) - ''' - return GiacMethods['weight_matrix'](self, *args) - - def weighted(self, *args): - r'''From Giac's documentation: - Help for weighted: - weighted(Opt) - Option for graph and digraph commands. - See also: 1/ directed 2/ graph 3/ digraph - ''' - return GiacMethods['weighted'](self, *args) - - def weights(self, *args): - r'''From Giac's documentation: - Help for weights: - weights(Opt) - Option for the edges command. - See also: 1/ edges - ''' - return GiacMethods['weights'](self, *args) - - def welch_window(self, *args): - r'''From Giac's documentation: - Help for welch_window: - welch_window(Lst,[Interval(n1..n2)]) - Applies the Welch windowing function to the given signal u (or to the elements with indices between n1 and n2) and returns the result in a new list. - See also: 1/ blackman_harris_window 2/ blackman_window 3/ bohman_window 4/ cosine_window 5/ gaussian_window 6/ hamming_window 7/ hann_poisson_window 8/ hann_window 9/ parzen_window 10/ poisson_window 11/ riemann_window 12/ triangle_window 13/ tukey_window 14/ bartlett_hann_window - Ex1: scatterplot(welch_window(randvector(1000,0..1))) - ''' - return GiacMethods['welch_window'](self, *args) - - def wheel_graph(self, *args): - r'''From Giac's documentation: - Help for wheel_graph: - wheel_graph(Intg(n)) - Returns a wheel graph with n+1 vertices. - See also: 1/ star_graph 2/ web_graph - Ex1:wheel_graph(5) - ''' - return GiacMethods['wheel_graph'](self, *args) - - def widget_size(self, *args): - r'''From Giac's documentation: - Help for widget_size: - widget_size(Intg(n)) - Changes the character sizes of the display on the xcas screen (size=n) and with more parameters define the general configuration. - See also: 1/ cas_setup - Ex1:widget_size(20) - Ex2:widget_size(8) - Ex3:widget_size(20,58,49,697,563,1,1,0) - ''' - return GiacMethods['widget_size'](self, *args) - - def wilcoxonp(self, *args): - r'''From Giac's documentation: - Help for wilcoxonp: - wilcoxonp(Intg,[Intg]) - Distribution of the Wilcoxon or Mann-Whitney test for one or two samples. - See also: 1/ wilcoxont 2/ wilcoxons - Ex1:wilcoxonp(4) - Ex2:wilcoxonp(7,5) - ''' - return GiacMethods['wilcoxonp'](self, *args) - - def wilcoxons(self, *args): - r'''From Giac's documentation: - Help for wilcoxons: - wilcoxons(List,List || Real) - Rank statistic of Wilcoxon or Mann-Whitney for 1 sample and one median or 2 samples. - See also: 1/ wilcoxont 2/ wilcoxonp - Ex1:wilcoxons([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , [2, 6, 10, 11, 13, 14, 15, 18, 19, 20]) - Ex2:wilcoxons([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , 10) - ''' - return GiacMethods['wilcoxons'](self, *args) - - def wilcoxont(self, *args): - r'''From Giac's documentation: - Help for wilcoxont: - wilcoxont(List,List || Real,[Func],[Real]) - Wilcoxon or Mann-Whitney test for one sample and a median or 2 samples. - See also: 1/ wilcoxonp 2/ wilcoxons 3/ studentt 4/ normalt - Ex1:wilcoxont([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , [2, 6, 10, 11, 13, 14, 15, 18, 19, 20]) - Ex2:wilcoxont([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , [2, 6, 10, 11, 13, 14, 15, 18, 19, 20],0.01) - Ex3:wilcoxont([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , 10,'>') - Ex4:wilcoxont([1, 3, 4, 5, 7, 8, 8, 12, 15, 17] , 10,'>',0.05) - ''' - return GiacMethods['wilcoxont'](self, *args) - - def writergb(self, *args): - r'''From Giac's documentation: - Help for writergb: - writergb(Str(s),Lst) - Write a PNG picture file named s either from a list [[number_channels,width,height],red,green,alpha,blue] where red,green,alpha,blue are matrices of pixels color or from a matrix of grey pixels or from 3 matrices of RGB colored pixels. - See also: 1/ readrgb - Ex1:writergb("image.png",[[255,0],[0,255]]) - Ex2:writergb("image.png",[[255,0],[0,0]],[[0,255],[0,0]],[[0,0],[255,0]]) - Ex3: a:=readrgb("rgb_image.png");writergb("brg_image.png",[a[0],a[4],a[1],a[3],a[2]]) - ''' - return GiacMethods['writergb'](self, *args) - - def writewav(self, *args): - r'''From Giac's documentation: - Help for writewav: - writewav(Str(s),Lst(l)) - Writes a WAV sound file. - See also: 1/ readwav - Ex1:writewav("la.wav",2^14*(sin(2*pi*440*soundsec(1)))) - Ex2:writewav("beep.wav",[[1,16,44100,80000],[65000$10000,0$10000,65000$10000,0$10000]]) - ''' - return GiacMethods['writewav'](self, *args) - - def xcas_mode(self, *args): - r'''From Giac's documentation: - Help for xcas_mode: - xcas_mode(Intg(0) or 1 or 2 or 3) - Switches to mode Xcas (0), Maple (1), Mupad (2), TI89 (3). - See also: 1/ python_compat - Ex1:xcas_mode(1) - Ex2:xcas_mode(0) - ''' - return GiacMethods['xcas_mode'](self, *args) - - def xml_print(self, *args): - r'''From Giac's documentation: - Help for xml_print: - xml_print(Str) - Indents a XML code given in a string (pretty print). - See also: 1/ export_mathml - Ex1:xml_print(export_mathml(a+2*b)) - ''' - return GiacMethods['xml_print'](self, *args) - - def xyztrange(self, *args): - r'''From Giac's documentation: - Help for xyztrange: - xyztrange(SeqReal) - xyztrange puts or erases the axes on the graphic-screen (cf button Cfg). - Ex1:xyztrange(-5.0,5.0,-5.0,2.0,-10.0,10.0,-1.0,6.0,-5.0,5.0,-1.2384,2.0,1,0.0,1.0) - ''' - return GiacMethods['xyztrange'](self, *args) - - def zeros(self, *args): - r'''From Giac's documentation: - Help for zeros: - zeros(Expr,[Var]) - Returns the zeros (real or complex according to the mode) of the expression (or the matrix where the lines are the solutions of the system : expression1=0,expression2=0...). - See also: 1/ - Ex1:zeros(x^2+4) - Ex2:zeros(ln(x)^2-4) - Ex3:zeros(ln(y)^2-2,y) - Ex4:zeros([x^2-1,x^2-y^2],[x,y]) - ''' - return GiacMethods['zeros'](self, *args) - - def ztrans(self, *args): - r'''From Giac's documentation: - Help for ztrans: - ztrans(Expr,[Var],[ZtransVar]) - z transform of a sequence. - See also: 1/ invztrans 2/ laplace 3/ invlaplace - Ex1:ztrans(a^x) - Ex2:ztrans(a^n,n,z) - ''' - return GiacMethods['ztrans'](self, *args) - - def type(self, *args): - r'''From Giac's documentation: - Help for type: - type(Expr) - Returns n in [1..12] that defines the type of the argument. - See also: 1/ DOM_FLOAT 2/ DOM_INT 3/ DOM_COMPLEX 4/ DOM_IDENT 5/ DOM_LIST 6/ DOM_SYMBOLIC 7/ DOM_RAT 8/ DOM_STRING 9/ DOM_FUNC 10/ subtype - Ex1:type("abc") - Ex2:type([1,2,3]) - ''' - return GiacMethods['type'](self, *args) - - def zip(self, *args): - r'''From Giac's documentation: - Help for zip: - zip(Fnc2d(f),Lst(l1),Lst(l2),[Val(default)]) - Returns a list whose j-th entry is f(l1[j],l2[j]): without default value its length is the minimum of the lengths of the two input lists and otherwise the shorter list is padded with the default value. - See also: 1/ - Ex1:zip('+',[a,b,c,d], [1,2,3,4]) - Ex2:zip('+',[a,b,c,d], [1,2,3]) - Ex3:zip('+',[a,b,c,d], [1,2,3],5) - Ex4:zip(sum,[a,b,c,d], [1,2,3,4]) - ''' - return GiacMethods['zip'](self, *args) - diff --git a/src/sage/libs/giac/giac.pxd b/src/sage/libs/giac/giac.pxd deleted file mode 100644 index d069c04a8a1..00000000000 --- a/src/sage/libs/giac/giac.pxd +++ /dev/null @@ -1,204 +0,0 @@ -# distutils: language = c++ -# **************************************************************************** -# Copyright (C) 2012, Frederic Han -# 2020, Vincent Delecroix <20100.delecroix@gmail.com> -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# https://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.libs.gmp.mpz cimport mpz_t, mpz_set -from libcpp.string cimport string - -cdef extern from "giac/giac.h" namespace "giac": - cdef cppclass context: - context() - - cdef struct ref_mpz_t: - pass - cdef struct ref_real_object: - pass - cdef struct ref_complex: - pass - cdef struct ref_identificateur: - pass - cdef struct ref_symbolic: - pass - cdef struct ref_modulo: - pass - cdef struct ref_algext: - pass - cdef struct giac_float: - pass - cdef cppclass Tref_fraction[T]: - pass - cdef cppclass Tref_tensor[T]: - pass - cdef cppclass vecteur: - vecteur(int) - vecteur() - void push_back(gen &) - int size() - cdef struct ref_vecteur: - pass - cdef struct ref_sparse_poly1: - pass - cdef struct ref_string: - pass - cdef struct ref_gen_user: - pass - cdef struct ref_gen_map: - pass - cdef struct ref_eqwdata: - pass - cdef struct ref_grob: - pass - cdef struct ref_void_pointer: - pass - - cdef cppclass gen: - gen() except + - gen(char *, context *) except + - gen(string , context *) except + - gen(int ) except + - gen(long long ) except + - gen(double ) except + - #gen(ref_mpz_t * ) except + - gen(mpz_t & ) except + - gen(void *ptr,short int subt) except + - gen(gen ) except + - gen (vecteur & v,short int s) except + - gen (ref_vecteur * vptr,short int s) except + - - mpz_t * ref_ZINTptr() except + - gen * ref_MODptr() except + - vecteur * ref_VECTptr() except + - - # - unsigned char type - signed char subtype - # (the meaning of types from dispatch.h) - # // immediate type (without mem allocation) should be < _ZINT - # _INT_= 0, // int val - # _DOUBLE_= 1, // double _DOUBLE_val - # // all type below or equal to _DOUBLE_ must be non pointers - # _ZINT= 2, // mpz_t * _ZINTptr - # _REAL= 3, // mpf_t * _REALptr - # // all type strictly below _CPLX must be real types - # _CPLX= 4, // gen * _CPLXptr - # _POLY= 5, // polynome * _POLYptr - # _IDNT= 6, // identificateur * _IDNTptr - # _VECT= 7, // vecteur * _VECTptr - # _SYMB= 8, // symbolic * _SYMBptr - # _SPOL1= 9, // sparse_poly1 * _SPOL1ptr - # _FRAC= 10, // fraction * _FRACptr - # _EXT= 11, // gen * _EXTptr - # _STRNG= 12, // string * _STRNGptr - # _FUNC= 13, // unary_fonction_ptr * _FUNCptr - # _ROOT= 14, // real_complex_rootof *_ROOTptr - # _MOD= 15, // gen * _MODptr - # _USER= 16, // gen_user * _USERptr - # _MAP=17, // map * _MAPptr - # _EQW=18, // eqwdata * _EQWptr - # _GROB=19, // grob * _GROBptr - # _POINTER_=20, // void * _POINTER_val - # _FLOAT_=21 // immediate, _FLOAT_val - - - # immediate types - int val # immediate int (type _INT_) - double _DOUBLE_val # immediate float (type _DOUBLE_) - giac_float _FLOAT_val - - # pointer types - ref_mpz_t * __ZINTptr # long int (type _ZINT) - ref_real_object * __REALptr # extended double (type _REAL) - ref_complex * __CPLXptr # complex as an gen[2] array (type _CPLX) - ref_identificateur * __IDNTptr # global name identifier (type _IDNT) - ref_symbolic * __SYMBptr # for symbolic objects (type _SYMB) - ref_modulo * __MODptr - ref_algext * __EXTptr # 2 gens for alg. extension (type ext) - # alg ext: 1st gen is a std::vector or a fraction, 2nd gen is - # a/ a std::vector, the minimal monic polynomial (the roots are permutable) - # b/ a real_complex_rootof given by it's min poly and - # c/ another type meaning that the root is expressed in terms - # of another rootof, in this case ext_reduce should be called - # For 2nd order extension, X^2=d is used if d!=1 mod 4 - # X is the positive solution - # if d=1 mod 4 the equation is X^2-X=(d-1)/4 - Tref_fraction[gen] * __FRACptr # fraction (type _FRAC) - Tref_tensor[gen] * __POLYptr # multidim. sparse polynomials (type poly) - # _VECTosite types (std::vector<>) - ref_vecteur * __VECTptr # vecteur: std::vectors & dense_POLY1 (type _VECT) - ref_sparse_poly1 * __SPOL1ptr # std::vector: sparse 1-d poly (type _SPOL1) - ref_string * __STRNGptr - unsigned _FUNC_ # ref_unary_function_ptr * __FUNCptr; - ref_gen_user * __USERptr - ref_gen_map * __MAPptr - ref_eqwdata * __EQWptr - ref_grob * __GROBptr - ref_void_pointer * __POINTERptr - - #operators - gen operator[](int i) except + - gen operator[](gen & i) except + - gen operator()(gen & i,context * contextptr) except + - gen operator()(gen & i,gen & progname,context * contextptr) except + - - gen operator+(gen & b) except + - gen operator-(gen & b) except + - gen operator*(gen & b) except + - gen operator/(gen & b) except + - - - gen GIAC_rdiv "rdiv"(gen & a,gen & b) except + # rational division - gen GIAC_eval "eval" (gen &,int , context *) except + - gen GIAC_protecteval "protecteval" (gen , int, context *) except + - gen GIAC_pow "pow"(gen & ,gen & , context * ) except + - gen GIAC_neg "operator-"(gen & ) except + - gen GIAC_pos "operator+"(gen & ) except + - gen GIAC_factor "_factor" (gen &, context *) except + - gen GIAC_factors "_factors" (gen &, context *) except + - gen GIAC_normal "normal" (gen &, context *) except + - gen GIAC_gcd "_gcd" (gen & args, context *) except + - gen GIAC_smod "_smod" (gen & args, context * ) except + - gen GIAC_mods "_mods" (gen & args, context * ) except + - gen GIAC_makemod "_makemod" (gen & , context * ) except + - string GIAC_print "print" (gen &, context *) except + - string GIAC_gen2tex "gen2tex" (gen &, context *) except + - ref_vecteur * GIAC_makenewvecteur "makenewvecteur"(gen & a,gen & b) except + - gen GIAC_size "_size"(gen & , context *) except + - gen GIAC_pari_unlock "_pari_unlock"(gen & , context *) except + - - unsigned int GIAC_taille "taille"(gen & , unsigned int) except + - void GIAC_try_parse_i "try_parse_i"(bool , context *) except + - - string GIAC_giac_aide_dir "giac_aide_dir"() except + - string GIAC_set_langage "_set_langage"(int , context *) except + - - gen GIAC_sto "sto" (gen &, gen &, bool, context *) except + - - int GIACctrl_c "ctrl_c" - - #test - gen GIAC_Airy_Ai "_Airy_Ai" (gen &, context *) except + - gen GIAC_ifactor "_ifactor" (gen &, context *) except + - - -cdef extern from "misc.h": - void ressetctrl_c() except + - int testctrl_c() except + - int giacgencmp( gen & , gen & , context *) except + - int giacgenrichcmp( gen & , gen & , int, context *) except + - #NB: we use the following multiplication otherwise some giac errors make python quit: - #l=giac([1,2]); l.transpose()*l - gen GIAC_giacmul "giacmul"( gen & , gen & , context *) except + - gen GIAC_giacdiv "giacdiv"( gen & , gen & , context *) except + - gen GIAC_giacmod "giacmod"( gen & , gen & , context *) except + - # - string browser_help(gen & , int lang) except + - - void GIAC_archive "archivegen"( string , gen & , context *) except + - gen GIAC_unarchive "unarchivegen"( string , context *) except + diff --git a/src/sage/libs/giac/giac.pyx b/src/sage/libs/giac/giac.pyx deleted file mode 100644 index 7c32771bee8..00000000000 --- a/src/sage/libs/giac/giac.pyx +++ /dev/null @@ -1,2070 +0,0 @@ -# sage.doctest: needs sage.libs.giac -# distutils: libraries = giac -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 -r""" -Interface to the c++ giac library. - -Giac is a general purpose Computer algebra system by Bernard Parisse released under GPLv3. - -- http://www-fourier.ujf-grenoble.fr/~parisse/giac.html -- It is build on C and C++ libraries: NTL (arithmetic), GSL (numerics), GMP - (big integers), MPFR (bigfloats) -- It provides fast algorithms for multivariate polynomial operations - (product, GCD, factorisation) and -- symbolic computations: solver, simplifications, limits/series, integration, - summation... -- Linear Algebra with numerical or symbolic coefficients. - -AUTHORS: - -- Frederic Han (2013-09-23): initial version -- Vincent Delecroix (2020-09-02): move inside Sage source code - -EXAMPLES: - -The class Pygen is the main tool to interact from python/sage with the c++ -library giac via cython. The initialisation of a Pygen just create an object -in giac, but the mathematical computation is not done. This class is mainly -for cython users. Here A is a Pygen element, and it is ready for any giac -function.:: - - sage: from sage.libs.giac.giac import * - sage: A = Pygen('2+2') - sage: A - 2+2 - sage: A.eval() - 4 - -In general, you may prefer to directly create a Pygen and execute the -evaluation in giac. This is exactly the meaning of the :func:`libgiac` -function.:: - - sage: a = libgiac('2+2') - sage: a - 4 - sage: isinstance(a, Pygen) - True - -Most common usage of this package in sage will be with the libgiac() function. -This function is just the composition of the Pygen initialisation and the -evaluation of this object in giac.:: - - sage: x,y,z=libgiac('x,y,z'); # add some giac objects - sage: f=(x+3*y)/(x+z+1)^2 -(x+z+1)^2/(x+3*y) - sage: f.factor() - (3*y-x^2-2*x*z-x-z^2-2*z-1)*(3*y+x^2+2*x*z+3*x+z^2+2*z+1)/((x+z+1)^2*(3*y+x)) - sage: f.normal() - (-x^4-4*x^3*z-4*x^3-6*x^2*z^2-12*x^2*z-5*x^2+6*x*y-4*x*z^3-12*x*z^2-12*x*z-4*x+9*y^2-z^4-4*z^3-6*z^2-4*z-1)/(x^3+3*x^2*y+2*x^2*z+2*x^2+6*x*y*z+6*x*y+x*z^2+2*x*z+x+3*y*z^2+6*y*z+3*y) - -To obtain more hints consider the help of the :func:`libgiac<_giac>` -function.:: - - sage: libgiac? # doctest: +SKIP - -Some settings of giac are available via the ``giacsettings`` element. (Ex: -maximal number of threads in computations, allowing probabilistic algorithms or -not...:: - - sage: R = PolynomialRing(QQ,8,'x') - sage: I = sage.rings.ideal.Katsura(R,8) - sage: giacsettings.proba_epsilon = 1e-15 - sage: Igiac = libgiac(I.gens()); - sage: time Bgiac = Igiac.gbasis([R.gens()],'revlex') # doctest: +SKIP - Running a probabilistic check for the reconstructed Groebner basis. If successfull, error probability is less than 1e-15 and is estimated to be less than 10^-109. Use proba_epsilon:=0 to certify (this takes more time). - Time: CPU 0.46 s, Wall: 0.50 s - sage: giacsettings.proba_epsilon = 0 - sage: Igiac = libgiac(I.gens()) - sage: time Bgiac=Igiac.gbasis([R.gens()],'revlex') # doctest: +SKIP - Time: CPU 2.74 s, Wall: 2.75 s - sage: giacsettings.proba_epsilon = 1e-15 - - :: - - sage: x = libgiac('x') - sage: f = 1/(2+sin(5*x)) - sage: oldrep = 2/5/sqrt(3)*(atan((2*tan(5*x/2)+1)/sqrt(3))+pi*floor(5*x/2/pi+1/2)) - sage: newrep = 2/5/sqrt(3)*(atan((-sqrt(3)*sin(5*x)+cos(5*x)+2*sin(5*x)+1)/(sqrt(3)*cos(5*x)+sqrt(3)-2*cos(5*x)+sin(5*x)+2))+5*x/2) - sage: ((f.int() - newrep) * (f.int() - oldrep())).normal() - 0 - sage: f.series(x,0,3) - 1/2-5/4*x+25/8*x^2-125/48*x^3+x^4*order_size(x) - sage: libgiac(sqrt(5)+pi).approx(100) - 5.377660631089582934871817052010779119637787758986631545245841837718337331924013898042449233720899343 - -TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: libgiac(3^100) - 515377520732011331036461129765621272702107522001 - sage: libgiac(-3^100) - -515377520732011331036461129765621272702107522001 - sage: libgiac(-11^1000) - -2469932918005826334124088385085221477709733385238396234869182951830739390375433175367866116456946191973803561189036523363533798726571008961243792655536655282201820357872673322901148243453211756020067624545609411212063417307681204817377763465511222635167942816318177424600927358163388910854695041070577642045540560963004207926938348086979035423732739933235077042750354729095729602516751896320598857608367865475244863114521391548985943858154775884418927768284663678512441565517194156946312753546771163991252528017732162399536497445066348868438762510366191040118080751580689254476068034620047646422315123643119627205531371694188794408120267120500325775293645416335230014278578281272863450085145349124727476223298887655183167465713337723258182649072572861625150703747030550736347589416285606367521524529665763903537989935510874657420361426804068643262800901916285076966174176854351055183740078763891951775452021781225066361670593917001215032839838911476044840388663443684517735022039957481918726697789827894303408292584258328090724141496484460001 - -Ensure that signed infinities get converted correctly:: - - sage: libgiac(+Infinity) - +infinity - sage: libgiac(-Infinity) - -infinity - -.. SEEALSO:: - - ``libgiac``, ``giacsettings``, ``Pygen``,``loadgiacgen`` - - -GETTING HELP: - -- To obtain some help on a giac keyword use the help() method. In sage the htmlhelp() method for Pygen element is disabled. Just use the ? or .help() method. - - :: - - sage: libgiac.gcd? # doctest: +SKIP - "Returns the greatest common divisor of 2 polynomials of several variables or of 2 integers or of 2 rationals. - (Intg or Poly),(Intg or Poly) - gcd(45,75);gcd(15/7,50/9);gcd(x^2-2*x+1,x^3-1);gcd(t^2-2*t+1,t^2+t-2);gcd((x^2-1)*(y^2-1)*z^2,x^3*y^3*z+(-(y^3))*z+x^3*z-z) - lcm,euler,modgcd,ezgcd,psrgcd,heugcd,Gcd" - -- You can find full html documentation about the **giac** functions at: - - - http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/cascmd_en/ - - - http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/fr/cascmd_fr/ - - - http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/el/cascmd_el/ - - - or in :doc:`$SAGE_LOCAL/share/giac/doc/en/cascmd_en/index.html` - - -.. NOTE:: - - Graphics 2D Output via qcas (the qt frontend to giac) is removed in the - sage version of giacpy. -""" -# **************************************************************************** -# Copyright (C) 2012, Frederic Han -# 2020, Vincent Delecroix <20100.delecroix@gmail.com> -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# https://www.gnu.org/licenses/ -#***************************************************************************** - -from cysignals.signals cimport * -from sys import maxsize as Pymaxint, version_info as Pyversioninfo -import os -import math - -# sage includes -from sage.ext.stdsage cimport PY_NEW - -from sage.libs.gmp.mpz cimport mpz_set - -from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ -from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.rings.integer cimport Integer -from sage.rings.infinity import AnInfinity -from sage.rings.rational cimport Rational -from sage.structure.element cimport Matrix - -from sage.symbolic.expression import symbol_table -from sage.calculus.calculus import symbolic_expression_from_string, SR_parser_giac -from sage.symbolic.ring import SR -from sage.symbolic.expression import Expression -from sage.symbolic.expression_conversions import InterfaceInit -from sage.interfaces.giac import giac - - -# Python3 compatibility ############################ -def encstring23(s): - return bytes(s, 'UTF-8') -# End of Python3 compatibility ##################### - - -######################################################## -# A global context pointer. One by giac session. -######################################################## -cdef context * context_ptr = new context() - -# Some global variables for optimisation -GIACNULL = Pygen('NULL') - -# Create a giac setting instance -giacsettings = GiacSetting() -Pygen('I:=sqrt(-1)').eval() # WTF? - -# A function to convert SR Expression with defined giac conversion to a string -# for giac/libgiac. -# NB: We want to do this without starting an external giac program and -# self._giac_() does. -SRexpressiontoGiac = InterfaceInit(giac) - - -####################################################### -# The wrapper to eval with giac -####################################################### -# in sage we don't export the giac function. We replace it by libgiac -def _giac(s): - """ - This function evaluate a python/sage object with the giac library. It creates in python/sage a Pygen element and evaluate it with giac: - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac - sage: x,y = libgiac('x,y') - sage: (x+2*y).cos().texpand() - cos(x)*(2*cos(y)^2-1)-sin(x)*2*cos(y)*sin(y) - - Coercion, Pygen and internal giac variables: The most useful objects will - be the Python object of type Pygen.:: - - sage: x,y,z = libgiac('x,y,z') - sage: f = sum([x[i] for i in range(5)])^15/(y+z);f.coeff(x[0],12) - (455*(x[1])^3+1365*(x[1])^2*x[2]+1365*(x[1])^2*x[3]+1365*(x[1])^2*x[4]+1365*x[1]*(x[2])^2+2730*x[1]*x[2]*x[3]+2730*x[1]*x[2]*x[4]+1365*x[1]*(x[3])^2+2730*x[1]*x[3]*x[4]+1365*x[1]*(x[4])^2+455*(x[2])^3+1365*(x[2])^2*x[3]+1365*(x[2])^2*x[4]+1365*x[2]*(x[3])^2+2730*x[2]*x[3]*x[4]+1365*x[2]*(x[4])^2+455*(x[3])^3+1365*(x[3])^2*x[4]+1365*x[3]*(x[4])^2+455*(x[4])^3)/(y+z) - - Warning: The complex number sqrt(-1) is exported in python as I. (But it - may appears as i):: - - sage: libgiac((1+I*sqrt(3))^3).normal() - -8 - sage: libgiac(1+I) - 1+i - - Python integers and reals can be directly converted to giac.:: - - sage: a = libgiac(2^1024);a.nextprime() - 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137859 - sage: libgiac(1.234567).erf().approx(10) - 0.9191788641 - - The Python object y defined above is of type Pygen. It is not an internal - giac variable. (Most of the time you won't need to use internal giac - variables).:: - - sage: libgiac('y:=1'); y - 1 - y - sage: libgiac.purge('y') - 1 - sage: libgiac('y') - y - - There are some natural coercion to Pygen elements:: - - sage: libgiac(pi)>3.14 ; libgiac(pi) >3.15 ; libgiac(3)==3 - True - False - True - - Linear Algebra. In Giac/Xcas vectors are just lists and matrices are lists - of list:: - - sage: x,y = libgiac('x,y') - sage: A = libgiac([[1,2],[3,4]]) # we create a giac matrix from it lines - sage: v = libgiac([x,y]); v # a giac vector - [x,y] - sage: A*v # matrix product with a vector outputs a vector - [x+2*y,3*x+4*y] - sage: v*v # dot product - x*x+y*y - - Remark that ``w=giac([[x],[y]])`` is a matrix of 1 column and 2 rows. It is - not a vector so w*w doesn't make sense.:: - - sage: w = libgiac([[x],[y]]) - sage: w.transpose()*w # this matrix product makes sense and output a 1x1 matrix. - matrix[[x*x+y*y]] - - In sage affectation doesn't create a new matrix. (cf. pointers) see also - the doc of 'Pygen.__setitem__': - - :: - - sage: B1=A; - sage: B1[0,0]=43; B1 # in place affectation changes both B1 and A - [[43,2],[3,4]] - sage: A - [[43,2],[3,4]] - sage: A[0][0]=A[0][0]+1; A # similar as A[0,0]=A[0,0]+1 - [[44,2],[3,4]] - sage: A.pcar(x) # compute the characteristic polynomial of A - x^2-48*x+170 - sage: B2=A.copy() # use copy to create another object - sage: B2[0,0]=55; B2 # here A is not modified - [[55,2],[3,4]] - sage: A - [[44,2],[3,4]] - - Sparse Matrices are available via the table function: - - :: - - sage: A = libgiac.table(()); A # create an empty giac table - table( - ) - sage: A[2,3] = 33; A[0,2] = '2/7' # set nonzero entries of the sparse matrix - sage: A*A # basic matrix operation are supported with sparse matrices - table( - (0,3) = 66/7 - ) - sage: D = libgiac.diag([22,3,'1/7']); D # some diagonal matrix - [[22,0,0],[0,3,0],[0,0,1/7]] - sage: libgiac.table(D) # to create a sparse matrix from an ordinary one - table( - (0,0) = 22, - (1,1) = 3, - (2,2) = 1/7 - ) - - But many matrix functions apply only with ordinary matrices so need conversions: - - :: - - sage: B1 = A.matrix(); B1 # convert the sparse matrix to a matrix, but the size is minimal - [[0,0,2/7,0],[0,0,0,0],[0,0,0,33]] - sage: B2 = B1.redim(4,4) # so we may need to resize B1 - sage: B2.pmin(x) - x^3 - - Lists of Pygen and Giac lists. Here l1 is a giac list and l2 is a python - list of Pygen type objects. - - :: - - sage: l1 = libgiac(range(10)); l2=[1/(i^2+1) for i in l1] - sage: sum(l2) - 33054527/16762850 - - So l1+l1 is done in giac and means a vector addition. But l2+l2 is done in Python so it is the list concatenation. - - :: - - sage: l1+l1 - [0,2,4,6,8,10,12,14,16,18] - sage: l2+l2 - [1, 1/2, 1/5, 1/10, 1/17, 1/26, 1/37, 1/50, 1/65, 1/82, 1, 1/2, 1/5, 1/10, 1/17, 1/26, 1/37, 1/50, 1/65, 1/82] - - Here V is not a Pygen element. We need to push it to giac to use a giac - method like dim, or we need to use an imported function. - - :: - - sage: V = [ [x[i]^j for i in range(8)] for j in range(8)] - sage: libgiac(V).dim() - [8,8] - sage: libgiac.det_minor(V).factor() - (x[6]-(x[7]))*(x[5]-(x[7]))*(x[5]-(x[6]))*(x[4]-(x[7]))*(x[4]-(x[6]))*(x[4]-(x[5]))*(x[3]-(x[7]))*(x[3]-(x[6]))*(x[3]-(x[5]))*(x[3]-(x[4]))*(x[2]-(x[7]))*(x[2]-(x[6]))*(x[2]-(x[5]))*(x[2]-(x[4]))*(x[2]-(x[3]))*(x[1]-(x[7]))*(x[1]-(x[6]))*(x[1]-(x[5]))*(x[1]-(x[4]))*(x[1]-(x[3]))*(x[1]-(x[2]))*(x[0]-(x[7]))*(x[0]-(x[6]))*(x[0]-(x[5]))*(x[0]-(x[4]))*(x[0]-(x[3]))*(x[0]-(x[2]))*(x[0]-(x[1])) - - Modular objects with ``%``.:: - - sage: V = libgiac.ranm(5,6) % 2; - sage: V.ker().rowdim()+V.rank() - 6 - sage: a = libgiac(7)%3; a; a%0; 7%3 - 1 % 3 - 1 - 1 - - Do not confuse with the python integers:: - - sage: type(7%3)==type(a);type(a)==type(7%3) - False - False - - Syntax with reserved or unknown Python/sage symbols. In general equations - needs symbols such as ``=``, ``<`` or ``>`` that have another meaning in Python - or Sage. So those objects must be quoted.:: - - sage: x = libgiac('x') - sage: (1+2*sin(3*x)).solve(x).simplify() - ...list[-pi/18,7*pi/18] - - sage: libgiac.solve('x^3-x>x',x) - list[((x>(-sqrt(2))) and (x<0)),x>(sqrt(2))] - - You can also add some hypothesis to a giac symbol:: - - sage: libgiac.assume('x>-pi && x2*sin(x)',x) - list[((x>(-5*pi/6)) and (x<(-pi/6))),((x>0) and (x<(pi/6))),((x>(5*pi/6)) and (x0') - list[x>0] - - Same problems with the ``..``:: - - sage: x = libgiac('x') - sage: f = 1/(5+cos(4*x)) - sage: oldrep = 1/2/(2*sqrt(6))*(atan(2*tan(4*x/2)/sqrt(6))+pi*floor(4*x/2/pi+1/2)) - sage: newrep = 1/2/(2*sqrt(6))*(atan((-sqrt(6)*sin(4*x)+2*sin(4*x))/(sqrt(6)*cos(4*x)+sqrt(6)-2*cos(4*x)+2))+4*x/2) - sage: ((f.int(x) - newrep)*(f.int(x)-oldrep)).normal() - 0 - sage: libgiac.fMax(f,'x=-0..pi').simplify() - pi/4,3*pi/4 - sage: libgiac.fMax.help() # doctest: +SKIP - "Returns the abscissa of the maximum of the expression. - Expr,[Var] - fMax(-x^2+2*x+1,x) - fMin" - sage: libgiac.sum(1/(1+x^2),'x=0..infinity').simplify() - (pi*exp(pi)^2+pi+exp(pi)^2-1)/(2*exp(pi)^2-2) - - From giac to sage. One can convert a Pygen element to sage with the sage - method. Get more details with:: - - sage: Pygen.sage? # doctest: +SKIP - - :: - - sage: L = libgiac('[1,sqrt(5),[1.3,x]]') - sage: L.sage() # All entries are converted recursively - [1, sqrt(5), [1.30000000000000, x]] - - To obtain matrices and vectors, use the :meth:`matrix` and - :meth:`vector` commands. Get more details with:: - - sage: Pygen._matrix_? # doctest: +SKIP - sage: Pygen._vector_? # doctest: +SKIP - - :: - - sage: n = var('n'); A = matrix([[1,2],[-1,1]]) - sage: B = libgiac(A).matpow(n) # We compute the symbolic power on A via libgiac - sage: C = matrix(SR,B); C # We convert B to sage - [ 1/2*(I*sqrt(2) + 1)^n + 1/2*(-I*sqrt(2) + 1)^n -1/2*I*sqrt(2)*(I*sqrt(2) + 1)^n + 1/2*I*sqrt(2)*(-I*sqrt(2) + 1)^n] - [ 1/4*I*sqrt(2)*(I*sqrt(2) + 1)^n - 1/4*I*sqrt(2)*(-I*sqrt(2) + 1)^n 1/2*(I*sqrt(2) + 1)^n + 1/2*(-I*sqrt(2) + 1)^n] - sage: (C.subs(n=3)-A^3).expand() - [0 0] - [0 0] - - - **MEMENTO of usual GIAC functions**: - - - *Expand with simplification* - - * ``ratnormal``, ``normal``, ``simplify`` (from the fastest to the most sophisticated) - - * NB: ``expand`` function doesn't regroup nor cancel terms, so it could be slow. (pedagogical purpose only?) - - - *Factor/Regroup* - - * ``factor``, ``factors``, ``regroup``, ``cfactor``, ``ifactor`` - - - *Misc* - - * ``unapply``, ``op``, ``subst`` - - - *Polynomials/Fractions* - - * ``coeff``, ``gbasis``, ``greduce``, ``lcoeff``, ``pcoeff``, ``canonical_form``, - - * ``proot``, ``poly2symb``, ``symb2poly``, ``posubLMQ``, ``poslbdLMQ``, ``VAS``, ``tcoeff``, ``valuation`` - - * ``gcd``, ``egcd``, ``lcm``, ``quo``, ``rem``, ``quorem``, ``abcuv``, ``chinrem``, - - * ``peval``, ``horner``, ``lagrange``, ``ptayl``, ``spline``, ``sturm``, ``sturmab`` - - * ``partfrac``, ``cpartfrac`` - - - *Memory/Variables* - - * ``assume``, ``about``, ``purge``, ``ans`` - - - *Calculus/Exact* - - * ``linsolve``, ``solve``, ``csolve``, ``desolve``, ``seqsolve``, ``reverse_rsolve``, ``matpow`` - - * ``limit``, ``series``, ``sum``, ``diff``, ``fMax``, ``fMin``, - - * ``integrate``, ``subst``, ``ibpdv``, ``ibpu``, ``preval`` - - - *Calculus/Exp, Log, powers* - - * ``exp2pow``, ``hyp2exp``, ``expexpand``, ``lin``, ``lncollect``, ``lnexpand``, ``powexpand``, ``pow2exp`` - - - *Trigo* - - * ``trigexpand``, ``tsimplify``, ``tlin``, ``tcollect``, - - * ``halftan``, ``cos2sintan``, ``sin2costan``, ``tan2sincos``, ``tan2cossin2``, ``tan2sincos2``, ``trigcos``, ``trigsin``, ``trigtan``, ``shift_phase`` - - * ``exp2trig``, ``trig2exp`` - - * ``atrig2ln``, ``acos2asin``, ``acos2atan``, ``asin2acos``, ``asin2atan``, ``atan2acos``, ``atan2asin`` - - - *Linear Algebra* - - * ``identity``, ``matrix``, ``makemat``, ``syst2mat``, ``matpow``, ``table``, ``redim`` - - * ``det``, ``det_minor``, ``rank``, ``ker``, ``image``, ``rref``, ``simplex_reduce``, - - * ``egv``, ``egvl``, ``eigenvalues``, ``pcar``, ``pcar_hessenberg``, ``pmin``, - - * ``jordan``, ``adjoint_matrix``, ``companion``, ``hessenberg``, ``transpose``, - - * ``cholesky``, ``lll``, ``lu``, ``qr``, ``svd``, ``a2q``, ``gauss``, ``gramschmidt``, - ``q2a``, ``isom``, ``mkisom`` - - - - *Finite Fields* - - * ``%``, ``% 0``, ``mod``, ``GF``, ``powmod`` - - - - *Integers* - - * ``gcd``, ``iabcuv``, ``ichinrem``, ``idivis``, ``iegcd``, - - * ``ifactor``, ``ifactors``, ``iquo``, ``iquorem``, ``irem``, - - * ``is_prime, is_pseudoprime``, ``lcm``, ``mod``, ``nextprime``, ``pa2b2``, ``prevprime``, - ``smod``, ``euler``, ``fracmod`` - - - *List* - - * ``append``, ``accumulate_head_tail``, ``concat``, ``head``, ``makelist``, ``member``, ``mid``, ``revlist``, ``rotate``, ``shift``, ``size``, ``sizes``, ``sort``, ``suppress``, ``tail`` - - - *Set* - - * ``intersect``, ``minus``, ``union``, ``is_element``, ``is_included`` - """ - return Pygen(s).eval() - - -####################################### -# A class to adjust giac configuration -####################################### -cdef class GiacSetting(Pygen): - """ - A class to customise the Computer Algebra System settings. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import giacsettings, libgiac - - ``threads`` (maximal number of allowed theads in giac):: - - sage: from sage.libs.giac.giac import giacsettings - sage: import os - sage: try: - ....: ncpu = int(os.environ['SAGE_NUM_THREADS']) - ....: except KeyError: - ....: ncpu =1 - sage: giacsettings.threads == ncpu - True - - ``digits`` (default digit number used for approximations):: - - sage: giacsettings.digits = 20 - sage: libgiac.approx('1/7') - 0.14285714285714285714 - sage: giacsettings.digits = 50 - sage: libgiac.approx('1/7') - 0.14285714285714285714285714285714285714285714285714 - sage: giacsettings.digits = 12 - - ``sqrtflag`` (flag to allow sqrt extractions during solve and - factorizations):: - - sage: giacsettings.sqrtflag = False - sage: libgiac('x**2-2').factor() - x^2-2 - sage: giacsettings.sqrtflag = True - sage: libgiac('x**2-2').factor() - (x-sqrt(2))*(x+sqrt(2)) - - ``complexflag`` (flag to allow complex number in solving equations or factorizations):: - - sage: giacsettings.complexflag=False;giacsettings.complexflag - False - sage: libgiac('x**2+4').factor() - x^2+4 - sage: giacsettings.complexflag = True - sage: libgiac('x**2+4').factor() - (x+2*i)*(x-2*i) - - - ``eval_level`` (recursive level of substitution of variables during an - evaluation):: - - sage: giacsettings.eval_level = 1 - sage: libgiac("purge(a):;b:=a;a:=1;b") - "Done",a,1,a - sage: giacsettings.eval_level=25; giacsettings.eval_level - 25 - sage: libgiac("purge(a):;b:=a;a:=1;b") - "Done",a,1,1 - - ``proba_epsilon`` (maximum probability of a wrong answer with a probabilist - algorithm). Set this number to 0 to disable probabilist algorithms - (slower):: - - sage: giacsettings.proba_epsilon=0;libgiac('proba_epsilon') - 0.0 - sage: giacsettings.proba_epsilon=10^(-13) - sage: libgiac('proba_epsilon')<10^(-14) - False - - ``epsilon`` (value used by the ``epsilon2zero`` function):: - - sage: giacsettings.epsilon = 1e-10 - sage: P = libgiac('1e-11+x+5') - sage: P == x+5 - False - sage: (P.epsilon2zero()).simplify() - x+5 - """ - def __repr__(self): - return "Giac Settings" - - def _sage_doc_(self): - return GiacSetting.__doc__ - - property digits: - r""" - Default digits number used for approximations. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import giacsettings, libgiac - sage: giacsettings.digits = 20 - sage: giacsettings.digits - 20 - sage: libgiac.approx('1/7') - 0.14285714285714285714 - sage: giacsettings.digits=12; - """ - def __get__(self): - return (self.cas_setup()[6])._val - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - pl[6] = value - Pygen('cas_setup(%s)' % pl).eval() - - property sqrtflag: - r""" - Flag to allow square roots in solving equations or factorizations. - """ - def __get__(self): - return (self.cas_setup()[9])._val == 1 - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - if value: - pl[9]=1 - else: - pl[9]=0 - Pygen('cas_setup(%s)' % pl).eval() - - property complexflag: - r""" - Flag to allow complex number in solving equations or factorizations. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac, giacsettings - sage: giacsettings.complexflag = False - sage: giacsettings.complexflag - False - sage: libgiac('x**2+4').factor() - x^2+4 - sage: giacsettings.complexflag=True; - sage: libgiac('x**2+4').factor() - (x+2*i)*(x-2*i) - """ - def __get__(self): - return (self.cas_setup()[2])._val == 1 - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - if value: - pl[2] = 1 - else: - pl[2] = 0 - Pygen('cas_setup(%s)' % pl).eval() - - property eval_level: - r""" - Recursive level of substitution of variables during an evaluation. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import giacsettings,libgiac - sage: giacsettings.eval_level=1 - sage: libgiac("purge(a):;b:=a;a:=1;b") - "Done",a,1,a - sage: giacsettings.eval_level=25; giacsettings.eval_level - 25 - sage: libgiac("purge(a):;b:=a;a:=1;b") - "Done",a,1,1 - sage: libgiac.purge('a,b') - 1,a - """ - def __get__(self): - return (self.cas_setup()[7][3])._val - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - pl[7] = [l[7][0],l[7][1],l[7][2], value] - Pygen('cas_setup(%s)' % pl).eval() - - property proba_epsilon: - r""" - Maximum probability of a wrong answer with a probabilist algorithm. - - Set this number to 0 to disable probabilist algorithms (slower). - - EXAMPLES:: - - sage: from sage.libs.giac.giac import giacsettings,libgiac - sage: giacsettings.proba_epsilon=0;libgiac('proba_epsilon') - 0.0 - sage: giacsettings.proba_epsilon=10^(-13) - sage: libgiac('proba_epsilon')<10^(-14) - False - """ - def __get__(self): - return (self.cas_setup()[5][1])._double - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - pl[5] = [l[5][0],value] - Pygen('cas_setup(%s)' % pl).eval() - - property epsilon: - r""" - Value used by the ``epsilon2zero`` function. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import giacsettings,libgiac - sage: giacsettings.epsilon = 1e-10 - sage: P = libgiac('1e-11+x+5') - sage: P == x+5 - False - sage: (P.epsilon2zero()).simplify() - x+5 - """ - def __get__(self): - return (self.cas_setup()[5][0])._double - - def __set__(self, value): - l = Pygen('cas_setup()').eval() - pl = [ i for i in l ] - pl[5] = [value,l[5][1]] - Pygen('cas_setup(%s)' % pl).eval() - - property threads: - r""" - Maximal number of allowed theads in giac. - """ - def __get__(self): - return (self.cas_setup()[7][0])._val - - def __set__(self, value): - Pygen('threads:=%s' % str(value)).eval() - -######################################################## -# # -# The python class that points to a cpp giac gen # -# # -######################################################## -include 'auto-methods.pxi' - -cdef class Pygen(GiacMethods_base): - - cdef gen * gptr #pointer to the corresponding C++ element of type giac::gen - - def __cinit__(self, s=None): - - #NB: the != here gives problems with the __richcmp__ function - #if (s!=None): - # so it's better to use isinstance - if (isinstance(s, None.__class__)): - # Do NOT replace with: self=GIACNULL (cf the doctest in __repr__ - sig_on() - self.gptr = new gen ((GIACNULL).gptr[0]) - sig_off() - return - - if isinstance(s, int): - # This looks 100 faster than the str initialisation - if s.bit_length() < Pymaxint.bit_length(): - sig_on() - self.gptr = new gen(s) - sig_off() - else: - sig_on() - self.gptr = new gen(pylongtogen(s)) - sig_off() - - elif isinstance(s, Integer): # for sage int (gmp) - sig_on() - if (abs(s)>Pymaxint): - self.gptr = new gen((s).value) - else: - self.gptr = new gen(s) # important for pow to have a int - sig_off() - - elif isinstance(s, Rational): # for sage rat (gmp) - #self.gptr = new gen(((Pygen(s.numerator())/Pygen(s.denominator()))).gptr[0]) - # FIXME: it's slow - sig_on() - self.gptr = new gen(GIAC_rdiv(gen(((s.numerator())).value),gen(((s.denominator())).value))) - sig_off() - - elif isinstance(s, Matrix): - s = Pygen(s.list()).list2mat(s.ncols()) - sig_on() - self.gptr = new gen((s).gptr[0]) - sig_off() - - elif isinstance(s, float): - sig_on() - self.gptr = new gen(s) - sig_off() - - elif isinstance(s, Pygen): - # in the test: x,y=Pygen('x,y');((x+2*y).sin()).texpand() - # the y are lost without this case. - sig_on() - self.gptr = new gen((s).gptr[0]) - sig_off() - - elif isinstance(s, (list, range)): - sig_on() - self.gptr = new gen(_wrap_pylist(s),0) - sig_off() - - elif isinstance(s, tuple): - sig_on() - self.gptr = new gen(_wrap_pylist(s),1) - sig_off() - - # Other types are converted with strings. - else: - if isinstance(s, Expression): - # take account of conversions with key giac in the sage symbol dict - try: - s = s._giac_init_() - except AttributeError: - s = SRexpressiontoGiac(s) - elif isinstance(s, AnInfinity): - s = s._giac_init_() - if not isinstance(s, str): - s = s.__str__() - sig_on() - self.gptr = new gen(encstring23(s),context_ptr) - sig_off() - - def __dealloc__(self): - del self.gptr - - def __repr__(self): - # fast evaluation of the complexity of the gen. (it's not the number of char ) - sig_on() - t=GIAC_taille(self.gptr[0], 6000) - sig_off() - if t < 6000: - sig_on() - result = GIAC_print(self.gptr[0], context_ptr).c_str().decode() - sig_off() - return result - else: - sig_on() - result = str(self.type) + "\nResult is too big for Display. If you really want to see it use print" - sig_off() - return result - - def __str__(self): - #if self.gptr == NULL: - # return '' - sig_on() - result = GIAC_print(self.gptr[0], context_ptr).c_str().decode() - sig_off() - return result - - def __len__(self): - """ - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: l=libgiac("seq[]");len(l) # 29552 comment28 - 0 - """ - if (self._type == 7): - sig_on() - rep=(self.gptr.ref_VECTptr()).size() - sig_off() - return rep - else: - sig_on() - rep=GIAC_size(self.gptr[0],context_ptr).val - sig_off() - #GIAC_size return a gen. we take the int: val - return rep - - def __getitem__(self, i): #TODO?: add gen support for indexes - """ - Lists of 10^6 integers should be translated to giac easily - - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: l=libgiac(list(range(10^6)));l[5] - 5 - sage: l[35:50:7] - [35,42,49] - sage: l[-10^6] - 0 - sage: t=libgiac(tuple(range(10))) - sage: t[:4:-1] - 9,8,7,6,5 - sage: x=libgiac('x'); sum([ x[i] for i in range(5)])^3 - (x[0]+x[1]+x[2]+x[3]+x[4])^3 - sage: A=libgiac.ranm(5,10); A[3,7]-A[3][7] - 0 - sage: A.transpose()[8,2]-A[2][8] - 0 - - Crash test:: - - sage: from sage.libs.giac.giac import Pygen - sage: l=Pygen() - sage: l[0] - Traceback (most recent call last): - ... - IndexError: list index 0 out of range - """ - cdef gen result - - if(self._type == 7) or (self._type == 12): #if self is a list or a string - if isinstance(i, (int, Integer)): - n=len(self) - if(ii] - sig_off() - return _wrap_gen(result) - else: - raise IndexError('list index %s out of range' % i) - else: - if isinstance(i, slice): - sig_on() - result = gen(_getgiacslice(self,i),self._subtype) - sig_off() - return _wrap_gen(result) - # add support for multi indexes - elif isinstance(i, tuple): - if(len(i)==2): - return self[i[0]][i[1]] - elif(len(i)==1): - # in case of a tuple like this: (3,) - return self[i[0]] - else: - return self[i[0],i[1]][tuple(i[2:])] - else: - raise TypeError('gen indexes are not yet implemented') - # Here we add support to formal variable indexes: - else: - cmd = '%s[%s]' % (self, i) - ans = Pygen(cmd).eval() - # if the answer is a string, it must be an error message because self is not a list or a string - if (ans._type == 12): - raise TypeError("Error executing code in Giac\nCODE:\n\t%s\nGiac ERROR:\n\t%s" % (cmd, ans)) - return ans - - def __setitem__(self, key, value): - """ - Set the value of a coefficient of a giac vector or matrix or list. - - Warning: It is an in place affectation. - - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: A = libgiac([ [ j+2*i for i in range(3)] for j in range(3)]); A - [[0,2,4],[1,3,5],[2,4,6]] - sage: A[1,2]=44;A - [[0,2,4],[1,3,44],[2,4,6]] - sage: A[2][2]=1/3;A - [[0,2,4],[1,3,44],[2,4,1/3]] - sage: x=libgiac('x') - sage: A[0,0]=x+1/x; A - [[x+1/x,2,4],[1,3,44],[2,4,1/3]] - sage: A[0]=[-1,-2,-3]; A - [[-1,-2,-3],[1,3,44],[2,4,1/3]] - sage: B=A; A[2,2] - 1/3 - sage: B[2,2]=6 # in place affectation - sage: A[2,2] # so A is also modified - 6 - sage: A.pcar(x) - x^3-8*x^2-159*x - - NB: For Large matrix it seems that the syntax ``A[i][j]=`` is faster that ``A[i,j]=``:: - - sage: from sage.libs.giac.giac import libgiac - sage: from time import time - sage: A=libgiac.ranm(4000,4000) - sage: t1=time(); A[500][500]=12345;t1=time()-t1 - sage: t2=time(); A[501,501]=54321;t2=time()-t2 - sage: t1,t2 # doctest: +SKIP - (0.0002014636993408203, 0.05124521255493164) - sage: A[500,500],A[501][501] - (12345, 54321) - """ - cdef gen v - sig_on() - cdef gen g = gen(encstring23('GIACPY_TMP_NAME050268070969290100291003'),context_ptr) - GIAC_sto((self).gptr[0],g,1,context_ptr) - g = gen(encstring23('GIACPY_TMP_NAME050268070969290100291003[%s]' % str(key)), context_ptr) - v=((Pygen(value).eval())).gptr[0] - GIAC_sto(v, g, 1, context_ptr) - Pygen('purge(GIACPY_TMP_NAME050268070969290100291003):;').eval() - sig_off() - return - - def __iter__(self): - """ - Pygen lists of 10^6 elements should be yield. - - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: l = libgiac(range(10^6)) - sage: [ i for i in l ] == list(range(10^6)) - True - - Check for :issue:`18841`:: - - sage: L = libgiac(range(10)) - sage: next(iter(L)) - 0 - """ - cdef int i - for i in range(len(self)): - yield self[i] - - def eval(self): - cdef gen result - sig_on() - result = GIAC_protecteval(self.gptr[0],giacsettings.eval_level,context_ptr) - sig_off() - return _wrap_gen(result) - - def __add__(self, right): - cdef gen result - if not isinstance(right, Pygen): - right=Pygen(right) - # Curiously this case is important: - # otherwise: f=1/(2+sin(5*x)) crash - if not isinstance(self, Pygen): - self=Pygen(self) - sig_on() - result = (self).gptr[0] + (right).gptr[0] - sig_off() - return _wrap_gen(result) - - def __call__(self, *args): - cdef gen result - cdef Pygen pari_unlock = Pygen('pari_unlock()') - cdef gen pari_unlock_result - cdef Pygen right - n = len(args) - if n > 1: - # FIXME? improve with a vector, or improve Pygen(list) - right = Pygen(args).eval() - elif n == 1: - right = Pygen(args[0]) - else: - right = GIACNULL - if not isinstance(self, Pygen): - self = Pygen(self) - # Some giac errors such as pari_locked are caught by the try - # so we can't put the sig_on() in the try. - # But now a keyboard interrupt fall back to this sig_on so - # it may have left the giac pari locked. - sig_on() - try: - result = self.gptr[0](right.gptr[0], context_ptr) - except RuntimeError: - # The previous computation might have failed due to a pari_lock - # So we will not raise an exception yet. - pari_unlock_result = GIAC_eval(pari_unlock.gptr[0], 1, context_ptr) - tmp = _wrap_gen(result) - # if pari was not locked in giac, we have locked it, so unlock it. - if tmp == 0: - pari_unlock_result = GIAC_eval(pari_unlock.gptr[0], 1, context_ptr) - tmp = _wrap_gen(result) - raise - else: - result = GIAC_eval(right.gptr[0], 1, context_ptr) - result = self.gptr[0](result, context_ptr) - finally: - sig_off() - return _wrap_gen(result) - - def __sub__(self, right): - cdef gen result - if not isinstance(right, Pygen): - right=Pygen(right) - if not isinstance(self, Pygen): - self=Pygen(self) - sig_on() - result = (self).gptr[0] - (right).gptr[0] - sig_off() - return _wrap_gen(result) - - def __mul__(self, right): - """ - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: (sqrt(5)*libgiac('x')).factor() # BUG test could give 0 - sqrt(5)*x - sage: (libgiac('x')*sqrt(5)).factor() - sqrt(5)*x - """ - cdef gen result - if not isinstance(right, Pygen): - right = Pygen(right) - if not isinstance(self, Pygen): - self = Pygen(self) - #result = (self).gptr[0] * (right).gptr[0] - #NB: with the natural previous method, the following error generated by - #giac causes python to quit instead of an error message. - #l=Pygen([1,2]);l.transpose()*l; - sig_on() - result = GIAC_giacmul((self).gptr[0], (right).gptr[0],context_ptr) - sig_off() - return _wrap_gen(result) - - # PB / in python3 is truediv - def __div__(self, right): - """ - TESTS:: - - sage: from sage.libs.giac.giac import libgiac - sage: (sqrt(3)/libgiac('x')).factor() # BUG test could give 0 - sqrt(3)/x - sage: (libgiac('x')/sqrt(3)).factor() - sqrt(3)*x/3 - """ - cdef gen result - if not isinstance(right, Pygen): - right = Pygen(right) - if not isinstance(self, Pygen): - self = Pygen(self) - sig_on() - result = GIAC_giacdiv((self).gptr[0], (right).gptr[0],context_ptr) - sig_off() - return _wrap_gen(result) - - def __truediv__(self, right): - cdef gen result - if not isinstance(right, Pygen): - right = Pygen(right) - if not isinstance(self, Pygen): - self = Pygen(self) - sig_on() - result = (self).gptr[0] / (right).gptr[0] - sig_off() - return _wrap_gen(result) - - def __pow__(self, right, ignored): - cdef gen result - if not isinstance(right, Pygen): - right = Pygen(right) - if not isinstance(self, Pygen): - self = Pygen(self) - sig_on() - result = GIAC_pow((self).gptr[0], (right).gptr[0], context_ptr ) - sig_off() - return _wrap_gen(result) - - def __mod__(self, right): - cdef gen result - if not isinstance(right, Pygen): - right = Pygen(right) - if not isinstance(self, Pygen): - self = Pygen(self) - #result = gen(GIAC_makenewvecteur((self).gptr[0],(right).gptr[0]),1) - #to have an integer output: - #result = GIAC_smod(result,context_ptr) - #we give a modular output: - sig_on() - result = GIAC_giacmod((self).gptr[0], (right).gptr[0],context_ptr) - sig_off() - return _wrap_gen(result) - - def __neg__(self): - cdef gen result - if not isinstance(self, Pygen): - self = Pygen(self) - sig_on() - result = GIAC_neg((self).gptr[0]) - sig_off() - return _wrap_gen(result) - - def __pos__(self): - return self - - # To be able to use the eval function before the GiacMethods initialisation - def cas_setup(self, *args): - return Pygen('cas_setup')(self, *args) - - def savegen(self, str filename): - """ - Archive a Pygen element to a file in giac compressed format. - - Use the loadgiacgen command to get back the Pygen from the file. - In C++ these files can be opened with ``giac::unarchive``. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: f=libgiac('(x+y+z+2)**10'); g=f.normal() - sage: g.savegen("fichiertest") # doctest: +SKIP - sage: a=loadgiacgen("fichiertest") # doctest: +SKIP - sage: from tempfile import NamedTemporaryFile - sage: F=NamedTemporaryFile() # chose a temporary file for a test - sage: g.savegen(F.name) - sage: a=loadgiacgen(F.name) - sage: a.factor() - (x+y+z+2)^10 - sage: F.close() - """ - sig_on() - GIAC_archive( encstring23(filename), (self).gptr[0], context_ptr) - sig_off() - - # NB: with giac <= 1.2.3-57 redim doesn't have a non evaluated for so Pygen('redim') fails. - # hence replacement for redim: - - def redim(self, a, b=None): - """ - Increase the size of a matrix when possible, otherwise return ``self``. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac - sage: C = libgiac([[1,2]]) - sage: C.redim(2,3) - [[1,2,0],[0,0,0]] - sage: C.redim(2,1) - [[1,2]] - """ - d=self.dim() - if d.type()==7: - if(a>d[0] and b>=d[1]): - A=self.semi_augment(Pygen((a-d[0],d[1])).matrix()) - if(b>d[1]): - A=A.augment(Pygen((a,b-d[1])).matrix()) - return A - elif(b>d[1] and a==d[0]): - return self.augment(Pygen((d[0],b-d[1])).matrix()) - else: - return self - else: - raise TypeError("self is not a giac List") - - # def htmlhelp(self, str lang='en'): - # """ - # Open the giac html detailed help about ``self`` in an external browser - - # There are currently 3 supported languages: 'en', 'fr', 'el' - - # """ - # l={'fr':1 , 'en':2, 'el':4} - # if (not lang in ['en', 'fr', 'el']): - # lang='en' - # try: - # url=browser_help(self.gptr[0],l[lang]).decode() - # giacbasedir=GIAC_giac_aide_dir().decode() - # except: - # raise RuntimeError('giac docs dir not found') - # print(url) - # if os.access(giacbasedir,os.F_OK): - # url='file:'+url - # wwwbrowseropen(url) - - def _help(self): - return self.findhelp().__str__() - - def _sage_doc_(self): - return self._help() - - def __doc__(self): - return self._help() - - # # # # # # # # # # # # # # # # # - # sage addons - # # # # # # # # # # # # # # # # # - def _latex_(self): - r""" - You can output Giac expressions in latex. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac - sage: M = matrix(QQ, [[1, 2], [3, 4]]) - sage: latex(M) - \left(\begin{array}{rr} - 1 & 2 \\ - 3 & 4 - \end{array}\right) - sage: gM = libgiac(M) - sage: latex(gM) - \left...\begin{array}{cc}...1...&...2...\\...3...&...4...\end{array}\right... - sage: gf = libgiac('(x^4 - y)/(y^2-3*x)') - sage: latex(gf) # output changed slightly from 1.5.0-63 to 1.5.0-87 - \frac{...x^{4}...-...y...}{...y^{2}-3...x...} - """ - sig_on() - result = GIAC_gen2tex(self.gptr[0], context_ptr).c_str().decode() - sig_off() - return result - - def _integer_(self, Z=None): - """ - Convert giac integers or modular integers to sage Integers (via gmp). - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: a=libgiac('10'); b=libgiac('2**300') - sage: a;type(ZZ(a)) - 10 - - sage: next_prime(b) - 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397533 - sage: c=libgiac('2 % nextprime(2**40)') - sage: ZZ(c^1000) - -233775163595 - sage: Mod(2,next_prime(2^40))^1000 - ZZ(c^1000) - 0 - sage: 2^320-(c^320).sage() - 0 - """ - cdef Integer n = PY_NEW(Integer) - typ = self._type - - if(typ == 0): - # giac _INT_ i.e int - return Integer(self._val) - - elif(typ == 2): - # giac _ZINT i.e mpz_t - sig_on() - mpz_set(n.value,(self.gptr.ref_ZINTptr())[0]) - sig_off() - return n - - elif(typ == 15): - # self is a giac modulo - sig_on() - a = _wrap_gen( (self.gptr.ref_MODptr())[0]) - # It is useless to get the modulus here - # because the result will be lift to ZZ. - result = ZZ(a) - sig_off() - return result - - else: - raise TypeError("cannot convert non giac integers to Integer") - - def _rational_(self, Z=None): - """ - Convert giac rationals to sage rationals. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: a = libgiac('103993/33102') - sage: b = QQ(a); b - 103993/33102 - sage: b == a.sage() - True - """ - typ = self._type - # _INT_ or _ZINT - if typ == 0 or typ == 2: - return QQ(ZZ(self)) - # _FRAC_ - elif typ == 10: - # giac _RAT_ - return ZZ(self.numer()) / ZZ(self.denom()) - else: - raise TypeError("cannot convert non giac _FRAC_ to QQ") - - def sage(self): - r""" - Convert a libgiac expression back to a Sage expression. (could be slow) - - This currently does not implement a parser for the Giac output language, - therefore only very simple expressions will convert successfully. - - Lists are converted recursively to sage. - - CURRENT STATUS: - - ZZ, QQ, ZZ/nZZ, strings, are supported, other type are sent to the symbolic ring - via strings. In particular symbolic expressions modulo n should be lift to ZZ - before ( with % 0 ). - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac - sage: m = libgiac('x^2 + 5*y') - sage: m.sage() - x^2 + 5*y - - :: - - sage: m = libgiac('sin(2*sqrt(1-x^2)) * (1 - cos(1/x))^2') - sage: m.trigexpand().sage() - 2*cos(sqrt(-x^2 + 1))*cos(1/x)^2*sin(sqrt(-x^2 + 1)) - 4*cos(sqrt(-x^2 + 1))*cos(1/x)*sin(sqrt(-x^2 + 1)) + 2*cos(sqrt(-x^2 + 1))*sin(sqrt(-x^2 + 1)) - - :: - - sage: a=libgiac(' 2 % 7') - sage: (a.sage())^6 - 1 - sage: a=libgiac('"une chaine"') - sage: b=a.sage(); b + b - 'une chaineune chaine' - sage: isinstance(b,str) - True - - The giac entries in the pynac conversion dictionary are used:: - - sage: x=var('x') - sage: f=libgiac.Gamma - sage: f(4) - 6 - sage: f(x) - Gamma(sageVARx) - sage: (f(x)).sage() - gamma(x) - - Converting a custom name by adding a new entry to the ``symbols_table``:: - - sage: ex = libgiac('myFun(x)') - sage: sage.symbolic.expression.register_symbol(sin, {'giac':'myFun'}) - sage: ex.sage() - sin(x) - """ - typ = self._type - - if typ != 7: - # self is not a list - if typ == 0 or typ == 2: - return ZZ(self) - - elif typ == 10: - return QQ(self) - - elif typ == 15: - # modular integer - sig_on() - a = _wrap_gen( (self.gptr.ref_MODptr())[0]) - b = _wrap_gen( (self.gptr.ref_MODptr())[1]) - result = IntegerModRing(ZZ(b))(ZZ(a)) - sig_off() - return result - - elif typ == 12: - # string - sig_on() - result = eval(self.__str__()) - sig_off() - return result - - else: - return SR(self) - - else: - # self is a list - sig_on() - result = [entry.sage() for entry in self] - sig_off() - return result - - def _symbolic_(self, R): - r""" - Convert ``self`` object to the ring R via a basic string evaluation. (slow) - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: u,v=var('u,v');a=libgiac('cos(u+v)').texpand() - sage: simplify(SR(a)+sin(u)*sin(v)) - cos(u)*cos(v) - - TESTS: - - Check that variables and constants are not mixed up (:issue:`30133`):: - - sage: ee, ii, pp = SR.var('e,i,pi') - sage: libgiac(ee * ii * pp).sage().variables() - (e, i, pi) - sage: libgiac(e * i * pi).sage().variables() - () - sage: libgiac.integrate(ee^x, x).sage() - e^x/log(e) - sage: y = SR.var('π') - sage: libgiac.integrate(cos(y), y).sage() - sin(π) - """ - if isinstance(R, SR.__class__): - # Try to convert some functions names to the symbolic ring - lsymbols = symbol_table['giac'].copy() - #lsymbols.update(locals) - try: - result = symbolic_expression_from_string(self.__str__(), lsymbols, - accept_sequence=True, - parser=SR_parser_giac) - return result - - except Exception: - raise NotImplementedError("Unable to parse Giac output: %s" % self.__repr__()) - else: - try: - result = R(self.__str__()) - return result - - except Exception: - raise NotImplementedError("Unable to parse Giac output: %s" % self.__repr__()) - - def _matrix_(self, R=ZZ): - r""" - Return matrix over the (Sage) ring R where self - should be a Giac matrix. The default ring is ZZ. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: R.=QQ[] - sage: M=libgiac('matrix(4,4,(k,l)->(x^k-y^l))'); M - //... - matrix[[0,1-y,1-y^2,1-y^3],[x-1,x-y,x-y^2,x-y^3],[x^2-1,x^2-y,x^2-y^2,x^2-y^3],[x^3-1,x^3-y,x^3-y^2,x^3-y^3]] - sage: M.eigenvals() # random - 0,0,(x^3+x^2+x-y^3-y^2-y+sqrt(x^6+2*x^5+3*x^4-14*x^3*y^3+2*x^3*y^2+2*x^3*y+6*x^3+2*x^2*y^3-14*x^2*y^2+2*x^2*y+5*x^2+2*x*y^3+2*x*y^2-14*x*y+4*x+y^6+2*y^5+3*y^4+6*y^3+5*y^2+4*y-12))/2,(x^3+x^2+x-y^3-y^2-y-sqrt(x^6+2*x^5+3*x^4-14*x^3*y^3+2*x^3*y^2+2*x^3*y+6*x^3+2*x^2*y^3-14*x^2*y^2+2*x^2*y+5*x^2+2*x*y^3+2*x*y^2-14*x*y+4*x+y^6+2*y^5+3*y^4+6*y^3+5*y^2+4*y-12))/2 - sage: Z=matrix(R,M);Z - [ 0 -y + 1 -y^2 + 1 -y^3 + 1] - [ x - 1 x - y -y^2 + x -y^3 + x] - [ x^2 - 1 x^2 - y x^2 - y^2 -y^3 + x^2] - [ x^3 - 1 x^3 - y x^3 - y^2 x^3 - y^3] - sage: parent(Z) - Full MatrixSpace of 4 by 4 dense matrices over Multivariate Polynomial Ring in x, y over Rational Field - """ - cdef int c - cdef int r - v = self.dim() - n = (v[0])._val - m = (v[1])._val - from sage.matrix.matrix_space import MatrixSpace - M = MatrixSpace(R, n, m) - sig_on() - entries = [[R((self[r])[c]) for c in range(m)] for r in range(n)] - sig_off() - return M(entries) - - def _vector_(self, R=None): - r""" - Return vector over the (Sage) ring R where self - should be a Giac matrix. The default ring is ZZ. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: v=libgiac(range(10)) - sage: vector(v+v) - (0, 2, 4, 6, 8, 10, 12, 14, 16, 18) - sage: vector(v+v/3,QQ) - (0, 4/3, 8/3, 4, 16/3, 20/3, 8, 28/3, 32/3, 12) - """ - if isinstance(R, None.__class__): - R=ZZ - - v = self.dim() - try: - n = v._val - except AttributeError: - raise TypeError("Entry is not a giac vector") - from sage.modules.free_module_element import vector - sig_on() - entries = [R(self[c]) for c in range(n)] - sig_off() - return vector(R,entries) - - # # # # # # # # # # # # # # # - - def mplot(self): - """ - Basic export of some 2D plots to sage. Only generic plots are supported. - lines, circles, ... are not implemented - """ - from sage.plot.line import line - from sage.plot.scatter_plot import scatter_plot - - xyscat = [] - xyplot = [] - plotdata = self - if not plotdata.type() == 'DOM_LIST': - plotdata = [plotdata] - - sig_on() - for G in plotdata: - if G.dim() > 2: # it is not a pnt. Ex: scatterplot - for g in G: - xyscat=xyscat+[[(g.real())._double,(g.im())._double]] - - else: - if G[1].type()=='DOM_LIST': - l=G[1].op() - else: - l=G[1][2].op() - xyplot=[[(u.real())._double,(u.im())._double] for u in l] - - if xyscat: - result = scatter_plot(xyscat) - - else: - result = line(xyplot) - sig_off() - - return result - - # # # # # # # # # # # # # # # # # # # # # # # # # - # WARNING: - # - # Do not use things like != in Pygen's __cinit__ - # with this __richcmp__ enabled - # The methods will bug: a=Pygen(2); a.sin() - # - # # # # # # # # # # # # # # # # # # # # # # # # # - - def __richcmp__(self, other, op): - if not isinstance(other, Pygen): - other = Pygen(other) - if not isinstance(self, Pygen): - self = Pygen(self) - sig_on() - result = giacgenrichcmp((self).gptr[0],(other).gptr[0], op, context_ptr ) - sig_off() - return result == 1 - - # - # Some attributes of the gen class: - # - - property _type: - def __get__(self): - sig_on() - result = self.gptr.type - sig_off() - return result - - property _subtype: - def __get__(self): - sig_on() - result = self.gptr.subtype - sig_off() - return result - - property _val: # immediate int (type _INT_) - """ - immediate int value of an _INT_ type gen. - """ - def __get__(self): - if self._type == 0: - sig_on() - result = self.gptr.val - sig_off() - return result - else: - raise TypeError("cannot convert non _INT_ giac gen") - - property _double: # immediate double (type _DOUBLE_) - """ - immediate conversion to float for a gen of _DOUBLE_ type. - """ - def __get__(self): - if self._type == 1: - sig_on() - result = self.gptr._DOUBLE_val - sig_off() - return result - else: - raise TypeError("cannot convert non _DOUBLE_ giac gen") - - property help: - def __get__(self): - return self._help() - - ################################################### - # Add the others methods - ################################################### - # - # NB: with __getattr__ this is about 10 times slower: [a.normal() for i in range(10**4)] - # than [GiacMethods["normal"](a) for i in range(10**4)] - # - # def __getattr__(self, name): - # return GiacMethods[str(name)](self) - - # test - - def giacAiry_Ai(self, *args): - cdef gen result = GIAC_Airy_Ai(self.gptr[0], context_ptr) - return _wrap_gen(result) - - def giacifactor(self, *args): - cdef gen result - sig_on() - result = GIAC_eval(self.gptr[0], 1, context_ptr) - result = GIAC_ifactor(result, context_ptr) - sig_off() - return _wrap_gen(result) - - -################################################################ -# A wrapper from a cpp element of type giac gen to create # -# the Python object # -################################################################ -cdef inline _wrap_gen(gen g)except +: - -# cdef Pygen pyg=Pygen('NULL') -# It is much faster with '' -# [x-x for i in range(10**4)] -# ('clock: ', 0.010000000000000009, -# than with 'NULL': -# [x-x for i in range(10**4)] -# ('clock: ', 1.1999999999999997, -# # # # # # -# This is faster than with: -# cdef Pygen pyg=Pygen('') -# ll=giac(range(10**6)) -# ('clock: ', 0.40000000000000036, ' time: ', 0.40346789360046387) -# gg=[1 for i in ll] -# ('clock: ', 0.669999999999999, ' time: ', 0.650738000869751) -# -# But beware when changing the None case in Pygen init. -# - sig_on() - cdef Pygen pyg=Pygen() - del pyg.gptr # Pygen.__cinit__() always creates a gen. So we have to delete it here. - pyg.gptr=new gen(g) - sig_off() - return pyg -# if(pyg.gptr !=NULL): -# return pyg -# else: -# raise MemoryError("empty gen") - -################################################################ -# A wrapper from a python list to a vector of gen # -################################################################ - -cdef vecteur _wrap_pylist(L) except +: - cdef vecteur * V - cdef int i - - if isinstance(L, (tuple, list, range)): - n = len(L) - V = new vecteur() - - sig_on() - for i in range(n): - V.push_back((Pygen(L[i])).gptr[0]) - sig_off() - return V[0] - else: - raise TypeError("argument must be a tuple or a list") - - -################################# -# slice wrapper for a giac list -################################# -cdef vecteur _getgiacslice(Pygen L, slice sl) except +: - cdef vecteur * V - cdef int u - - if L.type()=="DOM_LIST": - n=len(L) - V=new vecteur() - - sig_on() -# for u in range(n)[sl]: #pb python3 - b, e, st = sl.indices(n) - for u in range(b, e, st): - V.push_back((L.gptr[0])[u]) - sig_off() - return V[0] - else: - raise TypeError("argument must be a Pygen list and a slice") - - -cdef gen pylongtogen(a) except +: - # # - # basic conversion of Python long integers to gen via Horner's Method # - # # - - aneg=False - cdef gen g=gen(0) - cdef gen M - - if (a<0): - aneg=True - a=-a - if Pyversioninfo >= (2,7): - size=a.bit_length() # bit_length python >= 2.7 required. - shift=Pymaxint.bit_length()-1 - else: - size=math.trunc(math.log(a,2))+1 - shift=math.trunc(math.log(Pymaxint)) - M=gen((1<=shift): - size=size-shift - i=int(a>>size) - g=(g*M+gen(i)) - a=a-(i<(1< a) - if aneg: - # when cythonizing with cython 0.24: - # g=-g gives an Invalid operand type for '-' (gen) - g=GIAC_neg(g) - return g - - -############################################################# -# Examples of python functions directly implemented from giac -############################################################# -#def giaceval(Pygen self): -# cdef gen result -# try: -# result = GIAC_protecteval(self.gptr[0],1,context_ptr) -# return _wrap_gen(result) -# except: -# raise -# -# -#def giacfactor(Pygen self): -# -# cdef gen result -# try: -# result = GIAC_factor(self.gptr[0],context_ptr) -# return _wrap_gen(result) -# except: -# raise -# -# -# -#def giacfactors(Pygen self): -# cdef gen result -# try: -# result = GIAC_factors(self.gptr[0],context_ptr) -# return _wrap_gen(result) -# except: -# raise -# -# -# -# -#def giacnormal(Pygen self): -# cdef gen result -# try: -# result = GIAC_normal(self.gptr[0],context_ptr) -# return _wrap_gen(result) -# except: -# raise -# -# -#def giacgcd(Pygen a, Pygen b): -# cdef gen result -# try: -# result = gen( GIAC_makenewvecteur(a.gptr[0],b.gptr[0]) ,1) -# result = GIAC_gcd(result,context_ptr) -# return _wrap_gen(result) -# except: -# raise - - -############################################################# -# Most giac keywords -############################################################# -include 'keywords.pxi' -GiacMethods={} - - -class GiacFunction(Pygen): - # a class to evaluate args before call - """ - A Subclass of Pygen to create functions with evaluating all the args - before call so that they are substituted by their value. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: libgiac.simplify(exp(I*pi)) # simplify is a GiacFunction - -1 - sage: libgiac('a:=1') - 1 - sage: libgiac.purge('a') # purge is not a GiacFunction - 1 - sage: libgiac('a') - a - """ - def __call__(self, *args): - n = len(args) - if n == 1: - args = (Pygen(args[0]).eval(),) - return Pygen.__call__(self, *args) - - -class GiacFunctionNoEV(Pygen): - # a class to allow to write the __doc__ attribute. - """ - A Subclass of Pygen to create functions - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: libgiac('a:=1') - 1 - sage: libgiac.purge('a') # purge is a GiacFunctionNoEV - 1 - sage: libgiac('a') - a - """ - - -############################################################# -# Some convenient settings -############################################################ -Pygen('printpow(1)').eval() # default power is ^ -# FIXME: print I for sqrt(-1) instead of i -# GIAC_try_parse_i(False,context_ptr); (does not work??) - -NoEvArgsFunc=['purge','assume','quote'] - -for i in mostkeywords: - if i in NoEvArgsFunc: - # do not eval args before calling this function. Ex purge - #tmp=Pygen(i) - tmp = GiacFunctionNoEV(i) - else: - tmp = GiacFunction(i) - # in the sage version we remove: globals()[i]=tmp - GiacMethods[i] = tmp - -# We put the giac names that should not be exported to Python in moremethods. -for i in moremethods: - tmp = GiacFunction(i) - GiacMethods[i] = tmp - -for i in mostkeywords+moremethods: - GiacMethods[i].__doc__ = eval("Pygen." + i + ".__doc__") - -# To avoid conflicts we export only these few ones. Most giac keywords will be -# available through: libgiac.keywordname -__all__ = ['Pygen', 'giacsettings', 'libgiac', 'loadgiacgen', 'GiacFunction', - 'GiacMethods', 'GiacMethods_base'] - - -def loadgiacgen(str filename): - """ - Open a file in giac compressed format to create a Pygen element. - - Use the save method from Pygen elements to create such files. - - In C++ these files can be opened with giac::unarchive and created with - ``giac::archive``. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import * - sage: g=libgiac.texpand('cos(10*a+5*b)') - sage: g.save("fichiertest") # doctest: +SKIP - sage: a=loadgiacgen("fichiertest") # doctest: +SKIP - sage: from tempfile import NamedTemporaryFile - sage: F=NamedTemporaryFile() # chose a temporary file for a test - sage: g.savegen(F.name) - sage: a=loadgiacgen(F.name) - sage: a.tcollect() - cos(10*a+5*b) - sage: F.close() - """ - cdef gen result - sig_on() - result = GIAC_unarchive( encstring23(filename), context_ptr) - sig_off() - return _wrap_gen(result) - - -class GiacInstance: - """ - This class is used to create the giac interpreter object. - - EXAMPLES:: - - sage: from sage.libs.giac.giac import libgiac - sage: isinstance(libgiac,sage.libs.giac.giac.GiacInstance) - True - sage: libgiac.solve('2*exp(x)<(exp(x*2)-1),x') - list[x>(ln(sqrt(2)+1))] - sage: libgiac.solve? # doctest: +SKIP - ... - Docstring: - From Giac's documentation: - Help for solve: solve(Expr,[Var]) Solves a (or a set of) polynomial - ... - """ - - def __init__(self): - self.__dict__.update(GiacMethods) - - def __call__(self, s): - return _giac(s) - - def _sage_doc_(self): - return _giac.__doc__ - - def eval(self, code, strip=True, **kwds): - - if strip: - code = code.replace("\n","").strip() - return self(code) - - __doc__ = _giac.__doc__ - - -libgiac = GiacInstance() - -# Issue #23976 (bound threads with SAGE_NUM_THREADS) -import os -try: - ncpus = int(os.environ['SAGE_NUM_THREADS']) -except KeyError: - ncpus = 1 - -giacsettings.threads = ncpus diff --git a/src/sage/libs/giac/keywords.pxi b/src/sage/libs/giac/keywords.pxi deleted file mode 100644 index 9bc7eebe0b5..00000000000 --- a/src/sage/libs/giac/keywords.pxi +++ /dev/null @@ -1,9 +0,0 @@ -# file auto generated by mkkeywords.py -blacklist = ['eval', 'cas_setup', 'i', 'list', 'input', 'in', 'sto', 'string', 'and', 'break', 'continue', 'else', 'for', 'from', 'if', 'not', 'or', 'pow', 'print', 'return', 'set[]', 'try', 'while', 'open', 'output', 'do', 'of', 'Request', 'i[]', '[]', 'ffunction', 'sleep', '[..]'] - -toremove = ['!', '!=', '#', '$', '%', '/%', '%/', '%{%}', '&&', '&*', '&^', "'", '()', '*', '*=', '+', '-', '+&', '+=', '+infinity', '-<', '-=', '->', '-infinity', '.*', '.+', '.-', './', '.^', '/=', ':=', '<', '<=', '=', '=<', '==', '=>', '>', '>=', '?', '@', '@@', 'ACOSH', 'ACOT', 'ACSC', 'ASEC', 'ASIN', 'ASINH', 'ATAN', 'ATANH', 'COND', 'COS', 'COSH', 'COT', 'CSC', 'CST', 'Celsius2Fahrenheit', 'ClrDraw', 'ClrGraph', 'ClrIO', 'CyclePic', 'DIGITS', 'DOM_COMPLEX', 'DOM_FLOAT', 'DOM_FUNC', 'DOM_IDENT', 'DOM_INT', 'DOM_LIST', 'DOM_RAT', 'DOM_STRING', 'DOM_SYMBOLIC', 'DOM_int', 'DelFold', 'DelVar', 'Det', 'Dialog', 'Digits', 'Disp', 'DispG', 'DispHome', 'DrawFunc', 'DrawInv', 'DrawParm', 'DrawPol', 'DrawSlp', 'DropDown', 'DrwCtour', 'ERROR', 'EXP', 'EndDlog', 'FALSE', 'False', 'Fahrenheit2Celsius', 'Fill', 'Gcd', 'GetFold', 'Graph', 'IFTE', 'Input', 'InputStr', 'Int', 'Inverse', 'LN', 'LQ', 'LSQ', 'NORMALD', 'NewFold', 'NewPic', 'Nullspace', 'Output', 'Ox_2d_unit_vector', 'Ox_3d_unit_vector', 'Oy_2d_unit_vector', 'Oy_3d_unit_vector', 'Oz_3d_unit_vector', 'Pause', 'PopUp', 'Quo', 'REDIM', 'REPLACE', 'RclPic', 'Rem', 'Resultant', 'RplcPic', 'Rref', 'SCALE', 'SCALEADD', 'SCHUR', 'SIN', 'SVD', 'SVL', 'SWAPCOL', 'SWAPROW', 'SetFold', 'Si', 'StoPic', 'Store', 'TAN', 'TRUE', 'True', 'TeX', 'Text', 'Title', 'Unarchiv', 'WAIT', '^', '_(cm/s)', '_(ft/s)', '_(ft*lb)', '_(m/s)', '_(m/s^2)', '_(rad/s)', '_(rad/s^2)', '_(tr/min)', '_(tr/s)', '_A', '_Angstrom', '_Bq', '_Btu', '_Ci', '_F', '_F_', '_Fdy', '_G_', '_Gal', '_Gy', '_H', '_Hz', '_I0_', '_J', '_K', '_Kcal', '_MHz', '_MW', '_MeV', '_N', '_NA_', '_Ohm', '_P', '_PSun_', '_Pa', '_R', '_REarth_', '_RSun_', '_R_', '_Rankine', '_Rinfinity_', '_S', '_St', '_StdP_', '_StdT_', '_Sv', '_T', '_V', '_Vm_', '_W', '_Wb', '_Wh', '_a', '_a0_', '_acre', '_alpha_', '_angl_', '_arcmin', '_arcs', '_atm', '_au', '_b', '_bar', '_bbl', '_bblep', '_bu', '_buUS', '_c3_', '_c_', '_cal', '_cd', '_chain', '_cm', '_cm^2', '_cm^3', '_ct', '_cu', '_d', '_dB', '_deg', '_degreeF', '_dyn', '_eV', '_epsilon0_', '_epsilon0q_', '_epsilonox_', '_epsilonsi_', '_erg', '_f0_', '_fath', '_fbm', '_fc', '_fermi', '_flam', '_fm', '_ft', '_ft*lb', '_ftUS', '_ft^2', '_ft^3', '_g', '_g_', '_ga', '_galC', '_galUK', '_galUS', '_gf', '_gmol', '_gon', '_grad', '_grain', '_h', '_h_', '_ha', '_hbar_', '_hp', '_in', '_inH20', '_inHg', '_in^2', '_in^3', '_j', '_kWh', '_k_', '_kg', '_kip', '_km', '_km^2', '_knot', '_kph', '_kq_', '_l', '_lam', '_lambda0_', '_lambdac_', '_lb', '_lbf', '_lbmol', '_lbt', '_lep', '_liqpt', '_lm', '_lx', '_lyr', '_m', '_mEarth_', '_m^2', '_m^3', '_me_', '_mho', '_miUS', '_miUS^2', '_mi^2', '_mil', '_mile', '_mille', '_ml', '_mm', '_mmHg', '_mn', '_mol', '_mp_', '_mph', '_mpme_', '_mu0_', '_muB_', '_muN_', '_oz', '_ozUK', '_ozfl', '_ozt', '_pc', '_pdl', '_ph', '_phi_', '_pk', '_psi', '_ptUK', '_q_', '_qe_', '_qepsilon0_', '_qme_', '_qt', '_rad', '_rad_', '_rd', '_rem', '_rod', '_rpm', '_s', '_sb', '_sd_', '_sigma_', '_slug', '_sr', '_st', '_syr_', '_t', '_tbsp', '_tec', '_tep', '_tex', '_therm', '_ton', '_tonUK', '_torr', '_tr', '_tsp', '_twopi_', '_u', '_yd', '_yd^2', '_yd^3', '_yr', '_µ', '_µ', 'assert', 'affichage', 'alors', 'animate', 'animate3d', 'animation', 'approx_mode', 'archive', 'args', 'as_function_of', 'asc', 'asec', 'assign', 'backquote', 'begin', 'black', 'blanc', 'bleu', 'bloc', 'blue', 'breakpoint', 'by', 'c1oc2', 'c1op2', 'cache_tortue', 'cap', 'cap_flat_line', 'cap_round_line', 'cap_square_line', 'case', 'cat', 'catch', 'cd', 'choosebox', 'click', 'close', 'complex_mode', 'de', 'del', 'debug', 'default', 'div', 'double', 'ecris', 'efface', 'elif', 'end', 'end_for', 'end_if', 'end_while', 'epaisseur', 'epaisseur_ligne_1', 'epaisseur_ligne_2', 'epaisseur_ligne_3', 'epaisseur_ligne_4', 'epaisseur_ligne_5', 'epaisseur_ligne_6', 'epaisseur_ligne_7', 'epaisseur_point_1', 'epaisseur_point_2', 'epaisseur_point_3', 'epaisseur_point_4', 'epaisseur_point_5', 'epaisseur_point_6', 'epaisseur_point_7', 'erase', 'erase3d', 'est_cocyclique', 'est_inclus', 'et', 'faire', 'faux', 'feuille', 'ffaire', 'ffonction', 'fi', 'filled', 'fin_enregistrement', 'float', 'fonction', 'fopen', 'format', 'fpour', 'frame_3d', 'frames', 'fsi', 'ftantque', 'func', 'function', 'gauche', 'gl_ortho', 'gl_quaternion', 'gl_rotation', 'gl_shownames', 'gl_texture', 'gl_x', 'gl_x_axis_color', 'gl_x_axis_name', 'gl_x_axis_unit', 'gl_xtick', 'gl_y', 'gl_y_axis_color', 'gl_y_axis_name', 'gl_y_axis_unit', 'gl_ytick', 'gl_z', 'gl_z_axis_color', 'gl_z_axis_name', 'gl_z_axis_unit', 'gl_ztick', 'gnuplot', 'goto', 'graph2tex', 'graph3d2tex', 'graphe', 'graphe3d', 'graphe_probabiliste', 'graphe_suite', 'green', 'grid_paper', 'hidden_name', 'identifier', 'ifft', 'ifte', 'inputform', 'intersect', 'is_included', 'jusqu_a', 'jusqua', 'jusque', 'keep_algext', 'kill', 'label', 'labels', 'len', 'leve_crayon', 'line_width_1', 'line_width_2', 'line_width_3', 'line_width_4', 'line_width_5', 'line_width_6', 'line_width_7', 'lis', 'local', 'minus', 'mod', 'noir', 'nom_cache', 'non', 'od', 'option', 'otherwise', 'ou', 'pas', 'point_arret', 'point_carre', 'point_croix', 'point_div', 'point_etoile', 'point_invisible', 'point_losange', 'point_milieu', 'point_plus', 'point_point', 'point_polaire', 'point_triangle', 'point_width_1', 'point_width_2', 'point_width_3', 'point_width_4', 'point_width_5', 'point_width_6', 'point_width_7', 'pour', 'proc', 'program', 'quadrant1', 'quadrant2', 'quadrant3', 'quadrant4', 'range', 'redim', 'repeat', 'repete', 'repeter', 'replace', 'restart', 'rouge', 'saisir', 'saisir_chaine', 'sauve', 'save_history', 'scale', 'scaleadd', 'si', 'sinon', 'size', 'stack', 'step', 'switch', 'tantque', 'test', 'textinput', 'then', 'thiele', 'time', 'to', 'union', 'until', 'var', 'vector', 'vers', 'vert', 'vrai', 'watch', 'when', 'white', 'with_sqrt', 'write', 'wz_certificate', 'xor', 'yellow', '{}', '|', '||', 'expression'] - -moremethods = ['type', 'zip'] - -mostkeywords = ['Airy_Ai', 'Airy_Bi', 'Archive', 'BesselJ', 'BesselY', 'Beta', 'BlockDiagonal', 'Ci', 'Circle', 'Col', 'CopyVar', 'Dirac', 'Ei', 'Factor', 'GF', 'Gamma', 'Heaviside', 'JordanBlock', 'LU', 'LambertW', 'Li', 'Line', 'LineHorz', 'LineTan', 'LineVert', 'Phi', 'Pi', 'Psi', 'QR', 'RandSeed', 'Row', 'SortA', 'SortD', 'UTPC', 'UTPF', 'UTPN', 'UTPT', 'VARS', 'VAS', 'VAS_positive', 'Zeta', 'a2q', 'abcuv', 'about', 'abs', 'abscissa', 'accumulate_head_tail', 'acos', 'acos2asin', 'acos2atan', 'acosh', 'acot', 'acsc', 'acyclic', 'add', 'add_arc', 'add_edge', 'add_vertex', 'additionally', 'addtable', 'adjacency_matrix', 'adjoint_matrix', 'affix', 'algsubs', 'algvar', 'all_trig_solutions', 'allpairs_distance', 'alog10', 'altitude', 'angle', 'angle_radian', 'angleat', 'angleatraw', 'ans', 'antiprism_graph', 'append', 'apply', 'approx', 'arc', 'arcLen', 'arccos', 'arccosh', 'arclen', 'arcsin', 'arcsinh', 'arctan', 'arctanh', 'area', 'areaat', 'areaatraw', 'areaplot', 'arg', 'array', 'arrivals', 'articulation_points', 'asin', 'asin2acos', 'asin2atan', 'asinh', 'assign_edge_weights', 'assume', 'at', 'atan', 'atan2acos', 'atan2asin', 'atanh', 'atrig2ln', 'augment', 'auto_correlation', 'autosimplify', 'avance', 'avgRC', 'axes', 'axis', 'back', 'backward', 'baisse_crayon', 'bandwidth', 'bar_plot', 'barplot', 'bartlett_hann_window', 'barycenter', 'base', 'basis', 'batons', 'bellman_ford', 'bernoulli', 'besselJ', 'besselY', 'betad', 'betad_cdf', 'betad_icdf', 'betavariate', 'bezier', 'bezout_entiers', 'biconnected_components', 'binomial', 'binomial_cdf', 'binomial_icdf', 'bins', 'bipartite', 'bipartite_matching', 'bisection_solver', 'bisector', 'bit_depth', 'bitand', 'bitor', 'bitxor', 'blackman_harris_window', 'blackman_window', 'blockmatrix', 'bohman_window', 'border', 'boxcar', 'boxwhisker', 'brent_solver', 'bvpsolve', 'cFactor', 'cSolve', 'cZeros', 'camembert', 'canonical_form', 'canonical_labeling', 'cartesian_product', 'cauchy', 'cauchy_cdf', 'cauchy_icdf', 'cauchyd', 'cauchyd_cdf', 'cauchyd_icdf', 'cdf', 'ceil', 'ceiling', 'center', 'center2interval', 'centered_cube', 'centered_tetrahedron', 'cfactor', 'cfsolve', 'changebase', 'channel_data', 'channels', 'char', 'charpoly', 'chinrem', 'chisquare', 'chisquare_cdf', 'chisquare_icdf', 'chisquared', 'chisquared_cdf', 'chisquared_icdf', 'chisquaret', 'choice', 'cholesky', 'chr', 'chrem', 'chromatic_index', 'chromatic_number', 'chromatic_polynomial', 'circle', 'circumcircle', 'classes', 'clear', 'clique_cover', 'clique_cover_number', 'clique_number', 'clique_stats', 'clustering_coefficient', 'coeff', 'coeffs', 'col', 'colDim', 'colNorm', 'colSwap', 'coldim', 'collect', 'colnorm', 'color', 'colspace', 'colswap', 'comDenom', 'comb', 'combine', 'comment', 'common_perpendicular', 'companion', 'compare', 'complete_binary_tree', 'complete_graph', 'complete_kary_tree', 'complex', 'complex_variables', 'complexroot', 'concat', 'cond', 'condensation', 'cone', 'confrac', 'conic', 'conj', 'conjugate_equation', 'conjugate_gradient', 'connected', 'connected_components', 'cont', 'contains', 'content', 'contourplot', 'contract_edge', 'convert', 'convertir', 'convex', 'convexhull', 'convolution', 'coordinates', 'copy', 'correlation', 'cos', 'cos2sintan', 'cosh', 'cosine_window', 'cot', 'cote', 'count', 'count_eq', 'count_inf', 'count_sup', 'courbe_parametrique', 'courbe_polaire', 'covariance', 'covariance_correlation', 'cpartfrac', 'crationalroot', 'crayon', 'createwav', 'cross', 'crossP', 'cross_correlation', 'cross_point', 'cross_ratio', 'crossproduct', 'csc', 'csolve', 'csv2gen', 'cube', 'cumSum', 'cumsum', 'cumulated_frequencies', 'curl', 'current_sheet', 'curvature', 'curve', 'cyan', 'cycle2perm', 'cycle_graph', 'cycleinv', 'cycles2permu', 'cyclotomic', 'cylinder', 'dash_line', 'dashdot_line', 'dashdotdot_line', 'dayofweek', 'deSolve', 'debut_enregistrement', 'degree', 'degree_sequence', 'delcols', 'delete_arc', 'delete_edge', 'delete_vertex', 'delrows', 'deltalist', 'denom', 'densityplot', 'departures', 'derive', 'deriver', 'desolve', 'dessine_tortue', 'det', 'det_minor', 'developper', 'developper_transcendant', 'dfc', 'dfc2f', 'diag', 'diff', 'digraph', 'dijkstra', 'dim', 'directed', 'discard_edge_attribute', 'discard_graph_attribute', 'discard_vertex_attribute', 'disjoint_union', 'display', 'disque', 'disque_centre', 'distance', 'distance2', 'distanceat', 'distanceatraw', 'divergence', 'divide', 'divis', 'division_point', 'divisors', 'divmod', 'divpc', 'dnewton_solver', 'dodecahedron', 'domain', 'dot', 'dotP', 'dot_paper', 'dotprod', 'draw_arc', 'draw_circle', 'draw_graph', 'draw_line', 'draw_pixel', 'draw_polygon', 'draw_rectangle', 'droit', 'droite_tangente', 'dsolve', 'duration', 'e', 'e2r', 'ecart_type', 'ecart_type_population', 'ecm_factor', 'edge_connectivity', 'edges', 'egcd', 'egv', 'egvl', 'eigVc', 'eigVl', 'eigenvals', 'eigenvalues', 'eigenvectors', 'eigenvects', 'element', 'eliminate', 'ellipse', 'entry', 'envelope', 'epsilon', 'epsilon2zero', 'equal', 'equal2diff', 'equal2list', 'equation', 'equilateral_triangle', 'erf', 'erfc', 'error', 'est_permu', 'euler', 'euler_gamma', 'euler_lagrange', 'eval_level', 'evala', 'evalb', 'evalc', 'evalf', 'evalm', 'even', 'evolute', 'exact', 'exbisector', 'excircle', 'execute', 'exp', 'exp2list', 'exp2pow', 'exp2trig', 'expand', 'expexpand', 'expln', 'exponential', 'exponential_cdf', 'exponential_icdf', 'exponential_regression', 'exponential_regression_plot', 'exponentiald', 'exponentiald_cdf', 'exponentiald_icdf', 'export_graph', 'export_mathml', 'expovariate', 'expr', 'extend', 'extract_measure', 'extrema', 'ezgcd', 'f2nd', 'fMax', 'fMin', 'fPart', 'faces', 'facteurs_premiers', 'factor', 'factor_xn', 'factorial', 'factoriser', 'factoriser_entier', 'factoriser_sur_C', 'factors', 'fadeev', 'false', 'falsepos_solver', 'fclose', 'fcoeff', 'fdistrib', 'fft', 'fieldplot', 'find', 'find_cycles', 'findhelp', 'fisher', 'fisher_cdf', 'fisher_icdf', 'fisherd', 'fisherd_cdf', 'fisherd_icdf', 'fitdistr', 'flatten', 'float2rational', 'floor', 'flow_polynomial', 'fmod', 'foldl', 'foldr', 'fonction_derivee', 'forward', 'fourier', 'fourier_an', 'fourier_bn', 'fourier_cn', 'fprint', 'frac', 'fracmod', 'frame_2d', 'frequencies', 'frobenius_norm', 'froot', 'fsolve', 'fullparfrac', 'funcplot', 'function_diff', 'fxnd', 'gammad', 'gammad_cdf', 'gammad_icdf', 'gammavariate', 'gauss', 'gauss15', 'gauss_seidel_linsolve', 'gaussian_window', 'gaussjord', 'gaussquad', 'gbasis', 'gbasis_max_pairs', 'gbasis_reinject', 'gbasis_simult_primes', 'gcd', 'gcdex', 'genpoly', 'geometric', 'geometric_cdf', 'geometric_icdf', 'getDenom', 'getKey', 'getNum', 'getType', 'get_edge_attribute', 'get_edge_weight', 'get_graph_attribute', 'get_vertex_attribute', 'girth', 'gl_showaxes', 'grad', 'gramschmidt', 'graph', 'graph_automorphisms', 'graph_charpoly', 'graph_complement', 'graph_diameter', 'graph_equal', 'graph_join', 'graph_power', 'graph_rank', 'graph_spectrum', 'graph_union', 'graph_vertices', 'greduce', 'greedy_color', 'grid_graph', 'groupermu', 'hadamard', 'half_cone', 'half_line', 'halftan', 'halftan_hyp2exp', 'halt', 'hamdist', 'hamming_window', 'hann_poisson_window', 'hann_window', 'harmonic_conjugate', 'harmonic_division', 'has', 'has_arc', 'has_edge', 'hasard', 'head', 'heading', 'heapify', 'heappop', 'heappush', 'hermite', 'hessenberg', 'hessian', 'heugcd', 'hexagon', 'highlight_edges', 'highlight_subgraph', 'highlight_trail', 'highlight_vertex', 'highpass', 'hilbert', 'histogram', 'hold', 'homogeneize', 'homothety', 'horner', 'hybrid_solver', 'hybridj_solver', 'hybrids_solver', 'hybridsj_solver', 'hyp2exp', 'hyperbola', 'hypercube_graph', 'iPart', 'iabcuv', 'ibasis', 'ibpdv', 'ibpu', 'icdf', 'ichinrem', 'ichrem', 'icomp', 'icontent', 'icosahedron', 'id', 'identity', 'idivis', 'idn', 'iegcd', 'ifactor', 'ifactors', 'ifourier', 'igamma', 'igcd', 'igcdex', 'ihermite', 'ilaplace', 'im', 'imag', 'image', 'implicitdiff', 'implicitplot', 'import_graph', 'inString', 'in_ideal', 'incidence_matrix', 'incident_edges', 'incircle', 'increasing_power', 'independence_number', 'indets', 'index', 'induced_subgraph', 'inequationplot', 'inf', 'infinity', 'insert', 'insmod', 'int', 'intDiv', 'integer', 'integrate', 'integrer', 'inter', 'interactive_odeplot', 'interactive_plotode', 'interp', 'interval', 'interval2center', 'interval_graph', 'inv', 'inverse', 'inversion', 'invisible_point', 'invlaplace', 'invztrans', 'iquo', 'iquorem', 'iratrecon', 'irem', 'isPrime', 'is_acyclic', 'is_arborescence', 'is_biconnected', 'is_bipartite', 'is_clique', 'is_collinear', 'is_concyclic', 'is_conjugate', 'is_connected', 'is_coplanar', 'is_cospherical', 'is_cut_set', 'is_cycle', 'is_directed', 'is_element', 'is_equilateral', 'is_eulerian', 'is_forest', 'is_graphic_sequence', 'is_hamiltonian', 'is_harmonic', 'is_harmonic_circle_bundle', 'is_harmonic_line_bundle', 'is_inside', 'is_integer_graph', 'is_isomorphic', 'is_isosceles', 'is_network', 'is_orthogonal', 'is_parallel', 'is_parallelogram', 'is_permu', 'is_perpendicular', 'is_planar', 'is_prime', 'is_pseudoprime', 'is_rectangle', 'is_regular', 'is_rhombus', 'is_square', 'is_strongly_connected', 'is_strongly_regular', 'is_tournament', 'is_tree', 'is_triconnected', 'is_two_edge_connected', 'is_vertex_colorable', 'is_weighted', 'ismith', 'isobarycenter', 'isom', 'isomorphic_copy', 'isopolygon', 'isosceles_triangle', 'isprime', 'ithprime', 'jacobi_equation', 'jacobi_linsolve', 'jacobi_symbol', 'jordan', 'kde', 'keep_pivot', 'ker', 'kernel', 'kernel_density', 'kneser_graph', 'kolmogorovd', 'kolmogorovt', 'kovacicsols', 'kspaths', 'l1norm', 'l2norm', 'lagrange', 'laguerre', 'laplace', 'laplacian', 'laplacian_matrix', 'latex', 'lcf_graph', 'lcm', 'lcoeff', 'ldegree', 'left', 'left_rectangle', 'legend', 'legendre', 'legendre_symbol', 'length', 'lgcd', 'lhs', 'ligne_chapeau_carre', 'ligne_chapeau_plat', 'ligne_chapeau_rond', 'ligne_polygonale', 'ligne_polygonale_pointee', 'ligne_tiret', 'ligne_tiret_point', 'ligne_tiret_pointpoint', 'ligne_trait_plein', 'limit', 'limite', 'lin', 'line', 'line_graph', 'line_inter', 'line_paper', 'line_segments', 'linear_interpolate', 'linear_regression', 'linear_regression_plot', 'lineariser', 'lineariser_trigo', 'linfnorm', 'linsolve', 'linspace', 'lis_phrase', 'list2exp', 'list2mat', 'list_edge_attributes', 'list_graph_attributes', 'list_vertex_attributes', 'listplot', 'lll', 'ln', 'lname', 'lncollect', 'lnexpand', 'locus', 'log', 'log10', 'logarithmic_regression', 'logarithmic_regression_plot', 'logb', 'logistic_regression', 'logistic_regression_plot', 'lower', 'lowest_common_ancestor', 'lowpass', 'lp_assume', 'lp_bestprojection', 'lp_binary', 'lp_binaryvariables', 'lp_breadthfirst', 'lp_depthfirst', 'lp_depthlimit', 'lp_firstfractional', 'lp_gaptolerance', 'lp_hybrid', 'lp_initialpoint', 'lp_integer', 'lp_integertolerance', 'lp_integervariables', 'lp_interiorpoint', 'lp_iterationlimit', 'lp_lastfractional', 'lp_maxcuts', 'lp_maximize', 'lp_method', 'lp_mostfractional', 'lp_nodelimit', 'lp_nodeselect', 'lp_nonnegative', 'lp_nonnegint', 'lp_pseudocost', 'lp_simplex', 'lp_timelimit', 'lp_variables', 'lp_varselect', 'lp_verbose', 'lpsolve', 'lsmod', 'lsq', 'lu', 'lvar', 'mRow', 'mRowAdd', 'magenta', 'make_directed', 'make_weighted', 'makelist', 'makemat', 'makesuite', 'makevector', 'map', 'maple2mupad', 'maple2xcas', 'maple_ifactors', 'maple_mode', 'markov', 'mat2list', 'mathml', 'matpow', 'matrix', 'matrix_norm', 'max', 'maxflow', 'maximal_independent_set', 'maximize', 'maximum_clique', 'maximum_degree', 'maximum_independent_set', 'maximum_matching', 'maxnorm', 'mean', 'median', 'median_line', 'member', 'mgf', 'mid', 'middle_point', 'midpoint', 'min', 'minimal_edge_coloring', 'minimal_spanning_tree', 'minimal_vertex_coloring', 'minimax', 'minimize', 'minimum_cut', 'minimum_degree', 'mkisom', 'mksa', 'modgcd', 'mods', 'monotonic', 'montre_tortue', 'moustache', 'moving_average', 'moyal', 'moyenne', 'mul', 'mult_c_conjugate', 'mult_conjugate', 'multinomial', 'multiplier_conjugue', 'multiplier_conjugue_complexe', 'multiply', 'mupad2maple', 'mupad2xcas', 'mycielski', 'nCr', 'nDeriv', 'nInt', 'nPr', 'nSolve', 'ncols', 'negbinomial', 'negbinomial_cdf', 'negbinomial_icdf', 'neighbors', 'network_transitivity', 'newList', 'newMat', 'newton', 'newton_solver', 'newtonj_solver', 'nextperm', 'nextprime', 'nlpsolve', 'nodisp', 'non_recursive_normal', 'nop', 'nops', 'norm', 'normal', 'normal_cdf', 'normal_icdf', 'normald', 'normald_cdf', 'normald_icdf', 'normalize', 'normalt', 'normalvariate', 'nprimes', 'nrows', 'nuage_points', 'nullspace', 'number_of_edges', 'number_of_spanning_trees', 'number_of_triangles', 'number_of_vertices', 'numer', 'octahedron', 'odd', 'odd_girth', 'odd_graph', 'odeplot', 'odesolve', 'op', 'open_polygon', 'ord', 'order', 'order_size', 'ordinate', 'orthocenter', 'orthogonal', 'osculating_circle', 'p1oc2', 'p1op2', 'pa2b2', 'pade', 'parabola', 'parallel', 'parallelepiped', 'parallelogram', 'parameq', 'parameter', 'paramplot', 'parfrac', 'pari', 'part', 'partfrac', 'parzen_window', 'pas_de_cote', 'path_graph', 'pcar', 'pcar_hessenberg', 'pcoef', 'pcoeff', 'pencolor', 'pendown', 'penup', 'perimeter', 'perimeterat', 'perimeteratraw', 'periodic', 'perm', 'perminv', 'permu2cycles', 'permu2mat', 'permuorder', 'permute_vertices', 'perpen_bisector', 'perpendicular', 'petersen_graph', 'peval', 'pi', 'pie', 'piecewise', 'pivot', 'pixoff', 'pixon', 'planar', 'plane', 'plane_dual', 'playsnd', 'plex', 'plot', 'plot3d', 'plotarea', 'plotcdf', 'plotcontour', 'plotdensity', 'plotfield', 'plotfunc', 'plotimplicit', 'plotinequation', 'plotlist', 'plotode', 'plotparam', 'plotpolar', 'plotproba', 'plotseq', 'plotspectrum', 'plotwav', 'plus_point', 'pmin', 'point', 'point2d', 'point3d', 'poisson', 'poisson_cdf', 'poisson_icdf', 'poisson_window', 'polar', 'polar_coordinates', 'polar_point', 'polarplot', 'pole', 'poly2symb', 'polyEval', 'polygon', 'polygone_rempli', 'polygonplot', 'polygonscatterplot', 'polyhedron', 'polynom', 'polynomial_regression', 'polynomial_regression_plot', 'position', 'poslbdLMQ', 'posubLMQ', 'potential', 'pow2exp', 'power_regression', 'power_regression_plot', 'powermod', 'powerpc', 'powexpand', 'powmod', 'prepend', 'preval', 'prevperm', 'prevprime', 'primpart', 'printf', 'prism', 'prism_graph', 'product', 'projection', 'proot', 'propFrac', 'propfrac', 'psrgcd', 'ptayl', 'purge', 'pwd', 'pyramid', 'python_compat', 'q2a', 'qr', 'quadric', 'quadrilateral', 'quantile', 'quartile1', 'quartile3', 'quartiles', 'quest', 'quo', 'quorem', 'quote', 'r2e', 'radical_axis', 'radius', 'ramene', 'rand', 'randMat', 'randNorm', 'randPoly', 'randbetad', 'randbinomial', 'randchisquare', 'randexp', 'randfisher', 'randgammad', 'randgeometric', 'randint', 'randmarkov', 'randmatrix', 'randmultinomial', 'randnorm', 'random', 'random_bipartite_graph', 'random_digraph', 'random_graph', 'random_network', 'random_planar_graph', 'random_regular_graph', 'random_sequence_graph', 'random_tournament', 'random_tree', 'random_variable', 'randperm', 'randpoisson', 'randpoly', 'randseed', 'randstudent', 'randvar', 'randvector', 'randweibulld', 'rank', 'ranm', 'ranv', 'rassembler_trigo', 'rat_jordan', 'rational', 'rationalroot', 'ratnormal', 'rdiv', 're', 'read', 'readrgb', 'readwav', 'real', 'realroot', 'reciprocation', 'rect', 'rectangle', 'rectangle_droit', 'rectangle_gauche', 'rectangle_plein', 'rectangular_coordinates', 'recule', 'red', 'reduced_conic', 'reduced_quadric', 'ref', 'reflection', 'regroup', 'relabel_vertices', 'reliability_polynomial', 'rem', 'remain', 'remove', 'reorder', 'resample', 'residue', 'resoudre', 'resoudre_dans_C', 'resoudre_systeme_lineaire', 'resultant', 'reverse', 'reverse_graph', 'reverse_rsolve', 'revert', 'revlex', 'revlist', 'rgb', 'rhombus', 'rhombus_point', 'rhs', 'riemann_window', 'right', 'right_rectangle', 'right_triangle', 'risch', 'rm_a_z', 'rm_all_vars', 'rmbreakpoint', 'rmmod', 'rmwatch', 'romberg', 'rombergm', 'rombergt', 'rond', 'root', 'rootof', 'roots', 'rotate', 'rotation', 'round', 'row', 'rowAdd', 'rowDim', 'rowNorm', 'rowSwap', 'rowdim', 'rownorm', 'rowspace', 'rowswap', 'rref', 'rsolve', 'same', 'sample', 'samplerate', 'sans_factoriser', 'saute', 'scalarProduct', 'scalar_product', 'scatterplot', 'schur', 'sec', 'secant_solver', 'segment', 'seidel_spectrum', 'seidel_switch', 'select', 'semi_augment', 'seq', 'seqplot', 'seqsolve', 'sequence_graph', 'series', 'set_edge_attribute', 'set_edge_weight', 'set_graph_attribute', 'set_pixel', 'set_vertex_attribute', 'set_vertex_positions', 'shift', 'shift_phase', 'shortest_path', 'show_pixels', 'shuffle', 'sierpinski_graph', 'sign', 'signature', 'signe', 'similarity', 'simp2', 'simplex_reduce', 'simplifier', 'simplify', 'simpson', 'simult', 'sin', 'sin2costan', 'sinc', 'sincos', 'single_inter', 'sinh', 'sizes', 'slope', 'slopeat', 'slopeatraw', 'smith', 'smod', 'snedecor', 'snedecor_cdf', 'snedecor_icdf', 'snedecord', 'snedecord_cdf', 'snedecord_icdf', 'solid_line', 'solve', 'somme', 'sommet', 'sort', 'sorta', 'sortd', 'sorted', 'soundsec', 'spanning_tree', 'sphere', 'spline', 'split', 'spring', 'sq', 'sqrfree', 'sqrt', 'square', 'square_point', 'srand', 'sst', 'sst_in', 'st_ordering', 'star_graph', 'star_point', 'stdDev', 'stddev', 'stddevp', 'steffenson_solver', 'stereo2mono', 'str', 'strongly_connected_components', 'student', 'student_cdf', 'student_icdf', 'studentd', 'studentt', 'sturm', 'sturmab', 'sturmseq', 'style', 'subMat', 'subdivide_edges', 'subgraph', 'subs', 'subsop', 'subst', 'substituer', 'subtype', 'sum', 'sum_riemann', 'suppress', 'surd', 'svd', 'swapcol', 'swaprow', 'switch_axes', 'sylvester', 'symb2poly', 'symbol', 'syst2mat', 'tCollect', 'tExpand', 'table', 'tablefunc', 'tableseq', 'tabsign', 'tabvar', 'tail', 'tan', 'tan2cossin2', 'tan2sincos', 'tan2sincos2', 'tangent', 'tangente', 'tanh', 'taux_accroissement', 'taylor', 'tchebyshev1', 'tchebyshev2', 'tcoeff', 'tcollect', 'tdeg', 'tensor_product', 'tetrahedron', 'texpand', 'thickness', 'threshold', 'throw', 'title', 'titre', 'tlin', 'tonnetz', 'topologic_sort', 'topological_sort', 'torus_grid_graph', 'total_degree', 'tourne_droite', 'tourne_gauche', 'tpsolve', 'trace', 'trail', 'trail2edges', 'trames', 'tran', 'transitive_closure', 'translation', 'transpose', 'trapeze', 'trapezoid', 'traveling_salesman', 'tree', 'tree_height', 'tri', 'triangle', 'triangle_paper', 'triangle_plein', 'triangle_point', 'triangle_window', 'trig2exp', 'trigcos', 'trigexpand', 'triginterp', 'trigsimplify', 'trigsin', 'trigtan', 'trn', 'true', 'trunc', 'truncate', 'truncate_graph', 'tsimplify', 'tuer', 'tukey_window', 'tutte_polynomial', 'two_edge_connected_components', 'ufactor', 'ugamma', 'unapply', 'unarchive', 'underlying_graph', 'unfactored', 'uniform', 'uniform_cdf', 'uniform_icdf', 'uniformd', 'uniformd_cdf', 'uniformd_icdf', 'unitV', 'unquote', 'upper', 'user_operator', 'usimplify', 'valuation', 'vandermonde', 'variables_are_files', 'variance', 'version', 'vertex_connectivity', 'vertex_degree', 'vertex_distance', 'vertex_in_degree', 'vertex_out_degree', 'vertices', 'vertices_abc', 'vertices_abca', 'vpotential', 'web_graph', 'weibull', 'weibull_cdf', 'weibull_icdf', 'weibulld', 'weibulld_cdf', 'weibulld_icdf', 'weibullvariate', 'weight_matrix', 'weighted', 'weights', 'welch_window', 'wheel_graph', 'widget_size', 'wilcoxonp', 'wilcoxons', 'wilcoxont', 'writergb', 'writewav', 'xcas_mode', 'xml_print', 'xyztrange', 'zeros', 'ztrans'] - diff --git a/src/sage/libs/giac/meson.build b/src/sage/libs/giac/meson.build deleted file mode 100644 index 6dda5a6c8a7..00000000000 --- a/src/sage/libs/giac/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -giac = cc.find_library('giac', required: false, disabler: true) - -py.install_sources('__init__.py', 'giac.pxd', 'misc.h', subdir: 'sage/libs/giac') - -extension_data_cpp = {'giac': files('giac.pyx')} - -foreach name, pyx : extension_data_cpp - py.extension_module( - name, - sources: pyx, - subdir: 'sage/libs/giac', - install: true, - override_options: ['cython_language=cpp'], - include_directories: [inc_cpython, inc_ext, inc_rings], - dependencies: [py_dep, cysignals, giac, gmp], - ) -endforeach diff --git a/src/sage/libs/giac/misc.h b/src/sage/libs/giac/misc.h deleted file mode 100644 index af92cf93c9a..00000000000 --- a/src/sage/libs/giac/misc.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef GIACPYMISC_H -#define GIACPYMISC_H -#include - -#include - -using namespace std; -using namespace giac; - -inline void ressetctrl_c(void){ -giac::ctrl_c=false; -} - -inline int testctrl_c(void){ - if(giac::ctrl_c){ - return 1;} - else{ - return 0; - } -} - -/* -int giacgencmp(gen & a, gen & b, const context * context_ptr){ - if(a==b){return 0;} - else{ if (is_strictly_greater(a,b,context_ptr)){return 1;} - else{return -1;} - } -} -*/ - -int giacgenrichcmp(gen & a, gen & b, int op, const context * context_ptr){ - int rep=0;//be careful with undef results - switch ( op ) - { - case 0 : //< - if (is_strictly_greater(b,a,context_ptr)) {rep = 1;} - break; - case 1 : //<= - if (is_greater(b,a,context_ptr)) {rep = 1;} - break; - case 2 ://== - if (operator_equal(b,a,context_ptr)) {rep = 1;} - break; - case 3 ://!= - if (!operator_equal(b,a,context_ptr)) {rep = 1;} - break; - case 4 ://> - if (is_strictly_greater(a,b,context_ptr)) {rep = 1;} - break;//>= - case 5 : - if (is_greater(a,b,context_ptr)) {rep = 1;} - break; - default : - rep=0; - }; - return rep; -} - -gen giacmul(gen & a, gen & b, const context * context_ptr){ - return eval(a*b,context_ptr); -} - -gen giacdiv(gen & a, gen & b, const context * context_ptr){ - return eval(a/b,context_ptr); -} - -gen giacmod(gen & a, gen & b, const context * context_ptr){ - if(b != 0) - return eval(a * makemod(1,b),context_ptr); - else - return eval(makemod(a,b),context_ptr); -} - -int htmlbrowserhelp(char * s){ - if (system_browser_command(s)) - { return 0;} - else - {return 1;} -} - -string browser_help(const giac::gen & g, int language){ - giac::gen f(g); - string s; - giac::html_help_init("aide_cas",language,true,true); - if (f.type==giac::_SYMB) - f=f._SYMBptr->sommet; - if (f.type==giac::_FUNC) - s=f._FUNCptr->ptr()->s; - giac::html_vtt=giac::html_help(giac::html_mtt,s); - if (!giac::html_vtt.empty()){ - //giac::system_browser_command(giac::html_vtt.front()); - return giac::html_vtt.front(); - } - else{ - return ""; - } -} - - -void archivegen( const string filename, const gen & g, const context * context_ptr){ - ofstream of(filename.c_str()); - giac::archive(of,g,context_ptr); - of.close(); -} - -gen unarchivegen( const string filename, const context * context_ptr){ - ifstream f(filename.c_str()); - gen g=giac::unarchive(f,context_ptr); - f.close(); - return g; -} - - -#endif - From 9269c3cb974ed5ac3b5c54e5847de0aae9e8cd1d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:48:13 -0500 Subject: [PATCH 481/507] src/sage_setup/library_order.py: drop giac from library_order_list SageMath no longer links against libgiac directly. --- src/sage_setup/library_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/library_order.py b/src/sage_setup/library_order.py index 01b4ab0ff03..3a2321e7760 100644 --- a/src/sage_setup/library_order.py +++ b/src/sage_setup/library_order.py @@ -16,7 +16,7 @@ aliases = cython_aliases(required_modules=(), optional_modules=modules) library_order_list = aliases.get("SINGULAR_LIBRARIES", []) + [ - "giac", "intl", "curl", + "intl", "curl", "ec", "ecm" ] + aliases.get("LINBOX_LIBRARIES", []) + aliases.get("FFLASFFPACK_LIBRARIES", []) + aliases.get("GSL_LIBRARIES", []) + [ "pari", "flint", "ecl", "glpk", "ppl", From 3734d30b7ac66c907eb26598143433fa503be560 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:52:53 -0500 Subject: [PATCH 482/507] src/doc/en/reference/libs/index.rst: remove sage/libs/giac This directory has been removed (into a separate package). --- src/doc/en/reference/libs/index.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index b7b8fc0abe0..fa1fd8d1de6 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -48,13 +48,6 @@ FLINT sage/libs/flint/qsieve_sage sage/libs/flint/ulong_extras_sage -Giac ----- -.. toctree:: - :maxdepth: 1 - - sage/libs/giac - GMP-ECM ------- .. toctree:: From 84c71f257c0a64ac44ed78cfa8445d8f1d7d64ba Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:54:00 -0500 Subject: [PATCH 483/507] build/pkgs/sagelib/dependencies: remove giac The sage library can now be built without giac. --- build/pkgs/sagelib/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index b1ebfd0825e..a88194b16c0 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran +FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran ---------- All lines of this file are ignored except the first. From 007a1a5c76d94eeb25f365074c85b38bb63cfcb2 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:54:46 -0500 Subject: [PATCH 484/507] src/pyproject.toml: drop giac dependency The sage library no longer requires giac. --- src/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pyproject.toml b/src/pyproject.toml index 4b70dc133d1..14386494930 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -47,7 +47,6 @@ host-requires = [ "pkg:generic/libgd", "pkg:generic/gap", "pkg:generic/gfan", - "pkg:generic/giac", "pkg:generic/givaro", "pkg:generic/glpk", "pkg:generic/gmp", From 60a42a342680d04252ec7bb165a61be4a775cb2a Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 06:57:47 -0500 Subject: [PATCH 485/507] src/sage_setup/autogen/giacpy-mkkeywords.py: remove This helper script has been moved to the sagemath-giac package. --- src/sage_setup/autogen/giacpy-mkkeywords.py | 96 --------------------- 1 file changed, 96 deletions(-) delete mode 100644 src/sage_setup/autogen/giacpy-mkkeywords.py diff --git a/src/sage_setup/autogen/giacpy-mkkeywords.py b/src/sage_setup/autogen/giacpy-mkkeywords.py deleted file mode 100644 index 48e9c033736..00000000000 --- a/src/sage_setup/autogen/giacpy-mkkeywords.py +++ /dev/null @@ -1,96 +0,0 @@ -# see the discussion in trac #29171 about where to store this file. -""" -This file is to help the maintainer to upgrade the giac keywords in - - giacpy or giacpy_sage or sage.libs.giac - -It creates auto-methods.pxi, keywords.pxi, newkeyword.pxi -It needs: - -- the ``cas_help`` program from a giac installation -- the ``aide_cas.txt`` file that you can built yourself like this:: - - grep -E '^#' share/giac/doc/aide_cas |sed -e 's/^# //' >aide_cas.txt - -It should not be used on the fly for an automatic installation script, because auto-methods.pxi is -quite long to be built and adding new giac keywords often break the built of giacpy -while not implementing a new giac keyword doesn't break the giacpy built. - -newkeywords.pxi is just created to check things manually but is not used in iacpy or sage.libs.giac - -AUTHORS: - -- Frederic Han (2020-07) -""" -from subprocess import PIPE, Popen - -blacklist = ['eval', 'cas_setup', 'i', 'list', 'input', 'in', 'sto', 'string', 'and', 'break', 'continue', 'else', 'for', 'from', 'if', 'not', 'or', 'pow', 'print', 'return', 'set[]', 'try', 'while', 'open', 'output', 'do', 'of', 'Request', 'i[]', '[]', 'ffunction', 'sleep', '[..]'] - -toremove = ['!', '!=', '#', '$', '%', '/%', '%/', '%{%}', '&&', '&*', '&^', "'", '()', '*', '*=', '+', '-', '+&', '+=', '+infinity', '-<', '-=', '->', '-infinity', '.*', '.+', '.-', './', '.^', '/=', ':=', '<', '<=', '=', '=<', '==', '=>', '>', '>=', '?', '@', '@@', 'ACOSH', 'ACOT', 'ACSC', 'ASEC', 'ASIN', 'ASINH', 'ATAN', 'ATANH', 'COND', 'COS', 'COSH', 'COT', 'CSC', 'CST', 'Celsius2Fahrenheit', 'ClrDraw', 'ClrGraph', 'ClrIO', 'CyclePic', 'DIGITS', 'DOM_COMPLEX', 'DOM_FLOAT', 'DOM_FUNC', 'DOM_IDENT', 'DOM_INT', 'DOM_LIST', 'DOM_RAT', 'DOM_STRING', 'DOM_SYMBOLIC', 'DOM_int', 'DelFold', 'DelVar', 'Det', 'Dialog', 'Digits', 'Disp', 'DispG', 'DispHome', 'DrawFunc', 'DrawInv', 'DrawParm', 'DrawPol', 'DrawSlp', 'DropDown', 'DrwCtour', 'ERROR', 'EXP', 'EndDlog', 'FALSE', 'False', 'Fahrenheit2Celsius', 'Fill', 'Gcd', 'GetFold', 'Graph', 'IFTE', 'Input', 'InputStr', 'Int', 'Inverse', 'LN', 'LQ', 'LSQ', 'NORMALD', 'NewFold', 'NewPic', 'Nullspace', 'Output', 'Ox_2d_unit_vector', 'Ox_3d_unit_vector', 'Oy_2d_unit_vector', 'Oy_3d_unit_vector', 'Oz_3d_unit_vector', 'Pause', 'PopUp', 'Quo', 'REDIM', 'REPLACE', 'RclPic', 'Rem', 'Resultant', 'RplcPic', 'Rref', 'SCALE', 'SCALEADD', 'SCHUR', 'SIN', 'SVD', 'SVL', 'SWAPCOL', 'SWAPROW', 'SetFold', 'Si', 'StoPic', 'Store', 'TAN', 'TRUE', 'True', 'TeX', 'Text', 'Title', 'Unarchiv', 'WAIT', '^', '_(cm/s)', '_(ft/s)', '_(ft*lb)', '_(m/s)', '_(m/s^2)', '_(rad/s)', '_(rad/s^2)', '_(tr/min)', '_(tr/s)', '_A', '_Angstrom', '_Bq', '_Btu', '_Ci', '_F', '_F_', '_Fdy', '_G_', '_Gal', '_Gy', '_H', '_Hz', '_I0_', '_J', '_K', '_Kcal', '_MHz', '_MW', '_MeV', '_N', '_NA_', '_Ohm', '_P', '_PSun_', '_Pa', '_R', '_REarth_', '_RSun_', '_R_', '_Rankine', '_Rinfinity_', '_S', '_St', '_StdP_', '_StdT_', '_Sv', '_T', '_V', '_Vm_', '_W', '_Wb', '_Wh', '_a', '_a0_', '_acre', '_alpha_', '_angl_', '_arcmin', '_arcs', '_atm', '_au', '_b', '_bar', '_bbl', '_bblep', '_bu', '_buUS', '_c3_', '_c_', '_cal', '_cd', '_chain', '_cm', '_cm^2', '_cm^3', '_ct', '_cu', '_d', '_dB', '_deg', '_degreeF', '_dyn', '_eV', '_epsilon0_', '_epsilon0q_', '_epsilonox_', '_epsilonsi_', '_erg', '_f0_', '_fath', '_fbm', '_fc', '_fermi', '_flam', '_fm', '_ft', '_ft*lb', '_ftUS', '_ft^2', '_ft^3', '_g', '_g_', '_ga', '_galC', '_galUK', '_galUS', '_gf', '_gmol', '_gon', '_grad', '_grain', '_h', '_h_', '_ha', '_hbar_', '_hp', '_in', '_inH20', '_inHg', '_in^2', '_in^3', '_j', '_kWh', '_k_', '_kg', '_kip', '_km', '_km^2', '_knot', '_kph', '_kq_', '_l', '_lam', '_lambda0_', '_lambdac_', '_lb', '_lbf', '_lbmol', '_lbt', '_lep', '_liqpt', '_lm', '_lx', '_lyr', '_m', '_mEarth_', '_m^2', '_m^3', '_me_', '_mho', '_miUS', '_miUS^2', '_mi^2', '_mil', '_mile', '_mille', '_ml', '_mm', '_mmHg', '_mn', '_mol', '_mp_', '_mph', '_mpme_', '_mu0_', '_muB_', '_muN_', '_oz', '_ozUK', '_ozfl', '_ozt', '_pc', '_pdl', '_ph', '_phi_', '_pk', '_psi', '_ptUK', '_q_', '_qe_', '_qepsilon0_', '_qme_', '_qt', '_rad', '_rad_', '_rd', '_rem', '_rod', '_rpm', '_s', '_sb', '_sd_', '_sigma_', '_slug', '_sr', '_st', '_syr_', '_t', '_tbsp', '_tec', '_tep', '_tex', '_therm', '_ton', '_tonUK', '_torr', '_tr', '_tsp', '_twopi_', '_u', '_yd', '_yd^2', '_yd^3', '_yr', '_\xc2\xb5', '_µ', 'assert', 'affichage', 'alors', 'animate', 'animate3d', 'animation', 'approx_mode', 'archive', 'args', 'as_function_of', 'asc', 'asec', 'assign', 'backquote', 'begin', 'black', 'blanc', 'bleu', 'bloc', 'blue', 'breakpoint', 'by', 'c1oc2', 'c1op2', 'cache_tortue', 'cap', 'cap_flat_line', 'cap_round_line', 'cap_square_line', 'case', 'cat', 'catch', 'cd', 'choosebox', 'click', 'close', 'complex_mode', 'de', 'del', 'debug', 'default', 'div', 'double', 'ecris', 'efface', 'elif', 'end', 'end_for', 'end_if', 'end_while', 'epaisseur', 'epaisseur_ligne_1', 'epaisseur_ligne_2', 'epaisseur_ligne_3', 'epaisseur_ligne_4', 'epaisseur_ligne_5', 'epaisseur_ligne_6', 'epaisseur_ligne_7', 'epaisseur_point_1', 'epaisseur_point_2', 'epaisseur_point_3', 'epaisseur_point_4', 'epaisseur_point_5', 'epaisseur_point_6', 'epaisseur_point_7', 'erase', 'erase3d', 'est_cocyclique', 'est_inclus', 'et', 'faire', 'faux', 'feuille', 'ffaire', 'ffonction', 'fi', 'filled', 'fin_enregistrement', 'float', 'fonction', 'fopen', 'format', 'fpour', 'frame_3d', 'frames', 'fsi', 'ftantque', 'func', 'function', 'gauche', 'gl_ortho', 'gl_quaternion', 'gl_rotation', 'gl_shownames', 'gl_texture', 'gl_x', 'gl_x_axis_color', 'gl_x_axis_name', 'gl_x_axis_unit', 'gl_xtick', 'gl_y', 'gl_y_axis_color', 'gl_y_axis_name', 'gl_y_axis_unit', 'gl_ytick', 'gl_z', 'gl_z_axis_color', 'gl_z_axis_name', 'gl_z_axis_unit', 'gl_ztick', 'gnuplot', 'goto', 'graph2tex', 'graph3d2tex', 'graphe', 'graphe3d', 'graphe_probabiliste', 'graphe_suite', 'green', 'grid_paper', 'hidden_name', 'identifier', 'ifft', 'ifte', 'inputform', 'intersect', 'is_included', 'jusqu_a', 'jusqua', 'jusque', 'keep_algext', 'kill', 'label', 'labels', 'len', 'leve_crayon', 'line_width_1', 'line_width_2', 'line_width_3', 'line_width_4', 'line_width_5', 'line_width_6', 'line_width_7', 'lis', 'local', 'minus', 'mod', 'noir', 'nom_cache', 'non', 'od', 'option', 'otherwise', 'ou', 'pas', 'point_arret', 'point_carre', 'point_croix', 'point_div', 'point_etoile', 'point_invisible', 'point_losange', 'point_milieu', 'point_plus', 'point_point', 'point_polaire', 'point_triangle', 'point_width_1', 'point_width_2', 'point_width_3', 'point_width_4', 'point_width_5', 'point_width_6', 'point_width_7', 'pour', 'proc', 'program', 'quadrant1', 'quadrant2', 'quadrant3', 'quadrant4', 'range', 'redim', 'repeat', 'repete', 'repeter', 'replace', 'restart', 'rouge', 'saisir', 'saisir_chaine', 'sauve', 'save_history', 'scale', 'scaleadd', 'si', 'sinon', 'size', 'stack', 'step', 'switch', 'tantque', 'test', 'textinput', 'then', 'thiele', 'time', 'to', 'union', 'until', 'var', 'vector', 'vers', 'vert', 'vrai', 'watch', 'when', 'white', 'with_sqrt', 'write', 'wz_certificate', 'xor', 'yellow', '{}', '|', '||','expression'] - - -# basekeywords=['sin', 'cos', 'exp', 'tan', 'solve'] - - -# usualvars=['x','y','z','t','u','v'] - -moremethods = ['type','zip'] -# append seems fixed with current giac. - - -# the file aide_cas.txt should contain one line by keywords+ its synonyms. You can create it from th file aide_cas -# provided in giac source archive or installled in share/giac/doc like this: -#grep -E '^#' share/giac/doc/aide_cas |sed -e 's/^# //' >aide_cas.txt -with open('aide_cas.txt') as f: - mostkeywords = f.read().split() - -mostkeywordorig = ['Airy_Ai', 'Airy_Bi', 'Archive', 'BesselJ', 'BesselY', 'Beta', 'BlockDiagonal', 'Ci', 'Circle', 'Col', 'CopyVar', 'Dirac', 'Ei', 'Factor', 'GF', 'Gamma', 'Heaviside', 'JordanBlock', 'LU', 'LambertW', 'Li', 'Line', 'LineHorz', 'LineTan', 'LineVert', 'Phi', 'Pi', 'Psi', 'QR', 'RandSeed', 'Row', 'SortA', 'SortD', 'UTPC', 'UTPF', 'UTPN', 'UTPT', 'VARS', 'VAS', 'VAS_positive', 'Zeta', '_qe_', 'a2q', 'abcuv', 'about', 'abs', 'abscissa', 'accumulate_head_tail', 'acos', 'acos2asin', 'acos2atan', 'acosh', 'acot', 'acsc', 'acyclic', 'add', 'add_arc', 'add_edge', 'add_vertex', 'additionally', 'adjacency_matrix', 'adjoint_matrix', 'affix', 'algsubs', 'algvar', 'all_trig_solutions', 'allpairs_distance', 'alog10', 'altitude', 'angle', 'angle_radian', 'angleat', 'angleatraw', 'ans', 'antiprism_graph', 'apply', 'approx', 'arc', 'arcLen', 'arccos', 'arccosh', 'arclen', 'arcsin', 'arcsinh', 'arctan', 'arctanh', 'area', 'areaat', 'areaatraw', 'areaplot', 'arg', 'array', 'arrivals', 'articulation_points', 'asin', 'asin2acos', 'asin2atan', 'asinh', 'assign_edge_weights', 'assume', 'at', 'atan', 'atan2acos', 'atan2asin', 'atanh', 'atrig2ln', 'augment', 'auto_correlation', 'autosimplify', 'avance', 'avgRC', 'axes', 'back', 'backward', 'baisse_crayon', 'bandwidth', 'bar_plot', 'bartlett_hann_window', 'barycenter', 'base', 'basis', 'batons', 'bellman_ford', 'bernoulli', 'besselJ', 'besselY', 'betad', 'betad_cdf', 'betad_icdf', 'betavariate', 'bezier', 'bezout_entiers', 'biconnected_components', 'binomial', 'binomial_cdf', 'binomial_icdf', 'bins', 'bipartite', 'bipartite_matching', 'bisection_solver', 'bisector', 'bit_depth', 'bitand', 'bitor', 'bitxor', 'blackman_harris_window', 'blackman_window', 'blockmatrix', 'bohman_window', 'border', 'boxwhisker', 'brent_solver', 'bvpsolve', 'cFactor', 'cSolve', 'cZeros', 'camembert', 'canonical_form', 'canonical_labeling', 'cartesian_product', 'cauchy', 'cauchy_cdf', 'cauchy_icdf', 'cauchyd', 'cauchyd_cdf', 'cauchyd_icdf', 'cdf', 'ceil', 'ceiling', 'center', 'center2interval', 'centered_cube', 'centered_tetrahedron', 'cfactor', 'cfsolve', 'changebase', 'channel_data', 'channels', 'char', 'charpoly', 'chinrem', 'chisquare', 'chisquare_cdf', 'chisquare_icdf', 'chisquared', 'chisquared_cdf', 'chisquared_icdf', 'chisquaret', 'choice', 'cholesky', 'chr', 'chrem', 'chromatic_index', 'chromatic_number', 'chromatic_polynomial', 'circle', 'circumcircle', 'classes', 'clear', 'clique_cover', 'clique_cover_number', 'clique_number', 'clique_stats', 'clustering_coefficient', 'coeff', 'coeffs', 'col', 'colDim', 'colNorm', 'colSwap', 'coldim', 'collect', 'colnorm', 'color', 'colspace', 'colswap', 'comDenom', 'comb', 'combine', 'comment', 'common_perpendicular', 'companion', 'compare', 'complete_binary_tree', 'complete_graph', 'complete_kary_tree', 'complex', 'complex_variables', 'complexroot', 'concat', 'cond', 'condensation', 'cone', 'confrac', 'conic', 'conj', 'conjugate_equation', 'conjugate_gradient', 'connected', 'connected_components', 'cont', 'contains', 'content', 'contourplot', 'contract_edge', 'convert', 'convertir', 'convex', 'convexhull', 'convolution', 'coordinates', 'copy', 'correlation', 'cos', 'cos2sintan', 'cosh', 'cosine_window', 'cot', 'cote', 'count', 'count_eq', 'count_inf', 'count_sup', 'courbe_parametrique', 'courbe_polaire', 'covariance', 'covariance_correlation', 'cpartfrac', 'crationalroot', 'crayon', 'createwav', 'cross', 'crossP', 'cross_correlation', 'cross_point', 'cross_ratio', 'crossproduct', 'csc', 'csolve', 'csv2gen', 'cube', 'cumSum', 'cumsum', 'cumulated_frequencies', 'curl', 'current_sheet', 'curvature', 'curve', 'cyan', 'cycle2perm', 'cycle_graph', 'cycleinv', 'cycles2permu', 'cyclotomic', 'cylinder', 'dash_line', 'dashdot_line', 'dashdotdot_line', 'dayofweek', 'deSolve', 'debut_enregistrement', 'degree', 'degree_sequence', 'delcols', 'delete_arc', 'delete_edge', 'delete_vertex', 'delrows', 'deltalist', 'denom', 'densityplot', 'departures', 'derive', 'deriver', 'desolve', 'dessine_tortue', 'det', 'det_minor', 'developper', 'developper_transcendant', 'dfc', 'dfc2f', 'diag', 'diff', 'digraph', 'dijkstra', 'dim', 'directed', 'discard_edge_attribute', 'discard_graph_attribute', 'discard_vertex_attribute', 'disjoint_union', 'display', 'disque', 'disque_centre', 'distance', 'distance2', 'distanceat', 'distanceatraw', 'divergence', 'divide', 'divis', 'division_point', 'divisors', 'divmod', 'divpc', 'dnewton_solver', 'dodecahedron', 'domain', 'dot', 'dotP', 'dot_paper', 'dotprod', 'draw_arc', 'draw_circle', 'draw_graph', 'draw_line', 'draw_pixel', 'draw_polygon', 'draw_rectangle', 'droit', 'droite_tangente', 'dsolve', 'duration', 'e', 'e2r', 'ecart_type', 'ecart_type_population', 'ecm_factor', 'edge_connectivity', 'edges', 'egcd', 'egv', 'egvl', 'eigVc', 'eigVl', 'eigenvals', 'eigenvalues', 'eigenvectors', 'eigenvects', 'element', 'eliminate', 'ellipse', 'entry', 'envelope', 'epsilon', 'epsilon2zero', 'equal', 'equal2diff', 'equal2list', 'equation', 'equilateral_triangle', 'erf', 'erfc', 'error', 'est_permu', 'euler', 'euler_gamma', 'euler_lagrange', 'eval_level', 'evala', 'evalb', 'evalc', 'evalf', 'evalm', 'even', 'evolute', 'exact', 'exbisector', 'excircle', 'execute', 'exp', 'exp2list', 'exp2pow', 'exp2trig', 'expand', 'expexpand', 'expln', 'exponential', 'exponential_cdf', 'exponential_icdf', 'exponential_regression', 'exponential_regression_plot', 'exponentiald', 'exponentiald_cdf', 'exponentiald_icdf', 'export_graph', 'expovariate', 'expr', 'extend', 'extract_measure', 'extrema', 'ezgcd', 'f2nd', 'fMax', 'fMin', 'fPart', 'faces', 'facteurs_premiers', 'factor', 'factor_xn', 'factorial', 'factoriser', 'factoriser_entier', 'factoriser_sur_C', 'factors', 'fadeev', 'false', 'falsepos_solver', 'fclose', 'fcoeff', 'fdistrib', 'fft', 'fieldplot', 'find', 'find_cycles', 'findhelp', 'fisher', 'fisher_cdf', 'fisher_icdf', 'fisherd', 'fisherd_cdf', 'fisherd_icdf', 'fitdistr', 'flatten', 'float2rational', 'floor', 'flow_polynomial', 'fmod', 'foldl', 'foldr', 'fonction_derivee', 'forward', 'fourier_an', 'fourier_bn', 'fourier_cn', 'fprint', 'frac', 'fracmod', 'frame_2d', 'frequencies', 'frobenius_norm', 'froot', 'fsolve', 'fullparfrac', 'funcplot', 'function_diff', 'fxnd', 'gammad', 'gammad_cdf', 'gammad_icdf', 'gammavariate', 'gauss', 'gauss15', 'gauss_seidel_linsolve', 'gaussian_window', 'gaussjord', 'gaussquad', 'gbasis', 'gbasis_max_pairs', 'gbasis_reinject', 'gbasis_simult_primes', 'gcd', 'gcdex', 'genpoly', 'geometric', 'geometric_cdf', 'geometric_icdf', 'getDenom', 'getKey', 'getNum', 'getType', 'get_edge_attribute', 'get_edge_weight', 'get_graph_attribute', 'get_vertex_attribute', 'girth', 'gl_showaxes', 'grad', 'gramschmidt', 'graph', 'graph_automorphisms', 'graph_charpoly', 'graph_complement', 'graph_diameter', 'graph_equal', 'graph_join', 'graph_power', 'graph_rank', 'graph_spectrum', 'graph_union', 'graph_vertices', 'greduce', 'greedy_color', 'grid_graph', 'groupermu', 'hadamard', 'half_cone', 'half_line', 'halftan', 'halftan_hyp2exp', 'halt', 'hamdist', 'hamming_window', 'hann_poisson_window', 'hann_window', 'harmonic_conjugate', 'harmonic_division', 'has', 'has_arc', 'has_edge', 'hasard', 'head', 'heading', 'heapify', 'heappop', 'heappush', 'hermite', 'hessenberg', 'hessian', 'heugcd', 'hexagon', 'highlight_edges', 'highlight_subgraph', 'highlight_trail', 'highlight_vertex', 'highpass', 'hilbert', 'histogram', 'hold', 'homothety', 'horner', 'hybrid_solver', 'hybridj_solver', 'hybrids_solver', 'hybridsj_solver', 'hyp2exp', 'hyperbola', 'hypercube_graph', 'iPart', 'iabcuv', 'ibasis', 'ibpdv', 'ibpu', 'icdf', 'ichinrem', 'ichrem', 'icomp', 'icontent', 'icosahedron', 'id', 'identity', 'idivis', 'idn', 'iegcd', 'ifactor', 'ifactors', 'igamma', 'igcd', 'igcdex', 'ihermite', 'ilaplace', 'im', 'imag', 'image', 'implicitdiff', 'implicitplot', 'import_graph', 'inString', 'in_ideal', 'incidence_matrix', 'incident_edges', 'incircle', 'increasing_power', 'independence_number', 'indets', 'index', 'induced_subgraph', 'inequationplot', 'inf', 'infinity', 'insert', 'insmod', 'int', 'intDiv', 'integer', 'integrate', 'integrer', 'inter', 'interactive_odeplot', 'interactive_plotode', 'interp', 'interval', 'interval2center', 'interval_graph', 'inv', 'inverse', 'inversion', 'invisible_point', 'invlaplace', 'invztrans', 'iquo', 'iquorem', 'iratrecon', 'irem', 'isPrime', 'is_acyclic', 'is_arborescence', 'is_biconnected', 'is_bipartite', 'is_clique', 'is_collinear', 'is_concyclic', 'is_conjugate', 'is_connected', 'is_coplanar', 'is_cospherical', 'is_cut_set', 'is_cycle', 'is_directed', 'is_element', 'is_equilateral', 'is_eulerian', 'is_forest', 'is_graphic_sequence', 'is_hamiltonian', 'is_harmonic', 'is_harmonic_circle_bundle', 'is_harmonic_line_bundle', 'is_inside', 'is_integer_graph', 'is_isomorphic', 'is_isosceles', 'is_network', 'is_orthogonal', 'is_parallel', 'is_parallelogram', 'is_permu', 'is_perpendicular', 'is_planar', 'is_prime', 'is_pseudoprime', 'is_rectangle', 'is_regular', 'is_rhombus', 'is_square', 'is_strongly_connected', 'is_strongly_regular', 'is_tournament', 'is_tree', 'is_triconnected', 'is_two_edge_connected', 'is_vertex_colorable', 'is_weighted', 'ismith', 'isobarycenter', 'isom', 'isomorphic_copy', 'isopolygon', 'isosceles_triangle', 'isprime', 'ithprime', 'jacobi_equation', 'jacobi_linsolve', 'jacobi_symbol', 'jordan', 'kde', 'keep_pivot', 'ker', 'kernel', 'kernel_density', 'kneser_graph', 'kolmogorovd', 'kolmogorovt', 'kovacicsols', 'kspaths', 'l1norm', 'l2norm', 'lagrange', 'laguerre', 'laplace', 'laplacian', 'laplacian_matrix', 'latex', 'lcf_graph', 'lcm', 'lcoeff', 'ldegree', 'left', 'left_rectangle', 'legend', 'legendre', 'legendre_symbol', 'length', 'lgcd', 'lhs', 'ligne_chapeau_carre', 'ligne_chapeau_plat', 'ligne_chapeau_rond', 'ligne_polygonale', 'ligne_polygonale_pointee', 'ligne_tiret', 'ligne_tiret_point', 'ligne_tiret_pointpoint', 'ligne_trait_plein', 'limit', 'limite', 'lin', 'line', 'line_graph', 'line_inter', 'line_paper', 'line_segments', 'linear_interpolate', 'linear_regression', 'linear_regression_plot', 'lineariser', 'lineariser_trigo', 'linfnorm', 'linsolve', 'linspace', 'lis_phrase', 'list2exp', 'list2mat', 'list_edge_attributes', 'list_graph_attributes', 'list_vertex_attributes', 'listplot', 'lll', 'ln', 'lname', 'lncollect', 'lnexpand', 'locus', 'log', 'log10', 'logarithmic_regression', 'logarithmic_regression_plot', 'logb', 'logistic_regression', 'logistic_regression_plot', 'lower', 'lowest_common_ancestor', 'lowpass', 'lp_assume', 'lp_bestprojection', 'lp_binary', 'lp_binaryvariables', 'lp_breadthfirst', 'lp_depthfirst', 'lp_depthlimit', 'lp_firstfractional', 'lp_gaptolerance', 'lp_hybrid', 'lp_initialpoint', 'lp_integer', 'lp_integertolerance', 'lp_integervariables', 'lp_interiorpoint', 'lp_iterationlimit', 'lp_lastfractional', 'lp_maxcuts', 'lp_maximize', 'lp_method', 'lp_mostfractional', 'lp_nodelimit', 'lp_nodeselect', 'lp_nonnegative', 'lp_nonnegint', 'lp_pseudocost', 'lp_simplex', 'lp_timelimit', 'lp_variables', 'lp_varselect', 'lp_verbose', 'lpsolve', 'lsmod', 'lsq', 'lu', 'lvar', 'mRow', 'mRowAdd', 'magenta', 'make_directed', 'make_weighted', 'makelist', 'makemat', 'makesuite', 'makevector', 'map', 'maple2mupad', 'maple2xcas', 'maple_ifactors', 'maple_mode', 'markov', 'mat2list', 'mathml', 'matpow', 'matrix', 'matrix_norm', 'max', 'maxflow', 'maximal_independent_set', 'maximize', 'maximum_clique', 'maximum_degree', 'maximum_independent_set', 'maximum_matching', 'maxnorm', 'mean', 'median', 'median_line', 'member', 'mgf', 'mid', 'middle_point', 'midpoint', 'min', 'minimal_edge_coloring', 'minimal_spanning_tree', 'minimal_vertex_coloring', 'minimax', 'minimize', 'minimum_cut', 'minimum_degree', 'mkisom', 'mksa', 'modgcd', 'mods', 'monotonic', 'montre_tortue', 'moustache', 'moving_average', 'moyal', 'moyenne', 'mul', 'mult_c_conjugate', 'mult_conjugate', 'multinomial', 'multiplier_conjugue', 'multiplier_conjugue_complexe', 'multiply', 'mupad2maple', 'mupad2xcas', 'mycielski', 'nCr', 'nDeriv', 'nInt', 'nPr', 'nSolve', 'ncols', 'negbinomial', 'negbinomial_cdf', 'negbinomial_icdf', 'neighbors', 'network_transitivity', 'newList', 'newMat', 'newton', 'newton_solver', 'newtonj_solver', 'nextperm', 'nextprime', 'nlpsolve', 'nodisp', 'non_recursive_normal', 'nop', 'nops', 'norm', 'normal', 'normal_cdf', 'normal_icdf', 'normald', 'normald_cdf', 'normald_icdf', 'normalize', 'normalt', 'normalvariate', 'nprimes', 'nrows', 'nuage_points', 'nullspace', 'number_of_edges', 'number_of_spanning_trees', 'number_of_triangles', 'number_of_vertices', 'numer', 'octahedron', 'odd', 'odd_girth', 'odd_graph', 'odeplot', 'odesolve', 'op', 'open_polygon', 'ord', 'order', 'order_size', 'ordinate', 'orthocenter', 'orthogonal', 'osculating_circle', 'p1oc2', 'p1op2', 'pa2b2', 'pade', 'parabola', 'parallel', 'parallelepiped', 'parallelogram', 'parameq', 'parameter', 'paramplot', 'parfrac', 'pari', 'part', 'partfrac', 'parzen_window', 'pas_de_cote', 'path_graph', 'pcar', 'pcar_hessenberg', 'pcoef', 'pcoeff', 'pencolor', 'pendown', 'penup', 'perimeter', 'perimeterat', 'perimeteratraw', 'periodic', 'perm', 'perminv', 'permu2cycles', 'permu2mat', 'permuorder', 'permute_vertices', 'perpen_bisector', 'perpendicular', 'petersen_graph', 'peval', 'pi', 'piecewise', 'pivot', 'pixoff', 'pixon', 'planar', 'plane', 'plane_dual', 'playsnd', 'plex', 'plot', 'plot3d', 'plotarea', 'plotcdf', 'plotcontour', 'plotdensity', 'plotfield', 'plotfunc', 'plotimplicit', 'plotinequation', 'plotlist', 'plotode', 'plotparam', 'plotpolar', 'plotproba', 'plotseq', 'plotspectrum', 'plotwav', 'plus_point', 'pmin', 'point', 'point2d', 'point3d', 'poisson', 'poisson_cdf', 'poisson_icdf', 'poisson_window', 'polar', 'polar_coordinates', 'polar_point', 'polarplot', 'pole', 'poly2symb', 'polyEval', 'polygon', 'polygone_rempli', 'polygonplot', 'polygonscatterplot', 'polyhedron', 'polynom', 'polynomial_regression', 'polynomial_regression_plot', 'position', 'poslbdLMQ', 'posubLMQ', 'potential', 'pow2exp', 'power_regression', 'power_regression_plot', 'powermod', 'powerpc', 'powexpand', 'powmod', 'prepend', 'preval', 'prevperm', 'prevprime', 'primpart', 'printf', 'prism', 'prism_graph', 'product', 'projection', 'proot', 'propFrac', 'propfrac', 'psrgcd', 'ptayl', 'purge', 'pwd', 'pyramid', 'python_compat', 'q2a', 'qr', 'quadric', 'quadrilateral', 'quantile', 'quartile1', 'quartile3', 'quartiles', 'quest', 'quo', 'quorem', 'quote', 'r2e', 'radical_axis', 'radius', 'ramene', 'rand', 'randMat', 'randNorm', 'randPoly', 'randbetad', 'randbinomial', 'randchisquare', 'randexp', 'randfisher', 'randgammad', 'randgeometric', 'randint', 'randmarkov', 'randmatrix', 'randmultinomial', 'randnorm', 'random', 'random_bipartite_graph', 'random_digraph', 'random_graph', 'random_network', 'random_planar_graph', 'random_regular_graph', 'random_sequence_graph', 'random_tournament', 'random_tree', 'random_variable', 'randperm', 'randpoisson', 'randpoly', 'randseed', 'randstudent', 'randvar', 'randvector', 'randweibulld', 'rank', 'ranm', 'ranv', 'rassembler_trigo', 'rat_jordan', 'rational', 'rationalroot', 'ratnormal', 'rcl', 'rdiv', 're', 'read', 'readrgb', 'readwav', 'real', 'realroot', 'reciprocation', 'rectangle', 'rectangle_droit', 'rectangle_gauche', 'rectangle_plein', 'rectangular_coordinates', 'recule', 'red', 'reduced_conic', 'reduced_quadric', 'ref', 'reflection', 'regroup', 'relabel_vertices', 'reliability_polynomial', 'rem', 'remain', 'remove', 'reorder', 'resample', 'residue', 'resoudre', 'resoudre_dans_C', 'resoudre_systeme_lineaire', 'resultant', 'reverse', 'reverse_graph', 'reverse_rsolve', 'revert', 'revlex', 'revlist', 'rgb', 'rhombus', 'rhombus_point', 'rhs', 'riemann_window', 'right', 'right_rectangle', 'right_triangle', 'risch', 'rm_a_z', 'rm_all_vars', 'rmbreakpoint', 'rmmod', 'rmwatch', 'romberg', 'rombergm', 'rombergt', 'rond', 'root', 'rootof', 'roots', 'rotate', 'rotation', 'round', 'row', 'rowAdd', 'rowDim', 'rowNorm', 'rowSwap', 'rowdim', 'rownorm', 'rowspace', 'rowswap', 'rref', 'rsolve', 'same', 'sample', 'samplerate', 'sans_factoriser', 'saute', 'scalarProduct', 'scalar_product', 'scatterplot', 'schur', 'sec', 'secant_solver', 'segment', 'seidel_spectrum', 'seidel_switch', 'select', 'semi_augment', 'seq', 'seqplot', 'seqsolve', 'sequence_graph', 'series', 'set_edge_attribute', 'set_edge_weight', 'set_graph_attribute', 'set_pixel', 'set_vertex_attribute', 'set_vertex_positions', 'shift', 'shift_phase', 'shortest_path', 'show_pixels', 'shuffle', 'sierpinski_graph', 'sign', 'signature', 'signe', 'similarity', 'simp2', 'simplex_reduce', 'simplifier', 'simplify', 'simpson', 'simult', 'sin', 'sin2costan', 'sincos', 'single_inter', 'sinh', 'sizes', 'slope', 'slopeat', 'slopeatraw', 'smith', 'smod', 'snedecor', 'snedecor_cdf', 'snedecor_icdf', 'snedecord', 'snedecord_cdf', 'snedecord_icdf', 'solid_line', 'solve', 'somme', 'sommet', 'sort', 'sorta', 'sortd', 'sorted', 'soundsec', 'spanning_tree', 'sphere', 'spline', 'split', 'spring', 'sq', 'sqrfree', 'sqrt', 'square', 'square_point', 'srand', 'sst', 'sst_in', 'st_ordering', 'star_graph', 'star_point', 'start', 'stdDev', 'stddev', 'stddevp', 'steffenson_solver', 'stereo2mono', 'str', 'strongly_connected_components', 'student', 'student_cdf', 'student_icdf', 'studentd', 'studentt', 'sturm', 'sturmab', 'sturmseq', 'style', 'subMat', 'subdivide_edges', 'subgraph', 'subs', 'subsop', 'subst', 'substituer', 'subtype', 'sum', 'sum_riemann', 'suppress', 'surd', 'svd', 'swapcol', 'swaprow', 'switch_axes', 'sylvester', 'symb2poly', 'symbol', 'syst2mat', 'tCollect', 'tExpand', 'table', 'tablefunc', 'tableseq', 'tabvar', 'tail', 'tan', 'tan2cossin2', 'tan2sincos', 'tan2sincos2', 'tangent', 'tangente', 'tanh', 'taux_accroissement', 'taylor', 'tchebyshev1', 'tchebyshev2', 'tcoeff', 'tcollect', 'tdeg', 'tensor_product', 'tetrahedron', 'texpand', 'thickness', 'threshold', 'throw', 'title', 'titre', 'tlin', 'tonnetz', 'topologic_sort', 'topological_sort', 'torus_grid_graph', 'total_degree', 'tourne_droite', 'tourne_gauche', 'tpsolve', 'trace', 'trail', 'trail2edges', 'trames', 'tran', 'transitive_closure', 'translation', 'transpose', 'trapeze', 'trapezoid', 'traveling_salesman', 'tree', 'tree_height', 'triangle', 'triangle_paper', 'triangle_plein', 'triangle_point', 'triangle_window', 'trig2exp', 'trigcos', 'trigexpand', 'triginterp', 'trigsimplify', 'trigsin', 'trigtan', 'trn', 'true', 'trunc', 'truncate', 'truncate_graph', 'tsimplify', 'tuer', 'tukey_window', 'tutte_polynomial', 'two_edge_connected_components', 'ufactor', 'ugamma', 'unapply', 'unarchive', 'underlying_graph', 'unfactored', 'uniform', 'uniform_cdf', 'uniform_icdf', 'uniformd', 'uniformd_cdf', 'uniformd_icdf', 'unitV', 'unquote', 'upper', 'user_operator', 'usimplify', 'valuation', 'vandermonde', 'variables_are_files', 'variance', 'version', 'vertex_connectivity', 'vertex_degree', 'vertex_distance', 'vertex_in_degree', 'vertex_out_degree', 'vertices', 'vertices_abc', 'vertices_abca', 'vpotential', 'web_graph', 'weibull', 'weibull_cdf', 'weibull_icdf', 'weibulld', 'weibulld_cdf', 'weibulld_icdf', 'weibullvariate', 'weight_matrix', 'weighted', 'weights', 'welch_window', 'wheel_graph', 'widget_size', 'wilcoxonp', 'wilcoxons', 'wilcoxont', 'with_sqrt', 'writergb', 'writewav', 'xcas_mode', 'xyztrange', 'zeros', 'ztrans'] - -missing = set(mostkeywordorig).difference(mostkeywords) -print("Missing",missing) -newkeywords = (set(mostkeywords).difference(mostkeywordorig)).difference(toremove+blacklist) -print("\nNew Keywords found:", newkeywords) - -#undocumented keywords -#undocumented=['posubLMQ','poslbdLMQ','VAS_positive','VAS'] -undocumented = [] - -mostkeywords = mostkeywords+undocumented - -mostkeywords = set(mostkeywords).difference(toremove) -mostkeywords = set(mostkeywords).difference(blacklist) -mostkeywords = set(mostkeywords).difference(moremethods) -mostkeywords = list(mostkeywords) -mostkeywords.sort() - - -# building auto-methods.pxi -Mi = open("auto-methods.pxi","w") -Mi.write("# file auto generated by mkkeywords.py\n") -s = 'cdef class GiacMethods_base:\n """\n Wrapper for a giac ``gen`` containing auto-generated methods.\n' -s += '\n This class does not manage the ``gen`` inside in any way. It is just\n a dumb wrapper.' -s += '\n You almost certainly want to use one of the derived class\n :class:`Pygen` instead.\n """\n\n' -Mi.write(s) - -for i in mostkeywords + moremethods: - p = Popen(["cas_help", i], stdout=PIPE, stderr=PIPE, universal_newlines=True) - doc = p.communicate()[0] - - doc = doc.replace("\n", "\n ") # Indent doc - s = " def " + i + "(self,*args):\n" - s += " r'''From Giac's documentation:\n " + doc + "\n '''\n" - s += " return GiacMethods['" + i + "'](self,*args)\n\n" - Mi.write(s) - -Mi.close() - -# building keywords.pxi -with open("keywords.pxi", "w") as Fi: - Fi.write("# file auto generated by mkkeywords.py\n") - Fi.write("blacklist = " + str(blacklist) + "\n\n") - Fi.write("toremove = " + str(toremove) + "\n\n") - Fi.write("moremethods = " + str(moremethods) + "\n\n") - Fi.write("mostkeywords = " + str(mostkeywords) + "\n\n") - -with open("newkeyword.pxi", "w") as Fi: - Fi.write(str(list(set(mostkeywords).difference(mostkeywordorig)))) From 1e344f98529984a2aaccd7de740a97d435139568 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 9 Dec 2024 07:00:54 -0500 Subject: [PATCH 486/507] src/sage/libs/meson.build: no more giac subdirectory The src/sage/libs/giac tree has been split off into a separate package, sagemath-giac. --- src/sage/libs/meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/libs/meson.build b/src/sage/libs/meson.build index 53470399d46..75ff6335912 100644 --- a/src/sage/libs/meson.build +++ b/src/sage/libs/meson.build @@ -74,7 +74,6 @@ install_subdir('cremona', install_dir: sage_install_dir / 'libs') subdir('eclib') subdir('flint') subdir('gap') -subdir('giac') subdir('glpk') subdir('gmp') subdir('gsl') From 43b33950e0a8323643c15e3646d0a5d2df84873a Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 19 Jan 2025 11:37:30 -0500 Subject: [PATCH 487/507] src/sage/libs/giac: bring back wrapper for backwards compatibility This interface is now contained in a separate package, but until cython supports namespace packages, that interface must have a different name. We bring back sage.libs.giac as a wrapper around it to avoid breaking existing code. --- src/sage/libs/giac/__init__.py | 3 +++ src/sage/libs/giac/giac.py | 1 + src/sage/libs/meson.build | 1 + 3 files changed, 5 insertions(+) create mode 100644 src/sage/libs/giac/__init__.py create mode 100644 src/sage/libs/giac/giac.py diff --git a/src/sage/libs/giac/__init__.py b/src/sage/libs/giac/__init__.py new file mode 100644 index 00000000000..33e253ceedd --- /dev/null +++ b/src/sage/libs/giac/__init__.py @@ -0,0 +1,3 @@ +# sagemath_giac split its __init__.py into these two +from sagemath_giac.gb import * +from sagemath_giac.context import * diff --git a/src/sage/libs/giac/giac.py b/src/sage/libs/giac/giac.py new file mode 100644 index 00000000000..7dc4a0cc3b7 --- /dev/null +++ b/src/sage/libs/giac/giac.py @@ -0,0 +1 @@ +from sagemath_giac.giac import * diff --git a/src/sage/libs/meson.build b/src/sage/libs/meson.build index 75ff6335912..1c905720c9c 100644 --- a/src/sage/libs/meson.build +++ b/src/sage/libs/meson.build @@ -74,6 +74,7 @@ install_subdir('cremona', install_dir: sage_install_dir / 'libs') subdir('eclib') subdir('flint') subdir('gap') +install_subdir('giac', install_dir: sage_install_dir / 'libs') subdir('glpk') subdir('gmp') subdir('gsl') From 48ec518aa66b894b4bc4adbb88883d1b15b7fca9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 23 Feb 2025 04:45:55 +0000 Subject: [PATCH 488/507] bump python to at least 3.11 in spkg-configure.m4 this for missing on #39251 --- build/pkgs/python3/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index a9433207975..45c636842ed 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([python3], [ - m4_pushdef([MIN_VERSION], [3.9.0]) - m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.9.0]) + m4_pushdef([MIN_VERSION], [3.11.0]) + m4_pushdef([MIN_NONDEPRECATED_VERSION], [3.11.0]) m4_pushdef([LT_STABLE_VERSION], [3.14.0]) m4_pushdef([LT_VERSION], [3.14.0]) AC_ARG_WITH([python], From e7f3e16d241bbf35ddd486828c12827344a4dd2a Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sat, 22 Feb 2025 23:17:53 -0700 Subject: [PATCH 489/507] Added doctest --- src/sage/rings/polynomial/polynomial_ring_constructor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 78074c8a1d2..920f9b6c496 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -621,6 +621,12 @@ def PolynomialRing(base_ring, *args, **kwds): ....: '_test_distributivity', '_test_prod']) sage: R. = PolynomialRing(RIF,2) sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) + + We verify that multivariate polynomial rings over InfinityRing from + :issue:`34675` are fixed:: + + sage: PolynomialRing(InfinityRing, 2, 'x') + Multivariate Polynomial Ring in x0, x1 over The Infinity Ring """ from sage.rings.semirings.tropical_semiring import TropicalSemiring if base_ring not in Rings() and not isinstance(base_ring, TropicalSemiring): From 49a0528de8c59fc8e4f93c906f177f5de0b614ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 23 Feb 2025 15:33:49 +0100 Subject: [PATCH 490/507] full cython-lint cleanup in data_structure pyx files --- src/sage/data_structures/binary_matrix.pxd | 6 +-- src/sage/data_structures/binary_search.pxd | 2 +- src/sage/data_structures/bitset.pxd | 6 +-- src/sage/data_structures/bitset.pyx | 12 +++--- src/sage/data_structures/bitset_base.pxd | 8 ++-- src/sage/data_structures/bitset_base.pyx | 6 +-- src/sage/data_structures/blas_dict.pyx | 22 +++++----- .../bounded_integer_sequences.pyx | 42 ++++++++++--------- 8 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/sage/data_structures/binary_matrix.pxd b/src/sage/data_structures/binary_matrix.pxd index ce69e036ad2..3c008ec3ca9 100644 --- a/src/sage/data_structures/binary_matrix.pxd +++ b/src/sage/data_structures/binary_matrix.pxd @@ -86,7 +86,7 @@ cdef inline void binary_matrix_fill(binary_matrix_t m, bint bit) noexcept: """ cdef mp_bitcnt_t i - if bit: # set the matrix to 1 + if bit: # set the matrix to 1 for i in range(m.n_rows): bitset_set_first_n(m.rows[i], m.n_cols) else: @@ -117,7 +117,7 @@ cdef inline void binary_matrix_set(binary_matrix_t m, mp_bitcnt_t row, mp_bitcnt r""" Set an entry """ - bitset_set_to(m.rows[row],col,value) + bitset_set_to(m.rows[row], col, value) cdef inline bint binary_matrix_get(binary_matrix_t m, mp_bitcnt_t row, mp_bitcnt_t col) noexcept: r""" @@ -129,7 +129,7 @@ cdef inline binary_matrix_print(binary_matrix_t m): r""" Print the binary matrix """ - cdef mp_bitcnt_t i,j + cdef mp_bitcnt_t i, j import sys for i in range(m.n_rows): for j in range(m.n_cols): diff --git a/src/sage/data_structures/binary_search.pxd b/src/sage/data_structures/binary_search.pxd index 5eee088e8b8..afd10246a97 100644 --- a/src/sage/data_structures/binary_search.pxd +++ b/src/sage/data_structures/binary_search.pxd @@ -1,2 +1,2 @@ cdef Py_ssize_t binary_search(Py_ssize_t* v, Py_ssize_t n, Py_ssize_t x, Py_ssize_t* ins) noexcept -cdef Py_ssize_t binary_search0(Py_ssize_t* v, Py_ssize_t n, Py_ssize_t x) noexcept \ No newline at end of file +cdef Py_ssize_t binary_search0(Py_ssize_t* v, Py_ssize_t n, Py_ssize_t x) noexcept diff --git a/src/sage/data_structures/bitset.pxd b/src/sage/data_structures/bitset.pxd index a93e26c2ef9..b5ce99c4087 100644 --- a/src/sage/data_structures/bitset.pxd +++ b/src/sage/data_structures/bitset.pxd @@ -1,11 +1,11 @@ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008 Robert Bradshaw # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.data_structures.bitset_base cimport bitset_t diff --git a/src/sage/data_structures/bitset.pyx b/src/sage/data_structures/bitset.pyx index 973102fc2a6..884132ce6b1 100644 --- a/src/sage/data_structures/bitset.pyx +++ b/src/sage/data_structures/bitset.pyx @@ -16,7 +16,7 @@ linear in ``capacity``. faster. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Jason Grout # # Distributed under the terms of the GNU General Public License (GPL) @@ -28,8 +28,8 @@ linear in ``capacity``. # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.data_structures.bitset_base cimport * from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -763,7 +763,6 @@ cdef class FrozenBitset: ... ValueError: other cannot be None """ - cdef bint retval if other is None: raise ValueError("other cannot be None") cdef FrozenBitset left, right @@ -778,7 +777,7 @@ cdef class FrozenBitset: # Assumes ``left.size <= right.size``. return bitset_are_disjoint(left._bitset, right._bitset) - def __contains__(self, unsigned long n): + def __contains__(self, unsigned long n) -> bool: """ Test to see if ``n`` is in ``self``. @@ -821,7 +820,7 @@ cdef class FrozenBitset: """ return bitset_len(self._bitset) - def __str__(self): + def __str__(self) -> str: """ Return a string representing the bitset as a binary vector. @@ -2271,7 +2270,6 @@ def test_bitset_set_first_n(py_a, long n): sage: test_bitset_set_first_n('00'*64, 128) a.set_first_n(n) 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 """ - cdef bint bit = True cdef bitset_t a bitset_from_str(a, py_a) diff --git a/src/sage/data_structures/bitset_base.pxd b/src/sage/data_structures/bitset_base.pxd index b2fb299dd9c..0431dd9cd11 100644 --- a/src/sage/data_structures/bitset_base.pxd +++ b/src/sage/data_structures/bitset_base.pxd @@ -18,14 +18,14 @@ AUTHORS: the size of the given bitsets (:issue:`15820`) """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008 Robert Bradshaw # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** # This file declares the bitset types and the (inline functions). # Few functions that are not inline are in the `pyx` file. @@ -324,7 +324,7 @@ cdef inline bint mpn_equal_bits_shifted(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t cdef mp_limb_t b1h = b1[nlimbs] tmp_limb = (b2[i2] >> bit_offset) - if (n%GMP_LIMB_BITS)+bit_offset > GMP_LIMB_BITS: + if (n % GMP_LIMB_BITS) + bit_offset > GMP_LIMB_BITS: # Need bits from the next limb of b2 tmp_limb |= (b2[preinc(i2)] << neg_bit_offset) return (b1h ^ tmp_limb) & mask == 0 diff --git a/src/sage/data_structures/bitset_base.pyx b/src/sage/data_structures/bitset_base.pyx index 182812a891f..87679c6e95c 100644 --- a/src/sage/data_structures/bitset_base.pyx +++ b/src/sage/data_structures/bitset_base.pyx @@ -2,14 +2,14 @@ Few functions from ``bitset_base.pxd`` that are not inlined. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008 Robert Bradshaw # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** cdef char* bitset_chars(char* s, fused_bitset_t bits, char zero=c'0', char one=c'1') noexcept: """ diff --git a/src/sage/data_structures/blas_dict.pyx b/src/sage/data_structures/blas_dict.pyx index baa27e54c22..875a03f64b8 100644 --- a/src/sage/data_structures/blas_dict.pyx +++ b/src/sage/data_structures/blas_dict.pyx @@ -23,7 +23,7 @@ nonzero (as tested with `bool(v)`). This is mostly used by :class:`CombinatorialFreeModule`. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2010 Christian Stump # 2016 Travis Scrimshaw # 2016 Nicolas M. Thiéry @@ -32,8 +32,8 @@ This is mostly used by :class:`CombinatorialFreeModule`. # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** cpdef int iaxpy(a, dict X, dict Y, bint remove_zeros=True, bint factor_on_left=True) except -1: r""" @@ -112,13 +112,13 @@ cpdef int iaxpy(a, dict X, dict Y, bint remove_zeros=True, bint factor_on_left=T flag = -1 elif not a: return 0 - for (key, value) in X.iteritems(): + for key, value in X.items(): if flag == -1: if key in Y: - Y[key] -= value + Y[key] -= value else: - Y[key] = -value - continue # no need to check for zero + Y[key] = -value + continue # no need to check for zero else: if flag != 1: # If we had the guarantee that `a` is in the base @@ -136,8 +136,8 @@ cpdef int iaxpy(a, dict X, dict Y, bint remove_zeros=True, bint factor_on_left=T if key in Y: Y[key] += value else: - Y[key] = value - continue # no need to check for zero + Y[key] = value + continue # no need to check for zero if remove_zeros and not Y[key]: del Y[key] return 0 @@ -217,7 +217,7 @@ cpdef dict negate(dict D): sage: blas.negate(D1) {0: -1, 1: -1} """ - return { key: -value for key, value in D.iteritems() } + return {key: -value for key, value in D.items()} cpdef dict scal(a, dict D, bint factor_on_left=True): r""" @@ -345,7 +345,7 @@ cpdef dict linear_combination(dict_factor_iter, bint factor_on_left=True): cdef dict D for D, a in dict_factor_iter: - if not a: # We multiply by 0, so nothing to do + if not a: # We multiply by 0, so nothing to do continue if not result and a == 1: result = D.copy() diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index a96a55e5b50..ba0e8e91e04 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -195,7 +195,7 @@ cdef bint biseq_init_list(biseq_t R, list data, size_t bound) except -1: sig_check() item_c = item if item_c > bound: - raise OverflowError("list item {!r} larger than {}".format(item, bound) ) + raise OverflowError("list item {!r} larger than {}".format(item, bound)) biseq_inititem(R, index, item_c) index += 1 @@ -389,7 +389,8 @@ cdef mp_size_t biseq_contains(biseq_t S1, biseq_t S2, mp_size_t start) except -2 sig_on() for index from start <= index <= S1.length-S2.length: if mpn_equal_bits_shifted(S2.data.bits, S1.data.bits, - S2.length*S2.itembitsize, index*S2.itembitsize): + S2.length * S2.itembitsize, + index * S2.itembitsize): sig_off() return index sig_off() @@ -424,7 +425,8 @@ cdef mp_size_t biseq_startswith_tail(biseq_t S1, biseq_t S2, mp_size_t start) ex sig_on() for index from start <= index < S2.length: if mpn_equal_bits_shifted(S1.data.bits, S2.data.bits, - (S2.length - index)*S2.itembitsize, index*S2.itembitsize): + (S2.length - index) * S2.itembitsize, + index * S2.itembitsize): sig_off() return index sig_off() @@ -1381,35 +1383,35 @@ def _biseq_stresstest(): cdef list L = [BoundedIntegerSequence(6, [randint(0, 5) for z in range(randint(4, 10))]) for y in range(100)] cdef BoundedIntegerSequence S, T while True: - branch = randint(0,4) + branch = randint(0, 4) if branch == 0: - L[randint(0,99)] = L[randint(0,99)]+L[randint(0,99)] + L[randint(0, 99)] = L[randint(0, 99)] + L[randint(0, 99)] elif branch == 1: - x = randint(0,99) + x = randint(0, 99) if len(L[x]): - y = randint(0,len(L[x])-1) - z = randint(y,len(L[x])-1) - L[randint(0,99)] = L[x][y:z] + y = randint(0, len(L[x]) - 1) + z = randint(y, len(L[x]) - 1) + L[randint(0, 99)] = L[x][y:z] else: - L[x] = BoundedIntegerSequence(6, [randint(0,5) for z in range(randint(4,10))]) + L[x] = BoundedIntegerSequence(6, [randint(0, 5) for z in range(randint(4, 10))]) elif branch == 2: - t = list(L[randint(0,99)]) - t = repr(L[randint(0,99)]) - t = L[randint(0,99)].list() + t = list(L[randint(0, 99)]) + t = repr(L[randint(0, 99)]) + t = L[randint(0, 99)].list() elif branch == 3: - x = randint(0,99) + x = randint(0, 99) if len(L[x]): - y = randint(0,len(L[x])-1) + y = randint(0, len(L[x])-1) t = L[x][y] try: t = L[x].index(t) except ValueError: - raise ValueError("{} should be in {} (bound {}) at position {}".format(t,L[x],L[x].bound(),y)) + raise ValueError("{} should be in {} (bound {}) at position {}".format(t, L[x], L[x].bound(), y)) else: - L[x] = BoundedIntegerSequence(6, [randint(0,5) for z in range(randint(4,10))]) + L[x] = BoundedIntegerSequence(6, [randint(0, 5) for z in range(randint(4, 10))]) elif branch == 4: - S = L[randint(0,99)] - T = L[randint(0,99)] - biseq_startswith(S.data,T.data) + S = L[randint(0, 99)] + T = L[randint(0, 99)] + biseq_startswith(S.data, T.data) biseq_contains(S.data, T.data, 0) biseq_startswith_tail(S.data, T.data, 0) From cf79a0585020aebfb90725dbdccddcb38fce7431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 24 Feb 2025 13:17:01 +1300 Subject: [PATCH 491/507] remove py3.9 and older sphinx work arounds introduced in the last two synchronization of sage_autodoc.py with upstream --- src/sage_docbuild/ext/sage_autodoc.py | 63 ++++++--------------------- 1 file changed, 14 insertions(+), 49 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 8b80350585c..d62ff8b87d1 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -37,13 +37,14 @@ - François Bissey (2024-09-10): Tweaks to support python 3.9 (and older sphinx) as well - François Bissey (2024-11-12): rebased on Sphinx 8.1.3 (while trying to keep python 3.9 compatibility) + +- François Bissey (2025-02-24): Remove python 3.9 support hacks, making us closer to upstream """ from __future__ import annotations import functools import operator -import sys import re from inspect import Parameter, Signature from typing import TYPE_CHECKING, Any, NewType, TypeVar @@ -709,7 +710,7 @@ def add_content(self, more_content: StringList | None) -> None: # add additional content (e.g. from document), if present if more_content: - for line, src in zip(more_content.data, more_content.items): + for line, src in zip(more_content.data, more_content.items, strict=True): self.add_line(line, src[0], src[1]) def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]: @@ -1080,7 +1081,7 @@ def add_content(self, more_content: StringList | None) -> None: super().add_content(None) self.indent = old_indent if more_content: - for line, src in zip(more_content.data, more_content.items): + for line, src in zip(more_content.data, more_content.items, strict=True): self.add_line(line, src[0], src[1]) @classmethod @@ -1616,14 +1617,8 @@ def __init__(self, *args: Any) -> None: def can_document_member( cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: - # support both sphinx 8 and py3.9/older sphinx - try: - result_bool = isinstance(member, type) or ( - isattr and isinstance(member, NewType | TypeVar)) - except Exception: - result_bool = isinstance(member, type) or ( - isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) - return result_bool + return isinstance(member, type) or ( + isattr and isinstance(member, NewType | TypeVar)) def import_object(self, raiseerror: bool = False) -> bool: ret = super().import_object(raiseerror) @@ -1696,12 +1691,7 @@ def import_object(self, raiseerror: bool = False) -> bool: # ------------------------------------------------------------------- else: self.doc_as_attr = True - # support both sphinx 8 and py3.9/older sphinx - try: - test_bool = isinstance(self.object, NewType | TypeVar) - except Exception: - test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) - if test_bool: + if isinstance(self.object, NewType | TypeVar): modname = getattr(self.object, '__module__', self.modname) if modname != self.modname and self.modname.startswith(modname): bases = self.modname[len(modname):].strip('.').split('.') @@ -1710,12 +1700,7 @@ def import_object(self, raiseerror: bool = False) -> bool: return ret def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]: - # support both sphinx 8 and py3.9/older sphinx - try: - test_bool = isinstance(self.object, NewType | TypeVar) - except Exception: - test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) - if test_bool: + if isinstance(self.object, NewType | TypeVar): # Suppress signature return None, None, None @@ -1900,24 +1885,14 @@ def add_directive_header(self, sig: str) -> None: self.directivetype = 'attribute' super().add_directive_header(sig) - # support both sphinx 8 and py3.9/older sphinx - try: - test_bool = isinstance(self.object, NewType | TypeVar) - except Exception: - test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) - if test_bool: + if isinstance(self.object, NewType | TypeVar): return if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(' :final:', sourcename) canonical_fullname = self.get_canonical_fullname() - # support both sphinx 8 and py3.9/older sphinx - try: - newtype_test = isinstance(self.object, NewType) - except Exception: - newtype_test = inspect.isNewType(self.object) - if (not self.doc_as_attr and not newtype_test + if (not self.doc_as_attr and not isinstance(self.object, NewType) and canonical_fullname and self.fullname != canonical_fullname): self.add_line(' :canonical: %s' % canonical_fullname, sourcename) @@ -2032,12 +2007,7 @@ def get_variable_comment(self) -> list[str] | None: return None def add_content(self, more_content: StringList | None) -> None: - # support both sphinx 8 and py3.9/older sphinx - try: - newtype_test = isinstance(self.object, NewType) - except Exception: - newtype_test = inspect.isNewType(self.object) - if newtype_test: + if isinstance(self.object, NewType): if self.config.autodoc_typehints_format == "short": supertype = restify(self.object.__supertype__, "smart") else: @@ -3006,14 +2976,9 @@ def _get_property_getter(self) -> Callable | None: def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any: """Alternative getattr() for types""" - try: - for typ, func in app.registry.autodoc_attrgetters.items(): - if isinstance(obj, typ): - return func(obj, name, *defargs) - except AttributeError: - for typ, func in app.registry.autodoc_attrgettrs.items(): - if isinstance(obj, typ): - return func(obj, name, *defargs) + for typ, func in app.registry.autodoc_attrgetters.items(): + if isinstance(obj, typ): + return func(obj, name, *defargs) return safe_getattr(obj, name, *defargs) From dabc39060a163ada16a15b5d287b9a7d4e8e4a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 24 Feb 2025 13:36:40 +1300 Subject: [PATCH 492/507] update sphinx to 8.1.3 --- build/pkgs/sphinx/checksums.ini | 4 ++-- build/pkgs/sphinx/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 1721e0e8521..d6d0de6b17a 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,4 +1,4 @@ tarball=sphinx-VERSION-py3-none-any.whl -sha1=f9af5608fbb188f12e38d3c09cdefeef40255365 -sha256=c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239 +sha1=67dc18611c44f712539585db41aaee4b0a7ec646 +sha256=09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2 upstream_url=https://files.pythonhosted.org/packages/py3/s/sphinx/sphinx-VERSION-py3-none-any.whl diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 25627bcf165..406b8982770 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -7.4.7 +8.1.3 From c023a2d0b1b56afa65a4c62cb498b820d1261283 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 19 Jan 2025 20:56:30 -0500 Subject: [PATCH 493/507] build/pkgs/sagemath_giac: new optional package This optional sage/giac integration package replaces the old hard dependency on giac that was required to build sage.libs.giac. --- build/pkgs/sagemath_giac/SPKG.rst | 10 ++++++++++ build/pkgs/sagemath_giac/checksums.ini | 4 ++++ build/pkgs/sagemath_giac/dependencies | 1 + build/pkgs/sagemath_giac/package-version.txt | 1 + build/pkgs/sagemath_giac/spkg-configure.m4 | 3 +++ build/pkgs/sagemath_giac/spkg-install.in | 2 ++ build/pkgs/sagemath_giac/type | 1 + 7 files changed, 22 insertions(+) create mode 100644 build/pkgs/sagemath_giac/SPKG.rst create mode 100644 build/pkgs/sagemath_giac/checksums.ini create mode 100644 build/pkgs/sagemath_giac/dependencies create mode 100644 build/pkgs/sagemath_giac/package-version.txt create mode 100644 build/pkgs/sagemath_giac/spkg-configure.m4 create mode 100644 build/pkgs/sagemath_giac/spkg-install.in create mode 100644 build/pkgs/sagemath_giac/type diff --git a/build/pkgs/sagemath_giac/SPKG.rst b/build/pkgs/sagemath_giac/SPKG.rst new file mode 100644 index 00000000000..e68c8701c56 --- /dev/null +++ b/build/pkgs/sagemath_giac/SPKG.rst @@ -0,0 +1,10 @@ +============================================================================== + Sage: Open Source Mathematics Software: Giac integration +============================================================================== + +This pip-installable source distribution ``sagemath-giac`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``libgiac`` library for the +purpose of symbolic integration and certain Groebner basis +calculations. diff --git a/build/pkgs/sagemath_giac/checksums.ini b/build/pkgs/sagemath_giac/checksums.ini new file mode 100644 index 00000000000..fe47b2972bf --- /dev/null +++ b/build/pkgs/sagemath_giac/checksums.ini @@ -0,0 +1,4 @@ +tarball=sagemath-giac-VERSION.tar.gz +sha1=6a134b2f98d5f55cec51415141354eacf675f211 +sha256=4a565f0f279d9bce60332ec292fe487dd6d8f85f83066e6a582928cb611dd7f4 +upstream_url=https://github.com/sagemath/sagemath-giac/archive/refs/tags/VERSION.tar.gz diff --git a/build/pkgs/sagemath_giac/dependencies b/build/pkgs/sagemath_giac/dependencies new file mode 100644 index 00000000000..9e356574ea9 --- /dev/null +++ b/build/pkgs/sagemath_giac/dependencies @@ -0,0 +1 @@ +cysignals cython giac gmpy2 sagelib | $(PYTHON_TOOLCHAIN) $(PYTHON) diff --git a/build/pkgs/sagemath_giac/package-version.txt b/build/pkgs/sagemath_giac/package-version.txt new file mode 100644 index 00000000000..17e51c385ea --- /dev/null +++ b/build/pkgs/sagemath_giac/package-version.txt @@ -0,0 +1 @@ +0.1.1 diff --git a/build/pkgs/sagemath_giac/spkg-configure.m4 b/build/pkgs/sagemath_giac/spkg-configure.m4 new file mode 100644 index 00000000000..eb6492939dc --- /dev/null +++ b/build/pkgs/sagemath_giac/spkg-configure.m4 @@ -0,0 +1,3 @@ +SAGE_SPKG_CONFIGURE([sagemath_giac], [ + SAGE_PYTHON_PACKAGE_CHECK([sagemath_giac]) +]) diff --git a/build/pkgs/sagemath_giac/spkg-install.in b/build/pkgs/sagemath_giac/spkg-install.in new file mode 100644 index 00000000000..bb8c1204b88 --- /dev/null +++ b/build/pkgs/sagemath_giac/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install --no-build-isolation . diff --git a/build/pkgs/sagemath_giac/type b/build/pkgs/sagemath_giac/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_giac/type @@ -0,0 +1 @@ +optional From dc8813ee541a36bea051e2757dd2f9488b8afd69 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 25 Jan 2025 06:56:14 -0500 Subject: [PATCH 494/507] build/pkgs/giac: demote to optional This is now strictly required only by the (optional) sagemath_giac package. There is a feature test guarding the doctests in the old pexpect interface under sage.interfaces.giac, and nothing else in sagelib imports that pexpect interface, so if you do not use it explicitly, giac is optional. --- build/pkgs/giac/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/giac/type b/build/pkgs/giac/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/giac/type +++ b/build/pkgs/giac/type @@ -1 +1 @@ -standard +optional From 65ff51e6864f2a419c2510a7fbdc5edba104ac32 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 25 Jan 2025 18:09:16 -0500 Subject: [PATCH 495/507] src/sage/features/giac.py: standard -> optional --- src/sage/features/giac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/features/giac.py b/src/sage/features/giac.py index 67403a332be..34d7d5e0613 100644 --- a/src/sage/features/giac.py +++ b/src/sage/features/giac.py @@ -25,7 +25,7 @@ def __init__(self): True """ Executable.__init__(self, 'giac', executable='giac', - spkg='giac', type='standard') + spkg='giac', type='optional') def all_features(): From d7a5f54d2bae9e9899d6572ae1287305b849e920 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 25 Jan 2025 19:34:30 -0500 Subject: [PATCH 496/507] src/sage/features/sagemath.py: mark sage.libs.giac as optional --- src/sage/features/sagemath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index f536665d8cc..cf986835e6c 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -560,7 +560,7 @@ def __init__(self): """ JoinFeature.__init__(self, 'sage.libs.giac', [PythonModule('sage.libs.giac.giac')], - spkg='sagemath_giac', type='standard') + spkg='sagemath_giac', type='optional') class sage__libs__homfly(JoinFeature): From 2732887aeafac5776ebc5231aab2c86738ea2892 Mon Sep 17 00:00:00 2001 From: Caleb Van't Land Date: Sun, 23 Feb 2025 22:45:54 -0700 Subject: [PATCH 497/507] Added backticks around InfinityRing --- src/sage/rings/polynomial/polynomial_ring_constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 920f9b6c496..aab9d9393b6 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -622,7 +622,7 @@ def PolynomialRing(base_ring, *args, **kwds): sage: R. = PolynomialRing(RIF,2) sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive']) - We verify that multivariate polynomial rings over InfinityRing from + We verify that multivariate polynomial rings over ``InfinityRing`` from :issue:`34675` are fixed:: sage: PolynomialRing(InfinityRing, 2, 'x') From 962d64acdcd7f0969c6e177c7388243877ca6123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Feb 2025 10:24:20 +0100 Subject: [PATCH 498/507] adding some type annotations in lattices --- src/sage/combinat/posets/lattices.py | 72 ++++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 542213f0f06..61d36381719 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -916,7 +916,7 @@ def neutral_elements(self): t = sorted(self._hasse_diagram.neutral_elements()) return [self._vertex_to_element(v) for v in t] - def is_join_distributive(self, certificate=False): + def is_join_distributive(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is join-distributive and ``False`` otherwise. @@ -1005,7 +1005,7 @@ def is_join_distributive(self, certificate=False): diamond = next(self._hasse_diagram.subgraph_search_iterator(M3, return_graphs=False)) return (False, self[diamond[0]]) - def is_meet_distributive(self, certificate=False): + def is_meet_distributive(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is meet-distributive and ``False`` otherwise. @@ -1093,7 +1093,7 @@ def is_meet_distributive(self, certificate=False): diamond = next(self._hasse_diagram.subgraph_search_iterator(M3, return_graphs=False)) return (False, self[diamond[4]]) - def is_stone(self, certificate=False): + def is_stone(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is a Stone lattice, and ``False`` otherwise. @@ -1188,7 +1188,7 @@ def is_stone(self, certificate=False): return ok - def is_distributive(self, certificate=False): + def is_distributive(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is distributive, and ``False`` otherwise. @@ -1228,7 +1228,7 @@ def is_distributive(self, certificate=False): :meth:`is_semidistributive`, :meth:`is_join_distributive`, :meth:`is_meet_distributive`, :meth:`is_subdirectly_reducible`, :meth:`is_trim`, - :meth:`is_constructible_by_doublings` (by interval doubling), + :meth:`is_congruence_uniform`, :meth:`is_extremal` - Stronger properties: :meth:`is_stone` @@ -1262,7 +1262,7 @@ def is_distributive(self, certificate=False): self._vertex_to_element(diamond[2]), self._vertex_to_element(diamond[3]))) - def is_semidistributive(self): + def is_semidistributive(self) -> bool: """ Return ``True`` if the lattice is both join- and meet-semidistributive, and ``False`` otherwise. @@ -1308,7 +1308,7 @@ def is_semidistributive(self): H.out_degree_sequence().count(1)) and self.is_meet_semidistributive()) - def is_meet_semidistributive(self, certificate=False): + def is_meet_semidistributive(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is meet-semidistributive, and ``False`` otherwise. @@ -1400,7 +1400,7 @@ def is_meet_semidistributive(self, certificate=False): return (True, None) return True - def is_join_semidistributive(self, certificate=False): + def is_join_semidistributive(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is join-semidistributive, and ``False`` otherwise. @@ -1496,7 +1496,7 @@ def is_join_semidistributive(self, certificate=False): return all(H.kappa_dual(v) is not None for v in H if H.out_degree(v) == 1) - def is_extremal(self): + def is_extremal(self) -> bool: """ Return ``True`` if the lattice is extremal, and ``False`` otherwise. @@ -1526,7 +1526,7 @@ def is_extremal(self): mi = len(self.meet_irreducibles()) return ji == mi == self.height() - 1 - def is_trim(self, certificate=False): + def is_trim(self, certificate=False) -> bool | tuple: """ Return whether a lattice is trim. @@ -1575,7 +1575,7 @@ def is_trim(self, certificate=False): else: return (False, None) if certificate else False - def is_complemented(self, certificate=False): + def is_complemented(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is complemented, and ``False`` otherwise. @@ -1626,7 +1626,7 @@ def is_complemented(self, certificate=False): return (True, None) return (False, self._vertex_to_element(e)) - def is_cosectionally_complemented(self, certificate=False): + def is_cosectionally_complemented(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is cosectionally complemented, and ``False`` otherwise. @@ -1702,7 +1702,7 @@ def is_cosectionally_complemented(self, certificate=False): return False return (True, None) if certificate else True - def is_relatively_complemented(self, certificate=False): + def is_relatively_complemented(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is relatively complemented, and ``False`` otherwise. @@ -1815,7 +1815,7 @@ def is_relatively_complemented(self, certificate=False): self._vertex_to_element(e3))) return (True, None) if certificate else True - def is_sectionally_complemented(self, certificate=False): + def is_sectionally_complemented(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is sectionally complemented, and ``False`` otherwise. @@ -2086,7 +2086,7 @@ def complements(self, element=None): if self.meet(x, element) == self.bottom() and self.join(x, element) == self.top()] - def is_pseudocomplemented(self, certificate=False): + def is_pseudocomplemented(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is pseudocomplemented, and ``False`` otherwise. @@ -2153,7 +2153,7 @@ def is_pseudocomplemented(self, certificate=False): return (True, None) return True - def is_join_pseudocomplemented(self, certificate=False): + def is_join_pseudocomplemented(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is join-pseudocomplemented, and ``False`` otherwise. @@ -2323,7 +2323,7 @@ def is_orthocomplemented(self, unique=False): return True raise AssertionError("bug in is_orthocomplemented()") - def is_atomic(self, certificate=False): + def is_atomic(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is atomic, and ``False`` otherwise. @@ -2381,7 +2381,7 @@ def is_atomic(self, certificate=False): return (False, self._vertex_to_element(v)) return (True, None) - def is_coatomic(self, certificate=False): + def is_coatomic(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is coatomic, and ``False`` otherwise. @@ -2439,7 +2439,7 @@ def is_coatomic(self, certificate=False): return (False, self._vertex_to_element(v)) return (True, None) - def is_geometric(self): + def is_geometric(self) -> bool: """ Return ``True`` if the lattice is geometric, and ``False`` otherwise. @@ -2484,7 +2484,7 @@ def is_geometric(self): """ return self.is_atomic() and self.is_upper_semimodular() - def is_planar(self): + def is_planar(self) -> bool: r""" Return ``True`` if the lattice is *upward* planar, and ``False`` otherwise. @@ -2549,7 +2549,7 @@ def is_planar(self): g.add_edge(0, self.cardinality() - 1) return g.is_planar() - def is_modular(self, L=None, certificate=False): + def is_modular(self, L=None, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is modular and ``False`` otherwise. @@ -2654,7 +2654,7 @@ def is_modular(self, L=None, certificate=False): return (True, None) return True - def is_modular_element(self, x): + def is_modular_element(self, x) -> bool: r""" Return ``True`` if ``x`` is a modular element and ``False`` otherwise. @@ -2686,7 +2686,7 @@ def is_modular_element(self, x): """ return self.is_modular([x]) - def is_left_modular_element(self, x): + def is_left_modular_element(self, x) -> bool: r""" Return ``True`` if ``x`` is a left modular element and ``False`` otherwise. @@ -2719,7 +2719,7 @@ def is_left_modular_element(self, x): self.join(y, self.meet(x, z)) for y, z in self.cover_relations_iterator()) - def is_upper_semimodular(self, certificate=False): + def is_upper_semimodular(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is upper semimodular and ``False`` otherwise. @@ -2779,7 +2779,7 @@ def is_upper_semimodular(self, certificate=False): self._vertex_to_element(nonmodular[1]))) return False - def is_lower_semimodular(self, certificate=False): + def is_lower_semimodular(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is lower semimodular and ``False`` otherwise. @@ -2834,7 +2834,7 @@ def is_lower_semimodular(self, certificate=False): self._vertex_to_element(nonmodular[1]))) return False - def is_supersolvable(self, certificate=False): + def is_supersolvable(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is supersolvable, and ``False`` otherwise. @@ -3091,7 +3091,7 @@ def vertical_decomposition(self, elements_only=False): elms[i + 1] + 1)])) for i in range(n - 1)] - def is_vertically_decomposable(self, certificate=False): + def is_vertically_decomposable(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is vertically decomposable, and ``False`` otherwise. @@ -3185,7 +3185,7 @@ def sublattice(self, elms): return LatticePoset(self.subposet(current_set)) - def is_sublattice(self, other): + def is_sublattice(self, other) -> bool: """ Return ``True`` if the lattice is a sublattice of ``other``, and ``False`` otherwise. @@ -3683,7 +3683,7 @@ def center(self): comps = self.complements() return self.sublattice([e for e in neutrals if e in comps]) - def is_dismantlable(self, certificate=False): + def is_dismantlable(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is dismantlable, and ``False`` otherwise. @@ -3798,7 +3798,7 @@ def is_dismantlable(self, certificate=False): return True return (True, [self[e] for e in cert]) - def is_interval_dismantlable(self, certificate=False): + def is_interval_dismantlable(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is interval dismantlable, and ``False`` otherwise. @@ -3911,7 +3911,7 @@ def recursive_is_interval_dismantlable(self): return result if certificate else True return (False, minimal_non_int_dismant(self)) if certificate else False - def is_sublattice_dismantlable(self): + def is_sublattice_dismantlable(self) -> bool: """ Return ``True`` if the lattice is sublattice dismantlable, and ``False`` otherwise. @@ -3984,7 +3984,7 @@ def is_sublattice_dismantlable(self): return False - def is_subdirectly_reducible(self, certificate=False): + def is_subdirectly_reducible(self, certificate=False) -> bool | tuple: r""" Return ``True`` if the lattice is subdirectly reducible. @@ -4367,7 +4367,7 @@ def is_congruence_uniform(self) -> bool: """ return self.is_constructible_by_doublings(type="interval") - def is_isoform(self, certificate=False): + def is_isoform(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is isoform and ``False`` otherwise. @@ -4442,7 +4442,7 @@ def is_isoform(self, certificate=False): return False return ok - def is_uniform(self, certificate=False): + def is_uniform(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is uniform and ``False`` otherwise. @@ -4522,7 +4522,7 @@ def is_uniform(self, certificate=False): return False return ok - def is_regular(self, certificate=False): + def is_regular(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is regular and ``False`` otherwise. @@ -4590,7 +4590,7 @@ def is_regular(self, certificate=False): return False return ok - def is_simple(self, certificate=False): + def is_simple(self, certificate=False) -> bool | tuple: """ Return ``True`` if the lattice is simple and ``False`` otherwise. From 2f9c52dc4079c08ae2978478848ad7f196771913 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 24 Feb 2025 18:56:02 +0100 Subject: [PATCH 499/507] #39216: suggested corrections --- src/sage/graphs/isoperimetric_inequalities.pyx | 2 +- src/sage/graphs/weakly_chordal.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/isoperimetric_inequalities.pyx b/src/sage/graphs/isoperimetric_inequalities.pyx index 2fa715a102a..e40fbe3e2a1 100644 --- a/src/sage/graphs/isoperimetric_inequalities.pyx +++ b/src/sage/graphs/isoperimetric_inequalities.pyx @@ -110,7 +110,7 @@ def cheeger_constant(g): elif g.num_verts() == 1: return Infinity elif not g.is_connected(): - return QQ((0, 1)) + return QQ.zero() cdef StaticSparseCGraph cg cdef short_digraph sd # a copy of the graph g diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index e16a1445f3d..545f3f4fac1 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -277,7 +277,7 @@ def is_long_hole_free(g, certificate=False): if not res: # We release memory before returning the result - if isinstance(g, StaticSparseBackend): + if not isinstance(g, StaticSparseBackend): free_short_digraph(sd) bitset_free(dense_graph) @@ -526,7 +526,7 @@ def is_long_antihole_free(g, certificate=False): if not res: # We release memory before returning the result - if isinstance(g, StaticSparseBackend): + if not isinstance(g, StaticSparseBackend): free_short_digraph(sd) bitset_free(dense_graph) From 7455407ec6ca074a2c6c9a7d5b0d176e4f5cf218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Feb 2025 21:27:39 +0100 Subject: [PATCH 500/507] pep8 and mypy cleanup of arith/misc.py --- src/sage/arith/misc.py | 194 ++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 4feafec17c7..eb96ff5af61 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1456,16 +1456,16 @@ def random_prime(n, proof=None, lbound=2): return n lbound = max(2, lbound) if lbound > 2: - if lbound == 3 or n <= 2*lbound - 2: + if lbound == 3 or n <= 2 * lbound - 2: # check for Betrand's postulate (proved by Chebyshev) - if lbound < 25 or n <= 6*lbound/5: + if lbound < 25 or n <= 6 * lbound / 5: # see J. Nagura, Proc. Japan Acad. 28, (1952). 177-181 - if lbound < 2010760 or n <= 16598*lbound/16597: + if lbound < 2010760 or n <= 16598 * lbound / 16597: # see L. Schoenfeld, Math. Comp. 30 (1976), no 134, 337-360 if proof: - smallest_prime = ZZ(lbound-1).next_prime() + smallest_prime = ZZ(lbound - 1).next_prime() else: - smallest_prime = ZZ(lbound-1).next_probable_prime() + smallest_prime = ZZ(lbound - 1).next_probable_prime() if smallest_prime > n: raise ValueError("there are no primes between %s and %s (inclusive)" % (lbound, n)) @@ -1666,18 +1666,17 @@ def __call__(self, n, k=1): """ n = ZZ(n) k = ZZ(k) - one = ZZ(1) + one = ZZ.one() - if (k == ZZ(0)): - return prod(expt+one for p, expt in factor(n)) - elif (k == one): - return prod((p**(expt+one) - one).divide_knowing_divisible_by(p - one) - for p, expt in factor(n)) - else: - return prod((p**((expt+one)*k)-one).divide_knowing_divisible_by(p**k-one) - for p,expt in factor(n)) + if k == ZZ.zero(): + return prod(expt + one for p, expt in factor(n)) + if k == one: + return prod((p**(expt + one) - one).divide_knowing_divisible_by(p - one) + for p, expt in factor(n)) + return prod((p**((expt + one) * k) - one).divide_knowing_divisible_by(p**k - one) + for p, expt in factor(n)) - def plot(self, xmin=1, xmax=50, k=1, pointsize=30, rgbcolor=(0,0,1), join=True, + def plot(self, xmin=1, xmax=50, k=1, pointsize=30, rgbcolor=(0, 0, 1), join=True, **kwds): """ Plot the sigma (sum of `k`-th powers of divisors) function. @@ -1709,7 +1708,7 @@ def plot(self, xmin=1, xmax=50, k=1, pointsize=30, rgbcolor=(0,0,1), join=True, from sage.plot.all import list_plot P = list_plot(v, pointsize=pointsize, rgbcolor=rgbcolor, **kwds) if join: - P += list_plot(v, plotjoined=True, rgbcolor=(0.7,0.7,0.7), **kwds) + P += list_plot(v, plotjoined=True, rgbcolor=(0.7, 0.7, 0.7), **kwds) return P @@ -1887,11 +1886,11 @@ def __GCD_sequence(v, **kwargs): 1/10 """ if len(v) == 0: - return ZZ(0) - if hasattr(v,'universe'): + return ZZ.zero() + if hasattr(v, 'universe'): g = v.universe()(0) else: - g = ZZ(0) + g = ZZ.zero() for vi in v: g = vi.gcd(g, **kwargs) return g @@ -2186,7 +2185,7 @@ def xkcd(n=""): title = data['safe_title'] link = "http://xkcd.com/{}".format(data['num']) return html('

{}

'.format(title, img, alt) - + '
'.format(link)) + + '
Source: {0}
'.format(link)) # TODO: raise this error in such a way that it's not clear that # it is produced by sage, see http://xkcd.com/1024/ @@ -2496,20 +2495,20 @@ def mqrr_rational_reconstruction(u, m, T): if u == 0: if m > T: - return (0,1) + return (0, 1) else: return None n, d = 0, 0 t0, r0 = 0, m t1, r1 = 1, u while r1 != 0 and r0 > T: - q = r0/r1 # C division implicit floor + q = r0 / r1 # C division implicit floor if q > T: n, d, T = r1, t1, q - r0, r1 = r1, r0 - q*r1 - t0, t1 = t1, t0 - q*t1 - if d != 0 and GCD(n,d) == 1: - return (n,d) + r0, r1 = r1, r0 - q * r1 + t0, t1 = t1, t0 - q * t1 + if d != 0 and GCD(n, d) == 1: + return (n, d) return None @@ -3196,7 +3195,7 @@ def plot(self, xmin=1, xmax=50, pointsize=30, rgbcolor=(0, 0, 1), from sage.plot.all import list_plot P = list_plot(v, pointsize=pointsize, rgbcolor=rgbcolor, **kwds) if join: - P += list_plot(v, plotjoined=True, rgbcolor=(0.7,0.7,0.7), **kwds) + P += list_plot(v, plotjoined=True, rgbcolor=(0.7, 0.7, 0.7), **kwds) return P @@ -3339,13 +3338,13 @@ def carmichael_lambda(n): # first get rid of the prime factor 2 if n & 1 == 0: - two,e = L.pop(0) + two, e = L.pop(0) assert two == 2 k = e - 2 if e >= 3 else e - 1 t.append(1 << k) # then other prime factors - t += [p**(k - 1) * (p - 1) for p, k in L] + t.extend(p**(k - 1) * (p - 1) for p, k in L) # finish the job return LCM_list(t) @@ -3476,13 +3475,13 @@ def crt(a, b, m=None, n=None): return CRT_list(a, b) try: - f = (b-a).quo_rem + f = (b - a).quo_rem except (TypeError, AttributeError): # Maybe there is no coercion between a and b. # Maybe (b-a) does not have a quo_rem attribute a = py_scalar_to_element(a) b = py_scalar_to_element(b) - f = (b-a).quo_rem + f = (b - a).quo_rem g, alpha, beta = XGCD(m, n) q, r = f(g) @@ -3490,7 +3489,7 @@ def crt(a, b, m=None, n=None): raise ValueError("no solution to crt problem since gcd(%s,%s) does not divide %s-%s" % (m, n, a, b)) from sage.arith.functions import lcm - x = a + q*alpha*py_scalar_to_element(m) + x = a + q * alpha * py_scalar_to_element(m) return x % lcm(m, n) @@ -3615,6 +3614,7 @@ def CRT_list(values, moduli=None): moduli = [v.modulus() for v in values] values = [v.lift() for v in values] else: + assert moduli is not None if len(values) != len(moduli): raise ValueError("arguments to CRT_list should be lists of the same length") if not values: @@ -3688,8 +3688,9 @@ def CRT_vectors(X, moduli): r""" Vector form of the Chinese Remainder Theorem: given a list of integer vectors `v_i` and a list of coprime moduli `m_i`, find a vector `w` such - that `w = v_i \pmod m_i` for all `i`. This is more efficient than applying - :func:`CRT` to each entry. + that `w = v_i \pmod m_i` for all `i`. + + This is more efficient than applying :func:`CRT` to each entry. INPUT: @@ -3708,14 +3709,15 @@ def CRT_vectors(X, moduli): [10, 43, 17] """ # First find the CRT basis: - if len(X) == 0 or len(X[0]) == 0: + if not X or len(X[0]) == 0: return [] n = len(X) if n != len(moduli): raise ValueError("number of moduli must equal length of X") a = CRT_basis(moduli) modulus = prod(moduli) - return [sum(a[i]*X[i][j] for i in range(n)) % modulus for j in range(len(X[0]))] + return [sum(a[i] * X[i][j] for i in range(n)) % modulus + for j in range(len(X[0]))] def binomial(x, m, **kwds): @@ -3949,7 +3951,7 @@ def binomial(x, m, **kwds): m = ZZ(m) except TypeError: try: - m = ZZ(x-m) + m = ZZ(x - m) except TypeError: raise TypeError("either m or x-m must be an integer") @@ -3959,7 +3961,7 @@ def binomial(x, m, **kwds): # case 1: native binomial implemented on x try: return P(x.binomial(m, **kwds)) - except (AttributeError,TypeError): + except (AttributeError, TypeError): pass # case 2: conversion to integers @@ -3975,7 +3977,7 @@ def binomial(x, m, **kwds): # Assume that P has characteristic zero (can be int, float, ...) pass else: - if c > 0 and any(c.gcd(k) > 1 for k in range(2, m+1)): + if c > 0 and any(c.gcd(k) > 1 for k in range(2, m + 1)): raise ZeroDivisionError("factorial({}) not invertible in {}".format(m, P)) return P(x.binomial(m, **kwds)) @@ -4473,7 +4475,7 @@ def primitive_root(n, check=True): if n <= 4: if n: # n-1 is a primitive root for n in {1,2,3,4} - return n-1 + return n - 1 elif n % 2: # n odd if n.is_prime_power(): return ZZ(pari(n).znprimroot()) @@ -4557,7 +4559,7 @@ def quadratic_residues(n): [0, 1, 3, 4, 5, 9] """ n = abs(int(n)) - return sorted(set(ZZ((a*a) % n) for a in range(n // 2 + 1))) + return sorted(set(ZZ((a * a) % n) for a in range(n // 2 + 1))) class Moebius: @@ -4657,7 +4659,7 @@ def __repr__(self): """ return "The Moebius function" - def plot(self, xmin=0, xmax=50, pointsize=30, rgbcolor=(0,0,1), join=True, + def plot(self, xmin=0, xmax=50, pointsize=30, rgbcolor=(0, 0, 1), join=True, **kwds): """ Plot the Möbius function. @@ -4685,11 +4687,11 @@ def plot(self, xmin=0, xmax=50, pointsize=30, rgbcolor=(0,0,1), join=True, 1.0 """ values = self.range(xmin, xmax + 1) - v = [(n,values[n-xmin]) for n in range(xmin,xmax + 1)] + v = [(n, values[n - xmin]) for n in range(xmin, xmax + 1)] from sage.plot.all import list_plot P = list_plot(v, pointsize=pointsize, rgbcolor=rgbcolor, **kwds) if join: - P += list_plot(v, plotjoined=True, rgbcolor=(0.7,0.7,0.7), **kwds) + P += list_plot(v, plotjoined=True, rgbcolor=(0.7, 0.7, 0.7), **kwds) return P def range(self, start, stop=None, step=None): @@ -4723,8 +4725,8 @@ def range(self, start, stop=None, step=None): step = int(step) if start <= 0 < stop and start % step == 0: - return self.range(start, 0, step) + [ZZ.zero()] +\ - self.range(step, stop, step) + return self.range(start, 0, step) + [ZZ.zero()] + \ + self.range(step, stop, step) from sage.libs.pari import pari @@ -4822,8 +4824,8 @@ def continuant(v, n=None): if n == 1: return v[0] a, b = 1, v[0] - for k in range(1,n): - a, b = b, a + b*v[k] + for k in range(1, n): + a, b = b, a + b * v[k] return b @@ -4950,26 +4952,26 @@ def hilbert_symbol(a, b, p, algorithm='pari'): return one if a % p == 0: if b % p == 0: - return hilbert_symbol(p,-(b//p),p)*hilbert_symbol(a//p,b,p) + return hilbert_symbol(p, -(b // p), p) * hilbert_symbol(a // p, b, p) elif p == 2 and (b % 4) == 3: - if kronecker(a+b,p) == -1: + if kronecker(a + b, p) == -1: return -one - elif kronecker(b,p) == -1: + elif kronecker(b, p) == -1: return -one elif b % p == 0: if p == 2 and (a % 4) == 3: - if kronecker(a+b,p) == -1: + if kronecker(a + b, p) == -1: return -one - elif kronecker(a,p) == -1: + elif kronecker(a, p) == -1: return -one elif p == 2 and (a % 4) == 3 and (b % 4) == 3: return -one return one elif algorithm == 'all': - ans_pari = hilbert_symbol(a,b,p,algorithm='pari') - ans_direct = hilbert_symbol(a,b,p,algorithm='direct') + ans_pari = hilbert_symbol(a, b, p, algorithm='pari') + ans_direct = hilbert_symbol(a, b, p, algorithm='direct') if ans_pari != ans_direct: - raise RuntimeError("there is a bug in hilbert_symbol; two ways of computing the Hilbert symbol (%s,%s)_%s disagree" % (a,b,p)) + raise RuntimeError("there is a bug in hilbert_symbol; two ways of computing the Hilbert symbol (%s,%s)_%s disagree" % (a, b, p)) return ans_pari else: raise ValueError(f"algorithm {algorithm} not defined") @@ -5077,7 +5079,7 @@ def hilbert_conductor_inverse(d): if d <= 0: raise ValueError("d needs to be positive") if d == 1: - return (Z(-1), Z(1)) + return (Z(-1), Z.one()) if d == 2: return (Z(-1), Z(-1)) if d.is_prime(): @@ -5086,14 +5088,14 @@ def hilbert_conductor_inverse(d): if d % 8 == 5: return (Z(-2), -d) q = 3 - while q % 4 != 3 or kronecker_symbol(d,q) != -1: + while q % 4 != 3 or kronecker_symbol(d, q) != -1: q = next_prime(q) return (Z(-q), -d) else: mo = moebius(d) if mo == 0: raise ValueError("d needs to be squarefree") - if d % 2 == 0 and mo*d % 16 != 2: + if d % 2 == 0 and mo * d % 16 != 2: dd = mo * d / 2 else: dd = mo * d @@ -5491,41 +5493,38 @@ def two_squares(n): from sage.rings.finite_rings.integer_mod import Mod a = ZZ.one() b = ZZ.zero() - for (p,e) in F: + for p, e in F: if e >= 2: - m = p ** (e//2) + m = p ** (e // 2) a *= m b *= m if e % 2: if p == 2: # (a + bi) *= (1 + I) - a,b = a - b, a + b + a, b = a - b, a + b else: # p = 1 mod 4 # Find a square root of -1 mod p. # If y is a non-square, then y^((p-1)/4) is a square root of -1. - y = Mod(2,p) + y = Mod(2, p) while True: - s = y**((p-1)/4) - if not s*s + 1: + s = y**((p - 1) / 4) + if not s * s + 1: s = s.lift() break y += 1 # Apply Cornacchia's algorithm to write p as r^2 + s^2. r = p - while s*s > p: - r,s = s, r % s + while s * s > p: + r, s = s, r % s r %= s # Multiply (a + bI) by (r + sI) - a,b = a*r - b*s, b*r + a*s + a, b = a * r - b * s, b * r + a * s a = a.abs() b = b.abs() - assert a*a + b*b == n - if a <= b: - return (a,b) - else: - return (b,a) + assert a * a + b * b == n + return (a, b) if a <= b else (b, a) def three_squares(n): @@ -5602,9 +5601,9 @@ def three_squares(n): return sum_of_squares.three_squares_pyx(n) # First, remove all factors 4 from n - e = n.valuation(2)//2 + e = n.valuation(2) // 2 m = ZZ.one() << e - N = n >> (2*e) + N = n >> (2 * e) # Let x be the largest integer at most sqrt(N) x, r = N.sqrtrem() @@ -5612,7 +5611,7 @@ def three_squares(n): # otherwise N - x^2 will always factor. if not r: z = ZZ.zero() - return (z, z, x*m) + return (z, z, x * m) # Consider different cases to find an x such that N - x^2 is easily # written as the sum of 2 squares, because it is either p or 2p, @@ -5622,7 +5621,7 @@ def three_squares(n): if x % 2: x -= 1 while x >= 0: - p = N - x*x + p = N - x * x if p.is_pseudoprime(): break x -= 2 @@ -5631,7 +5630,7 @@ def three_squares(n): if x % 2 == 0: x -= 1 while x >= 0: - p = N - x*x + p = N - x * x if p.is_pseudoprime(): break x -= 2 @@ -5640,7 +5639,7 @@ def three_squares(n): if x % 2 == 0: x -= 1 while x >= 0: - p = (N - x*x) >> 1 + p = (N - x * x) >> 1 if p.is_pseudoprime(): break x -= 2 @@ -5660,18 +5659,18 @@ def three_squares(n): # This will only really loop if we hit the "x < 0" case above. while True: try: - a,b = two_squares(N - x*x) + a, b = two_squares(N - x * x) break except ValueError: x -= 1 assert x >= 0 if x >= b: - return (a*m, b*m, x*m) + return (a * m, b * m, x * m) elif x >= a: - return (a*m, x*m, b*m) + return (a * m, x * m, b * m) else: - return (x*m, a*m, b*m) + return (x * m, a * m, b * m) def four_squares(n): @@ -5733,20 +5732,20 @@ def four_squares(n): # First, remove all factors 4 from n e = n.valuation(2) // 2 m = ZZ.one() << e - N = n >> (2*e) + N = n >> (2 * e) # Subtract a suitable x^2 such that N - x^2 is 1,2,3,5,6 mod 8, # which can then be written as a sum of 3 squares. x = N.isqrt() - y = N - x*x + y = N - x * x if y >= 7 and (y % 4 == 0 or y % 8 == 7): x -= 1 - y += 2*x + 1 + y += 2 * x + 1 - a,b,c = three_squares(y) + a, b, c = three_squares(y) # Correct sorting is guaranteed by construction - return (a*m, b*m, c*m, x*m) + return (a * m, b * m, c * m, x * m) def sum_of_k_squares(k, n): @@ -5840,18 +5839,19 @@ def sum_of_k_squares(k, n): raise ValueError("k = %s must be nonnegative" % k) if n < 0: - raise ValueError("%s is not a sum of %s squares" % (n,k)) + raise ValueError("%s is not a sum of %s squares" % (n, k)) # Recursively subtract the largest square - t = [] + t: list[int] = [] while k > 4: x = n.isqrt() - t.insert(0, x) - n -= x*x + t.append(x) + n -= x * x k -= 1 - t = list(four_squares(n)) + t - return tuple(t) + u = list(four_squares(n)) + u.extend(reversed(t)) + return tuple(u) def subfactorial(n): @@ -6451,17 +6451,17 @@ def smooth_part(x, base): tree = ProductTree(base) fs = [] rems = tree.remainders(x) - for j,(p,r) in enumerate(zip(tree, rems)): + for j, (p, r) in enumerate(zip(tree, rems)): if not r: x //= p v = 1 while True: - y,r = divmod(x, p) + y, r = divmod(x, p) if r: break x = y v += 1 - fs.append((p,v)) + fs.append((p, v)) from sage.structure.factorization import Factorization return Factorization(fs) From 5737d1903ed8b7a2a2548ef32a46ca780c452907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Feb 2025 21:36:27 +0100 Subject: [PATCH 501/507] one more detail --- src/sage/arith/misc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index eb96ff5af61..4ef197bfaf4 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -2700,11 +2700,11 @@ def factor(n, proof=None, int_=False, algorithm='pari', verbose=0, **kwds): 2^2 * 3 * 5 * 7 sage: [x for x in f] [(2, 2), (3, 1), (5, 1), (7, 1)] - sage: [p for p,e in f] + sage: [p for p, e in f] [2, 3, 5, 7] - sage: [e for p,e in f] + sage: [e for p, e in f] [2, 1, 1, 1] - sage: [p^e for p,e in f] + sage: [p^e for p, e in f] [4, 3, 5, 7] We can factor Python, numpy and gmpy2 numbers:: From 26316fca83a53475337393515958c30ec74af6eb Mon Sep 17 00:00:00 2001 From: Vincent Neiger Date: Tue, 25 Feb 2025 09:45:27 +0100 Subject: [PATCH 502/507] add 'SEEALSO' blocks --- src/sage/matrix/matrix_polynomial_dense.pyx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index cea4f5ed6a6..f1f6ac44e54 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -3581,6 +3581,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Traceback (most recent call last): ... ValueError: shifts length should be the row dimension + + .. SEEALSO:: + + :meth:`minimal_interpolant_basis`, :meth:`minimal_relation_basis` """ m = self.nrows() n = self.ncols() @@ -3909,6 +3913,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Traceback (most recent call last): ... ValueError: shifts length should be the row dimension + + .. SEEALSO:: + + :meth:`minimal_approximant_basis`, :meth:`minimal_relation_basis` """ from sage.matrix.constructor import matrix # for identity from copy import copy @@ -4573,6 +4581,10 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): Traceback (most recent call last): ... ValueError: modulus matrix must be nonsingular + + .. SEEALSO:: + + :meth:`minimal_approximant_basis`, :meth:`minimal_interpolant_basis` """ from sage.matrix.constructor import matrix # for matrix.block From 125c475af88e1b19e24260207e4e22a9753acdb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Feb 2025 11:31:41 +0100 Subject: [PATCH 503/507] remove useless line in poset factor method --- src/sage/combinat/posets/posets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index d11b2f941a9..883de7d63b5 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5335,7 +5335,6 @@ def edge_color(va, vb): fusion.add_edge([i0, i1]) break - fusion = fusion.transitive_closure() resu = [] for s in fusion.connected_components(sort=False): subg = [x for x in prod_dg if all(x[i] == v0[i] for i in factors_range From 2b692a1383771c7011e187fe6a8258288bc03189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Feb 2025 13:32:45 +0100 Subject: [PATCH 504/507] trying to enhance poset factorisation --- src/sage/combinat/posets/posets.py | 35 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 883de7d63b5..43db574883d 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5270,19 +5270,24 @@ def factor(self): sage: P.factor() Traceback (most recent call last): ... - NotImplementedError: the poset is not connected + NotImplementedError: the poset is empty or not connected sage: P = posets.Crown(2) sage: P.factor() [Finite poset containing 4 elements] sage: Poset().factor() - [Finite poset containing 0 elements] + Traceback (most recent call last): + ... + NotImplementedError: the poset is empty or not connected sage: factor(posets.BooleanLattice(2)) [Finite poset containing 2 elements, Finite poset containing 2 elements] + sage: factor(Poset(DiGraph([[0,1],[1,2],[0,3]]))) + [Finite poset containing 4 elements] + REFERENCES: .. [Feig1986] Joan Feigenbaum, *Directed Cartesian-Product Graphs @@ -5293,10 +5298,13 @@ def factor(self): from sage.graphs.graph import Graph from sage.misc.flatten import flatten dg = self._hasse_diagram - if not dg.is_connected(): - raise NotImplementedError('the poset is not connected') + if not dg.is_connected() or not dg.order(): + raise NotImplementedError('the poset is empty or not connected') if Integer(dg.num_verts()).is_prime(): return [self] + if sum(e for _, e in self.degree_polynomial().factor()) == 1: + return [self] + G = dg.to_undirected() is_product, dic = G.is_cartesian_product(relabeling=True) if not is_product: @@ -5307,19 +5315,22 @@ def factor(self): v0 = next(iter(dic.values())) n = len(v0) factors_range = range(n) - fusion = Graph(n) def edge_color(va, vb): for i in range(n): if va[i] != vb[i]: return i + neighbors_table = {x: [[y for y in prod_dg.neighbor_iterator(x) + if edge_color(x, y) == i] + for i in factors_range] + for x in prod_dg} + + fusion_edges = [] for i0, i1 in Subsets(factors_range, 2): for x in prod_dg: - neigh0 = [y for y in prod_dg.neighbor_iterator(x) - if edge_color(x, y) == i0] - neigh1 = [z for z in prod_dg.neighbor_iterator(x) - if edge_color(x, z) == i1] + neigh0 = neighbors_table[x][i0] + neigh1 = neighbors_table[x][i1] for x0, x1 in product(neigh0, neigh1): _x2 = list(x0) _x2[i1] = x1[i1] @@ -5327,14 +5338,16 @@ def edge_color(va, vb): A0 = prod_dg.has_edge(x, x0) B0 = prod_dg.has_edge(x1, x2) if A0 != B0: - fusion.add_edge([i0, i1]) + fusion_edges.append([i0, i1]) break A1 = prod_dg.has_edge(x, x1) B1 = prod_dg.has_edge(x0, x2) if A1 != B1: - fusion.add_edge([i0, i1]) + fusion_edges.append([i0, i1]) break + fusion = Graph([list(range(n)), fusion_edges], + format="vertices_and_edges") resu = [] for s in fusion.connected_components(sort=False): subg = [x for x in prod_dg if all(x[i] == v0[i] for i in factors_range From 1fb49d7eb1aabdfff943fcfd20301fc8bb60d1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Feb 2025 15:49:08 +0100 Subject: [PATCH 505/507] try an idea --- src/sage/combinat/posets/posets.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 43db574883d..a22c2f104df 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5232,7 +5232,7 @@ def factor(self): """ Factor the poset as a Cartesian product of smaller posets. - This only works for connected posets for the moment. + This only works for connected posets. The decomposition of a connected poset as a Cartesian product of posets (prime in the sense that they cannot be written as @@ -5309,7 +5309,7 @@ def factor(self): is_product, dic = G.is_cartesian_product(relabeling=True) if not is_product: return [self] - dic = {key: tuple(flatten(dic[key])) for key in dic} + dic = {key: tuple(flatten(val)) for key, val in dic.items()} prod_dg = dg.relabel(dic, inplace=False) v0 = next(iter(dic.values())) @@ -5317,14 +5317,15 @@ def factor(self): factors_range = range(n) def edge_color(va, vb): - for i in range(n): - if va[i] != vb[i]: - return i - - neighbors_table = {x: [[y for y in prod_dg.neighbor_iterator(x) - if edge_color(x, y) == i] - for i in factors_range] - for x in prod_dg} + return next(i for i, (vai, vbi) in enumerate(zip(va, vb)) + if vai != vbi) + + neighbors_table = {} + for x in prod_dg: + z = [[] for _ in range(n)] + for y in prod_dg.neighbor_iterator(x): + z[edge_color(x, y)].append(y) + neighbors_table[x] = z fusion_edges = [] for i0, i1 in Subsets(factors_range, 2): From 4b82be4663a3fec19e410cde014f91a41cf2bd45 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 10 Feb 2025 23:09:39 -0600 Subject: [PATCH 506/507] update meson and meson-python to the latest versions --- build/pkgs/meson/checksums.ini | 4 ++-- build/pkgs/meson/package-version.txt | 2 +- build/pkgs/meson_python/checksums.ini | 4 ++-- build/pkgs/meson_python/package-version.txt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini index 23230d96040..0767e4d3390 100644 --- a/build/pkgs/meson/checksums.ini +++ b/build/pkgs/meson/checksums.ini @@ -1,4 +1,4 @@ tarball=meson-VERSION-py3-none-any.whl -sha1=baf5b9bc9ca97f18c7dc87cfaf0e1dc4d617a4cf -sha256=d5223ecca9564d735d36daaba2571abc6c032c8c3a7ffa0674e803ef0c7e0219 +sha1=88a5ccf7aba04e82c05f46dc02a4c209423e3f0d +sha256=ae3f12953045f3c7c60e27f2af1ad862f14dee125b4ed9bcb8a842a5080dbf85 upstream_url=https://files.pythonhosted.org/packages/py3/m/meson/meson-VERSION-py3-none-any.whl diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt index 3a3cd8cc8b0..bd8bf882d06 100644 --- a/build/pkgs/meson/package-version.txt +++ b/build/pkgs/meson/package-version.txt @@ -1 +1 @@ -1.3.1 +1.7.0 diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index d754ae46233..4dac49782b2 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,4 +1,4 @@ tarball=meson_python-VERSION.tar.gz -sha1=71bf382c2f2e76aada2f511a84bd59a99a6b1238 -sha256=fddb73eecd49e89c1c41c87937cd89c2d0b65a1c63ba28238681d4bd9484d26f +sha1=9e6dbb00173189dc2d5b0a7eeb8a206268dc0a4f +sha256=efb91f69f2e19eef7bc9a471ed2a4e730088cc6b39eacaf3e49fc4f930eb5f83 upstream_url=https://files.pythonhosted.org/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index a5510516948..7cca7711a0d 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.15.0 +0.17.1 From 7ef543355f7bca75e3aa2e8e90cbd5e41a2c2f46 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Fri, 28 Feb 2025 21:21:22 +0100 Subject: [PATCH 507/507] Updated SageMath version to 10.6.beta8 --- CITATION.cff | 4 ++-- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 4 ++-- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/version_requirements.txt | 2 +- build/pkgs/sage_docbuild/version_requirements.txt | 2 +- build/pkgs/sage_setup/version_requirements.txt | 2 +- build/pkgs/sage_sws2rst/version_requirements.txt | 2 +- build/pkgs/sagelib/version_requirements.txt | 2 +- build/pkgs/sagemath_bliss/version_requirements.txt | 2 +- build/pkgs/sagemath_categories/version_requirements.txt | 2 +- build/pkgs/sagemath_coxeter3/version_requirements.txt | 2 +- build/pkgs/sagemath_environment/version_requirements.txt | 2 +- build/pkgs/sagemath_mcqd/version_requirements.txt | 2 +- build/pkgs/sagemath_meataxe/version_requirements.txt | 2 +- build/pkgs/sagemath_objects/version_requirements.txt | 2 +- build/pkgs/sagemath_repl/version_requirements.txt | 2 +- build/pkgs/sagemath_sirocco/version_requirements.txt | 2 +- build/pkgs/sagemath_tdlib/version_requirements.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_conda/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-bliss/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-coxeter3/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-mcqd/VERSION.txt | 2 +- pkgs/sagemath-meataxe/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- pkgs/sagemath-sirocco/VERSION.txt | 2 +- pkgs/sagemath-tdlib/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 38 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index bf07776a356..2396e20378b 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.6.beta7 +version: 10.6.beta8 doi: 10.5281/zenodo.8042260 -date-released: 2025-02-21 +date-released: 2025-02-28 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index c20b0627cda..82a45f43151 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.6.beta7, Release Date: 2025-02-21 +SageMath version 10.6.beta8, Release Date: 2025-02-28 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 1b9ad33fde5..9fe4eacaa79 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=27066acef8c8ecf26fec60314786b7badc4241b9 -sha256=97d840ff5a0404351a814577ed021e2aa27ace1a77f89917b907eb1a4b1ca393 +sha1=e093bc99c7c72359d0ea6afd49046133607fe391 +sha256=2d5d04f26068b2c8aafd1e3b69e23ae2788390846f779e5714df6856ce89cde1 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index cdaef2a43a1..c70aebdc1c8 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -402ea35f5510d6664356dbfd218807355197fad1 +77c4e9b745daab99cdc0e2e989515a6f4ec91ed9 diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 07a61ab735e..6bbeb4e62c5 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.6b7 +sage-conf ~= 10.6b8 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index f0421af21af..61e7edf8ac5 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.6b7 +sage-docbuild ~= 10.6b8 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index 48fc6cd26a5..f9499ea2b51 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.6b7 +sage-setup ~= 10.6b8 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index 22e0bd981fc..6c6123c64b2 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.6b7 +sage-sws2rst ~= 10.6b8 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 92e731e689a..0c40a7d6a6f 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.6b7 +sagemath-standard ~= 10.6b8 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index 1dcf8a9322a..ad2bcb89655 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.6b7 +sagemath-bliss ~= 10.6b8 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 639f01d78ee..13955dbca1e 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.6b7 +sagemath-categories ~= 10.6b8 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index f715f9357e4..233620005cb 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.6b7 +sagemath-coxeter3 ~= 10.6b8 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 93ecd3e5a14..934987ed255 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.6b7 +sagemath-environment ~= 10.6b8 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index 70273aad838..37bc4c345a2 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.6b7 +sagemath-mcqd ~= 10.6b8 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index 8036474a775..c66674c0977 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.6b7 +sagemath-meataxe ~= 10.6b8 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 66992114227..a133d70c5fd 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.6b7 +sagemath-objects ~= 10.6b8 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index 22b16b93978..5803db9adde 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.6b7 +sagemath-repl ~= 10.6b8 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index 3dd38fd94ac..46d3dec865a 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.6b7 +sagemath-sirocco ~= 10.6b8 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 15ac9d50e66..49fbad0f185 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.6b7 +sagemath-tdlib ~= 10.6b8 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/src/VERSION.txt b/src/VERSION.txt index 51a36a91c8a..f8811841aa9 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.6.beta7 +10.6.beta8 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 2dea11b2d1e..a2dcc9c9c31 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.6.beta7' -SAGE_RELEASE_DATE='2025-02-21' -SAGE_VERSION_BANNER='SageMath version 10.6.beta7, Release Date: 2025-02-21' +SAGE_VERSION='10.6.beta8' +SAGE_RELEASE_DATE='2025-02-28' +SAGE_VERSION_BANNER='SageMath version 10.6.beta8, Release Date: 2025-02-28' diff --git a/src/sage/version.py b/src/sage/version.py index 37a2f5f5fba..c9b3d0a929b 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.6.beta7' -date = '2025-02-21' -banner = 'SageMath version 10.6.beta7, Release Date: 2025-02-21' +version = '10.6.beta8' +date = '2025-02-28' +banner = 'SageMath version 10.6.beta8, Release Date: 2025-02-28'