From 59226b1d799b971de9a535f4b51ed124597e713b Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sat, 11 Jun 2022 21:05:41 +0900 Subject: [PATCH] Add free resolutions --- src/doc/en/reference/index.rst | 12 +- src/doc/en/reference/references/index.rst | 7 +- src/doc/en/reference/resolutions/conf.py | 1 + src/doc/en/reference/resolutions/index.rst | 13 + src/sage/homology/free_resolution.pxd | 3 + src/sage/homology/free_resolution.pyx | 667 +++++++++++++++++++++ src/sage/homology/graded_resolution.pyx | 524 ++++++++++++++++ src/sage/interfaces/singular.py | 62 +- src/sage/libs/singular/decl.pxd | 8 + src/sage/libs/singular/function.pxd | 1 + src/sage/libs/singular/function.pyx | 119 ++-- src/sage/libs/singular/polynomial.pyx | 7 - src/sage/libs/singular/singular.pyx | 7 - 13 files changed, 1308 insertions(+), 123 deletions(-) create mode 120000 src/doc/en/reference/resolutions/conf.py create mode 100644 src/doc/en/reference/resolutions/index.rst create mode 100644 src/sage/homology/free_resolution.pxd create mode 100644 src/sage/homology/free_resolution.pyx create mode 100644 src/sage/homology/graded_resolution.pyx diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 065bccac955..9008c78e157 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -92,18 +92,22 @@ Discrete Mathematics * :doc:`Symbolic Logic ` * :doc:`SAT solvers ` -Geometry, Topology, and Homological Algebra -------------------------------------------- +Geometry and Topology +--------------------- * :doc:`Euclidean Spaces and Vector Calculus ` * :doc:`Combinatorial and Discrete Geometry ` -* :doc:`Cell Complexes, Simplicial Complexes, and - Simplicial Sets ` +* :doc:`Cell Complexes, Simplicial Complexes, and Simplicial Sets ` * :doc:`Manifolds and Differential Geometry ` * :doc:`Hyperbolic Geometry ` * :doc:`Parametrized Surfaces ` * :doc:`Knot Theory ` + +Homological Algebra +------------------- + * :doc:`Chain Complexes and their Homology ` +* :doc:`Resolutions ` Number Fields, Function Fields, and Valuations ---------------------------------------------- diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 30a792e8214..70ccf51e48c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -95,7 +95,7 @@ REFERENCES: graphs and isoperimetric inequalities*, The Annals of Probability 32 (2004), no. 3A, 1727-1745. -.. [ASV2020] Federico Ardila, Mariel Supina, and Andrés R. Vindas-Meléndez, +.. [ASV2020] Federico Ardila, Mariel Supina, and Andrés R. Vindas-Meléndez, *The Equivariant Ehrhart Theory of the Permutahedron*, Proc. Amer. Math. Soc. Volume 148, Number 12, 2020, pp. 5091--5107. @@ -4296,6 +4296,9 @@ REFERENCES: polynomials*. Trans. Amer. Math. Soc., 245 (1978), 89-118. +.. [MilStu2005] Ezra Miller and Bernd Sturmfels, *Combinatorial Commutative Algebra*, + GTM Vol. 227, Springer Science & Business Media, 2005. + .. [Mil2004] Victor S. Miller, "The Weil pairing, and its efficient calculation", J. Cryptol., 17(4):235-261, 2004 @@ -5370,7 +5373,7 @@ REFERENCES: .. [St1986] Richard Stanley. *Two poset polytopes*, Discrete Comput. Geom. (1986), :doi:`10.1007/BF02187680` -.. [Stap2011] Alan Stapledon. *Equivariant Ehrhart Theory*. +.. [Stap2011] Alan Stapledon. *Equivariant Ehrhart Theory*. Advances in Mathematics 226 (2011), no. 4, 3622-3654 .. [Sta1973] \H. M. Stark, Class-Numbers of Complex Quadratic diff --git a/src/doc/en/reference/resolutions/conf.py b/src/doc/en/reference/resolutions/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/resolutions/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/resolutions/index.rst b/src/doc/en/reference/resolutions/index.rst new file mode 100644 index 00000000000..82f73cb94c8 --- /dev/null +++ b/src/doc/en/reference/resolutions/index.rst @@ -0,0 +1,13 @@ +Resolutions +=========== + +Free and graded resolutions are tools for commutative algebra and algebraic +geometry. + +.. toctree:: + :maxdepth: 2 + + sage/homology/free_resolution + sage/homology/graded_resolution + +.. include:: ../footer.txt diff --git a/src/sage/homology/free_resolution.pxd b/src/sage/homology/free_resolution.pxd new file mode 100644 index 00000000000..4ad023387d8 --- /dev/null +++ b/src/sage/homology/free_resolution.pxd @@ -0,0 +1,3 @@ +from sage.libs.singular.decl cimport * + +cdef singular_monomial_exponents(poly *p, ring *r) diff --git a/src/sage/homology/free_resolution.pyx b/src/sage/homology/free_resolution.pyx new file mode 100644 index 00000000000..be084842b45 --- /dev/null +++ b/src/sage/homology/free_resolution.pyx @@ -0,0 +1,667 @@ +""" +Free resolutions + +The :class:`FreeResolution` implements a finite free resolution, which is a +chain complex of free modules, terminating with a zero module at the end, whose +homology groups are all zero. + +The class is intended to be subclassed for finite free resolutions in different +subject areas. Thus :meth:`_repr_module` may be overrided by a subclass. See +Examples below. + +EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution_generic + sage: S. = PolynomialRing(QQ) + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: m2 = matrix(S, 3, [-y, x, z, -y, -w, z]) + sage: r = FreeResolution_generic(S, [m1, m2], name='S') + sage: r + S^1 <-- S^3 <-- S^2 <-- 0 + +:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + +The :class:`FreeResolution` computes a minimal free resolution of modules +over a multivariate polynomial ring. + +EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r + S^1 <-- S^3 <-- S^2 <-- 0 + +An example of a minimal free resolution from [CLO2005]_:: + + sage: R. = QQ[] + sage: I = R.ideal([y*z - x*w, y^3 - x^2*z, x*z^2 - y^2*w, z^3 - y*w^2]) + sage: r = FreeResolution(I) + sage: r + S^1 <-- S^4 <-- S^4 <-- S^1 <-- 0 + sage: len(r) + 3 + sage: r.matrix(2) + [-z^2 -x*z y*w -y^2] + [ y 0 -x 0] + [ -w y z x] + [ 0 w 0 z] + +AUTHORS: + +- Kwankyu Lee (2022-05-13): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2022 Kwankyu Lee +# +# 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.libs.singular.decl cimport * +from sage.libs.singular.decl cimport ring +from sage.libs.singular.function cimport Resolution, new_sage_polynomial, access_singular_ring +from sage.libs.singular.function import singular_function +from sage.structure.sequence import Sequence, Sequence_generic +from sage.misc.cachefunc import cached_method +from sage.matrix.constructor import matrix as _matrix +from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense +from sage.modules.free_module_element import vector +from sage.modules.free_module import Module_free_ambient +from sage.rings.integer_ring import ZZ +from sage.rings.ideal import Ideal_generic + +from sage.structure.sage_object import SageObject + + +class FreeResolution_generic(SageObject): + """ + Base class of free resolutions. + + INPUT: + + - ``base_ring`` -- a ring + + - ``maps`` -- list of matrices over the base ring + + The matrix at index `i` in the list defines the differential map from + `(i+1)`-th free module to the `i`-th free module over the base ring by + multiplication on the left. The number of matrices in the list is the + length of the resolution. The number of rows and columns of the matrices + define the ranks of the free modules in the resolution. + + Note that the first matrix in the list defines the differential map at + homological index `1`. A subclass can define ``_initial_differential`` + attribute that contains the `0`-th differential map whose codomain is the + target of the free resolution. + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution_generic + sage: S. = PolynomialRing(QQ) + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: m2 = matrix(S, 3, [-y, x, z, -y, -w, z]) + sage: r = FreeResolution_generic(S, [m1, m2], name='S') + sage: r + S^1 <-- S^3 <-- S^2 <-- 0 + + :: + + sage: from sage.homology.free_resolution import FreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r.differential(0) + Coercion map: + From: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + To: Quotient module by Submodule of Ambient free module of rank 1 + over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + def __init__(self, base_ring, maps, name='F'): + """ + Initialize. + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution_generic + sage: S. = PolynomialRing(QQ) + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: m2 = matrix(S, 3, [-y, x, z, -y, -w, z]) + sage: r = FreeResolution_generic(S, [m1, m2], name='S') + sage: TestSuite(r).run(skip=['_test_pickling']) + """ + self.__base_ring = base_ring + self.__maps = maps + self.__name = name + self.__length = len(maps) + + def __repr__(self): + """ + Return the string form of this resolution. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + """ + s = self._repr_module(0) + for i in range(1, self.__length + 1): + s += ' <-- ' + self._repr_module(i) + s += ' <-- 0' + return s + + def _repr_module(self, i): + """ + Return the string form of the `i`-th free module. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution_generic + sage: S. = PolynomialRing(QQ) + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: m2 = matrix(S, 3, [-y, x, z, -y, -w, z]) + sage: r = FreeResolution_generic(S, [m1, m2], name='S') + sage: r # indirect doctest + S^1 <-- S^3 <-- S^2 <-- 0 + """ + if i == 0: + r = self.__maps[0].nrows() + s = f'{self.__name}^{r}' + return s + elif i > self.__length: + s = '0' + else: + r = self.__maps[i - 1].ncols() + if r > 0: + s = f'{self.__name}^{r}' + else: + s = '0' + return s + + def __len__(self): + """ + Return the length of this resolution. + + The length of a free resolution is the index of the last nonzero free module. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: len(r) + 2 + """ + return self.__length + + def __getitem__(self, i): + """ + Return the `i`-th free module of this resolution. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: r.target() + Quotient module by Submodule of Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + if i < 0: + raise IndexError('invalid index') + elif i > self.__length: + F = (self.__base_ring)**0 + elif i == self.__length: + F = (self.__base_ring)**(self.__maps[i - 1].ncols()) + else: + F = (self.__base_ring)**(self.__maps[i].nrows()) + return F + + def differential(self, i): + """ + Return the matrix representing the `i`-th differential map. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: r.differential(3) + Free module morphism defined by the matrix + [] + Domain: Ambient free module of rank 0 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Codomain: Ambient free module of rank 2 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + sage: r.differential(2) + Free module morphism defined as left-multiplication by the matrix + [-y x] + [ z -y] + [-w z] + Domain: Ambient free module of rank 2 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Codomain: Ambient free module of rank 3 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + sage: r.differential(1) + Free module morphism defined as left-multiplication by the matrix + [z^2 - y*w y*z - x*w y^2 - x*z] + Domain: Ambient free module of rank 3 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Codomain: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + sage: r.differential(0) + Coercion map: + From: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + To: Quotient module by Submodule of Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + if i < 0: + raise IndexError('invalid index') + elif i == 0: + try: + return self._initial_differential + except AttributeError: + raise ValueError('0th differential map undefined') + elif i == self.__length + 1: + s = (self.__base_ring)**0 + t = (self.__base_ring)**(self.__maps[i - 2].ncols()) + m = s.hom(0, t) + elif i > self.__length + 1: + s = (self.__base_ring)**0 + t = (self.__base_ring)**0 + m = s.hom(0, t) + else: + s = (self.__base_ring)**(self.__maps[i - 1].ncols()) + t = (self.__base_ring)**(self.__maps[i - 1].nrows()) + m = s.hom(self.__maps[i - 1], t, side='right') + return m + + def target(self): + """ + Return the codomain of the 0-th differential map. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: r.target() + Quotient module by Submodule of Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + return self.differential(0).codomain() + + def matrix(self, i): + """ + Return the matrix representing the `i`-th differential map. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: r.matrix(3) + [] + sage: r.matrix(2) + [-y x] + [ z -y] + [-w z] + sage: r.matrix(1) + [z^2 - y*w y*z - x*w y^2 - x*z] + """ + if i <= 0: + raise IndexError(f'invalid index') + elif i <= self.__length: + return self.__maps[i - 1] + else: + return self.differential(i).matrix() + + def chain_complex(self): + """ + Return this resolution as a chain complex. + + A chain complex in Sage has its own useful methods. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: unicode_art(r.chain_complex()) + ⎛-y x⎞ + ⎜ z -y⎟ + (z^2 - y*w y*z - x*w y^2 - x*z) ⎝-w z⎠ + 0 <── C_0 <────────────────────────────── C_1 <────── C_2 <── 0 + """ + from sage.homology.chain_complex import ChainComplex + mats = {} + for i in range(self.__length, 0, -1): + mats[i] = self.matrix(i) + return ChainComplex(mats, degree_of_differential=-1) + + +class FreeResolution(FreeResolution_generic): + """ + Minimal free resolutions of ideals of multivariate polynomial rings. + + INPUT: + + - ``ideal`` -- a homogeneous ideal of a multi-variate polynomial ring or + a submodule of a free module `M` of rank `n` over `S` + + - ``name`` -- a string; name of the base ring + + - ``algorithm`` -- Singular algorithm to compute a resolution of ``ideal`` + + OUTPUT: a minimal free resolution of the ideal + + The available algorithms and the corresponding Singular commands are shown + below: + + ============= ============================ + algorithm Singular commands + ============= ============================ + ``minimal`` ``mres(ideal)`` + ``shreyer`` ``minres(sres(std(ideal)))`` + ``standard`` ``minres(nres(std(ideal)))`` + ``heuristic`` ``minres(res(std(ideal)))`` + ============= ============================ + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r + S^1 <-- S^3 <-- S^2 <-- 0 + sage: len(r) + 2 + + :: + + sage: FreeResolution(I, algorithm='minimal') + S^1 <-- S^3 <-- S^2 <-- 0 + sage: FreeResolution(I, algorithm='shreyer') + S^1 <-- S^3 <-- S^2 <-- 0 + sage: FreeResolution(I, algorithm='standard') + S^1 <-- S^3 <-- S^2 <-- 0 + sage: FreeResolution(I, algorithm='heuristic') + S^1 <-- S^3 <-- S^2 <-- 0 + """ + def __init__(self, ideal, name='S', algorithm='heuristic'): + """ + Initialize. + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: TestSuite(r).run(skip=['_test_pickling']) + """ + if isinstance(ideal, Ideal_generic): + S = ideal.ring() + m = ideal + rank = 1 + elif isinstance(ideal, Module_free_ambient): + S = ideal.base_ring() + m = ideal.matrix().transpose() + rank = m.nrows() + elif isinstance(ideal, Matrix_mpolynomial_dense): + S = ideal.base_ring() + m = ideal.transpose() + rank = ideal.ncols() + else: + raise TypeError('no ideal, module, or matrix') + + nvars = S.ngens() + + # This ensures the first component of the Singular resolution to be a + # module, like the later components. This is important when the + # components are converted to Sage modules. + module = singular_function("module") + mod = module(m) + + if algorithm == 'minimal': + mres = singular_function('mres') # syzygy method + r = mres(mod, 0) + elif algorithm == 'shreyer': + std = singular_function('std') + sres = singular_function('sres') # Shreyer method + minres = singular_function('minres') + r = minres(sres(std(mod), 0)) + elif algorithm == 'standard': + nres = singular_function('nres') # standard basis method + minres = singular_function('minres') + r = minres(nres(mod, 0)) + elif algorithm == 'heuristic': + std = singular_function('std') + res = singular_function('res') # heuristic method + minres = singular_function('minres') + r = minres(res(std(mod), 0)) + + res_mats = to_sage_resolution(r) + + super().__init__(S, res_mats, name=name) + + self._ideal = ideal + self._name = name + + @property + @cached_method + def _initial_differential(self): + """ + Defines the `0`-th differential map of this resolution. + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r._initial_differential + Coercion map: + From: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + To: Quotient module by Submodule of Ambient free module of rank 1 + over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + ideal = self._ideal + if isinstance(ideal, Ideal_generic): + S = ideal.ring() + M = S**1 + N = M.submodule([vector([g]) for g in ideal.gens()]) + elif isinstance(ideal, Module_free_ambient): + S = ideal.base_ring() + M = ideal.ambient_module() + N = ideal + elif isinstance(ideal, Matrix_mpolynomial_dense): + S = ideal.base_ring() + N = ideal.row_space() + M = N.ambient_module() + Q = M.quotient(N) + return Q.coerce_map_from(M) + + +cdef singular_monomial_exponents(poly *p, ring *r): + """ + Return the list of exponents of monomial ``p``. + """ + cdef int v + cdef list ml = list() + + for v in range(1, r.N + 1): + ml.append(p_GetExp(p, v, r)) + return ml + +cdef to_sage_resolution(Resolution res): + """ + Pull the data from Singular resolution ``res`` to construct a Sage + resolution. + + INPUT: + + - ``res`` -- Singular resolution + + The procedure is destructive, and ``res`` is not usable afterward. + """ + cdef ring *singular_ring + cdef syStrategy singular_res + cdef poly *p + cdef poly *p_iter + cdef poly *first + cdef poly *previous + cdef poly *acc + cdef resolvente mods + cdef ideal *mod + cdef int i, j, k, idx, rank, nrows, ncols + cdef bint zero_mat + + singular_res = res._resolution[0] + sage_ring = res.base_ring + singular_ring = access_singular_ring(res.base_ring) + + if singular_res.minres != NULL: + mods = singular_res.minres + elif singular_res.fullres != NULL: + mods = singular_res.fullres + else: + raise ValueError('Singular resolution is not usable') + + res_mats = [] + + # length is the length of fullres. The length of minres + # can be shorter. Hence we avoid SEGFAULT by stopping + # at NULL pointer. + for idx in range(singular_res.length): + mod = mods[idx] + if mod == NULL: + break + rank = mod.rank + free_module = sage_ring ** rank + + nrows = rank + ncols = mod.ncols # IDELEMS(mod) + + mat = _matrix(sage_ring, nrows, ncols) + matdegs = [] + zero_mat = True + for j in range(ncols): + p = mod.m[j] + degs = [] + # code below copied and modified from to_sage_vector_destructive + # in sage.libs.singular.function.Converter + for i in range(1, rank + 1): + previous = NULL + acc = NULL + first = NULL + p_iter = p + while p_iter != NULL: + if p_GetComp(p_iter, singular_ring) == i: + p_SetComp(p_iter, 0, singular_ring) + p_Setm(p_iter, singular_ring) + if acc == NULL: + first = p_iter + else: + acc.next = p_iter + acc = p_iter + if p_iter == p: + p = pNext(p_iter) + if previous != NULL: + previous.next = pNext(p_iter) + p_iter = pNext(p_iter) + acc.next = NULL + else: + previous = p_iter + p_iter = pNext(p_iter) + + if zero_mat: + zero_mat = first == NULL + + mat[i - 1, j] = new_sage_polynomial(sage_ring, first) + + # Singular sometimes leaves zero matrix in the resolution. We can stop + # when one is seen. + if zero_mat: + break + + res_mats.append(mat) + + return res_mats diff --git a/src/sage/homology/graded_resolution.pyx b/src/sage/homology/graded_resolution.pyx new file mode 100644 index 00000000000..03620cdb195 --- /dev/null +++ b/src/sage/homology/graded_resolution.pyx @@ -0,0 +1,524 @@ +r""" +Graded free resolutions + +This module defines :class:`GradedFreeResolution` which computes a +graded free resolution of a homogeneous ideal `I` of a graded multivariate +polynomial ring `S`, or a homogeneous submodule of a graded free module `M` +over `S`. The output resolution is always minimal. + +The degrees given to the variables of `S` are integers or integer vectors of +the same length. In the latter case, `S` is said to be multigraded, and the +resolution is a multigraded free resolution. The standard grading where all +variables have degree `1` is used if the degrees are not specified. + +A summand of the graded free module `M` is a shifted (or twisted) module of +rank one over `S`, denoted `S(-d)` with shift `d`. + +The computation of the resolution is done by the libSingular behind. Different +Singular algorithms can be chosen for best performance. + +EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I, algorithm='minimal') + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: GradedFreeResolution(I, algorithm='shreyer') + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: GradedFreeResolution(I, algorithm='standard') + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: GradedFreeResolution(I, algorithm='heuristic') + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + +:: + + sage: d = r.differential(2) + sage: d + Free module morphism defined as left-multiplication by the matrix + [ y x] + [-z -y] + [ w z] + Domain: Ambient free module of rank 2 over the integral domain Multivariate Polynomial Ring + in x, y, z, w over Rational Field + Codomain: Ambient free module of rank 3 over the integral domain Multivariate Polynomial Ring + in x, y, z, w over Rational Field + sage: d.image() + Submodule of Ambient free module of rank 3 over the integral domain Multivariate Polynomial Ring + in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [ y -z w] + [ x -y z] + sage: m = d.image() + sage: GradedFreeResolution(m, shifts=(2,2,2)) + S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + +An example of multigraded resolution from Example 9.1 of [MilStu2005]_:: + + sage: R. = QQ[] + sage: S. = QQ[] + sage: phi = S.hom([s, s*t, s*t^2, s*t^3]) + sage: I = phi.kernel(); I + Ideal (c^2 - b*d, b*c - a*d, b^2 - a*c) of Multivariate Polynomial Ring in a, b, c, d over Rational Field + sage: P3 = ProjectiveSpace(S) + sage: C = P3.subscheme(I) # twisted cubic curve + sage: r = GradedFreeResolution(I, degrees=[(1,0), (1,1), (1,2), (1,3)]) + sage: r + S(0) <-- S(-(2, 4))⊕S(-(2, 3))⊕S(-(2, 2)) <-- S(-(3, 5))⊕S(-(3, 4)) <-- 0 + sage: r.K_polynomial(names='s,t') + s^3*t^5 + s^3*t^4 - s^2*t^4 - s^2*t^3 - s^2*t^2 + 1 + +AUTHORS: + +- Kwankyu Lee (2022-05): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2022 Kwankyu Lee +# +# 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.libs.singular.decl cimport * +from sage.libs.singular.decl cimport ring +from sage.libs.singular.function cimport Resolution, new_sage_polynomial, access_singular_ring +from sage.libs.singular.function import singular_function +from sage.structure.sequence import Sequence, Sequence_generic +from sage.misc.cachefunc import cached_method +from sage.matrix.constructor import matrix as _matrix +from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense +from sage.modules.free_module_element import vector +from sage.modules.free_module import Module_free_ambient +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.rings.ideal import Ideal_generic + +from sage.homology.free_resolution import FreeResolution +from sage.homology.free_resolution cimport singular_monomial_exponents + + +class GradedFreeResolution(FreeResolution): + """ + Graded free resolutions of ideals of multi-variate polynomial rings. + + INPUT: + + - ``ideal`` -- a homogeneous ideal of a multivariate polynomial ring `S`, or + a homogeneous submodule of a free module `M` of rank `n` over `S` + + - ``degree`` -- a list of integers or integer vectors giving degrees of + variables of `S`; this is a list of 1s by default + + - ``shifts`` -- a list of integers or integer vectors giving shifts of + degrees of `n` summands of the free module `M`; this is a list of zero + degrees of length `n` by default + + - ``name`` -- a string; name of the base ring + + - ``algorithm`` -- Singular algorithm to compute a resolution of ``ideal`` + + If ``ideal`` is an ideal of `S`, then `M = S`, a free module of rank `1` + over `S`. + + OUTPUT: a graded minimal free resolution of ``ideal`` + + The available algorithms and the corresponding Singular commands are shown + below: + + ============= ============================ + algorithm Singular commands + ============= ============================ + ``minimal`` ``mres(ideal)`` + ``shreyer`` ``minres(sres(std(ideal)))`` + ``standard`` ``minres(nres(std(ideal)))`` + ``heuristic`` ``minres(res(std(ideal)))`` + ============= ============================ + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: len(r) + 2 + """ + def __init__(self, ideal, degrees=None, shifts=None, name='S', algorithm='heuristic'): + """ + Initialize. + + TESTS:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: TestSuite(r).run(skip=['_test_pickling']) + """ + cdef int i, j, k, ncols, nrows + cdef list res_shifts, prev_shifts, new_shifts + + if isinstance(ideal, Ideal_generic): + S = ideal.ring() + m = ideal + rank = 1 + elif isinstance(ideal, Module_free_ambient): + S = ideal.base_ring() + m = ideal.matrix().transpose() + rank = m.nrows() + elif isinstance(ideal, Matrix_mpolynomial_dense): + S = ideal.base_ring() + m = ideal.transpose() + rank = ideal.ncols() + else: + raise TypeError('no ideal, module, or matrix') + + nvars = S.ngens() + + if degrees is None: + degrees = nvars*[1] # standard grading + + if len(degrees) != nvars: + raise ValueError('the length of degrees does not match the number of generators') + + if degrees[0] in ZZ: + zero_deg = 0 + multigrade = False + else: # degrees are integer vectors + degrees = [vector(v) for v in degrees] + zero_deg = degrees[0].parent().zero() + multigrade = True + + # This ensures the first component of the Singular resolution to be a + # module, like the later components. This is important when the + # components are converted to Sage modules. + module = singular_function("module") + mod = module(m) + + if shifts is None: + shifts = rank*[zero_deg] + + if algorithm == 'minimal': + mres = singular_function('mres') # syzygy method + r = mres(mod, 0) + elif algorithm == 'shreyer': + std = singular_function('std') + sres = singular_function('sres') # Shreyer method + minres = singular_function('minres') + r = minres(sres(std(mod), 0)) + elif algorithm == 'standard': + nres = singular_function('nres') # standard basis method + minres = singular_function('minres') + r = minres(nres(mod, 0)) + elif algorithm == 'heuristic': + std = singular_function('std') + res = singular_function('res') # heuristic method + minres = singular_function('minres') + r = minres(res(std(mod), 0)) + + res_mats, res_degs = to_sage_resolution_graded(r, degrees) + + # compute shifts of free modules in the resolution + res_shifts = [] + prev_shifts = list(shifts) + for k in range(len(res_degs)): + new_shifts = [] + degs = res_degs[k] + ncols = len(degs) + for j in range(ncols): + col = degs[j] + nrows = len(col) + # should be enough to compute the new shifts + # from any one entry of the column vector + for i in range(nrows): + d = col[i] + if d is not None: + e = prev_shifts[i] + new_shifts.append(d + e) + break + res_shifts.append(new_shifts) + prev_shifts = new_shifts + + super(FreeResolution, self).__init__(S, res_mats, name=name) + + self._ideal = ideal + self._shifts = shifts + self._degrees = degrees + self._res_shifts = res_shifts + self._multigrade = multigrade + self._zero_deg = zero_deg + self._name = name + + def _repr_module(self, i): + """ + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r._repr_module(0) + 'S(0)' + sage: r._repr_module(1) + 'S(-2)⊕S(-2)⊕S(-2)' + sage: r._repr_module(2) + 'S(-3)⊕S(-3)' + sage: r._repr_module(3) + '0' + """ + if i > len(self): + m = '0' + else: + if i == 0: + shifts = self._shifts + else: + shifts = self._res_shifts[i - 1] + + if len(shifts) > 0: + for j in range(len(shifts)): + shift = shifts[j] + if j == 0: + m = f'{self._name}' + \ + (f'(-{shift})' if shift != self._zero_deg else '(0)') + else: + m += f'\u2295{self._name}' + \ + (f'(-{shift})' if shift != self._zero_deg else '(0)') + else: + m = '0' + return m + + def shifts(self, i): + """ + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r.shifts(0) + [0] + sage: r.shifts(1) + [2, 2, 2] + sage: r.shifts(2) + [3, 3] + sage: r.shifts(3) + [] + """ + if i < 0: + raise IndexError('invalid index') + elif i == 0: + shifts = self._shifts + elif i > len(self): + shifts = [] + else: + shifts = self._res_shifts[i - 1] + + return shifts + + def betti(self, i, a=None): + """ + Return the `i`-th Betti number in degree `a`. + + INPUT: + + - ``i`` -- nonnegative integer + + - ``a`` -- a degree; if ``None``, return Betti numbers in all degrees + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r.betti(0) + {0: 1} + sage: r.betti(1) + {2: 3} + sage: r.betti(2) + {3: 2} + sage: r.betti(1, 0) + 0 + sage: r.betti(1, 1) + 0 + sage: r.betti(1, 2) + 3 + """ + shifts = self.shifts(i) + + if a is None: + degrees = shifts + else: + degrees = [a] + + betti = {} + for s in degrees: + betti[s] = len([d for d in shifts if d == s]) + + if a is None: + return betti + else: + return betti[a] if a in betti else 0 + + def K_polynomial(self, names=None): + """ + Return the K-polynomial of this resolution. + + INPUT: + + - ``names`` -- a string of names of the variables of the K-polynomial + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: P. = PolynomialRing(QQ) + sage: I = P.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFreeResolution(I) + sage: r.K_polynomial() + 2*t^3 - 3*t^2 + 1 + """ + if self._multigrade: + n = self._degrees[0].degree() + else: + n = 1 + + if names is not None: + L = LaurentPolynomialRing(ZZ, names=names) + else: + L = LaurentPolynomialRing(ZZ, 't', n) + + kpoly = 1 + sign = -1 + for j in range(len(self)): + for v in self._res_shifts[j]: + if self._multigrade: + kpoly += sign * L.monomial(*list(v)) + else: + kpoly += sign * L.monomial(v) + sign = -sign + + return kpoly + + +cdef to_sage_resolution_graded(Resolution res, degrees): + """ + Pull the data from Singular resolution ``res`` to construct a Sage + resolution. + + INPUT: + + - ``res`` -- Singular resolution + + - ``degrees`` -- list of integers or integer vectors + + The procedure is destructive, and ``res`` is not usable afterward. + """ + cdef ring *singular_ring + cdef syStrategy singular_res + cdef poly *p + cdef poly *p_iter + cdef poly *first + cdef poly *previous + cdef poly *acc + cdef resolvente mods + cdef ideal *mod + cdef int i, j, k, idx, rank, nrows, ncols + cdef int ngens = len(degrees) + cdef bint zero_mat + + singular_res = res._resolution[0] + sage_ring = res.base_ring + singular_ring = access_singular_ring(res.base_ring) + + if singular_res.minres != NULL: + mods = singular_res.minres + elif singular_res.fullres != NULL: + mods = singular_res.fullres + else: + raise ValueError('Singular resolution is not usable') + + res_mats = [] + res_degs = [] + + # length is the length of fullres. The length of minres + # can be shorter. Hence we avoid SEGFAULT by stopping + # at NULL pointer. + for idx in range(singular_res.length): + mod = mods[idx] + if mod == NULL: + break + rank = mod.rank + free_module = sage_ring ** rank + + nrows = rank + ncols = mod.ncols # IDELEMS(mod) + + mat = _matrix(sage_ring, nrows, ncols) + matdegs = [] + zero_mat = True + for j in range(ncols): + p = mod.m[j] + degs = [] + # code below copied and modified from to_sage_vector_destructive + # in sage.libs.singular.function.Converter + for i in range(1, rank + 1): + previous = NULL + acc = NULL + first = NULL + p_iter = p + while p_iter != NULL: + if p_GetComp(p_iter, singular_ring) == i: + p_SetComp(p_iter, 0, singular_ring) + p_Setm(p_iter, singular_ring) + if acc == NULL: + first = p_iter + else: + acc.next = p_iter + acc = p_iter + if p_iter == p: + p = pNext(p_iter) + if previous != NULL: + previous.next = pNext(p_iter) + p_iter = pNext(p_iter) + acc.next = NULL + else: + previous = p_iter + p_iter = pNext(p_iter) + + if zero_mat: + zero_mat = first == NULL + + mat[i - 1, j] = new_sage_polynomial(sage_ring, first) + + # degree of a homogeneous polynomial can be computed from the + # first monomial + if first != NULL: + exps = singular_monomial_exponents(first, singular_ring) + deg = 0 + for k in range(ngens): + deg += exps[k] * degrees[k] + degs.append(deg) + else: + degs.append(None) + + matdegs.append(degs) # store degrees of the column + + # Singular sometimes leaves zero matrix in the resolution. We can stop + # when one is seen. + if zero_mat: + break + + res_mats.append(mat) + res_degs.append(matdegs) + + return res_mats, res_degs + + diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 41e9c9ff251..1975ffae5fe 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1,31 +1,6 @@ r""" Interface to Singular -AUTHORS: - -- David Joyner and William Stein (2005): first version - -- Martin Albrecht (2006-03-05): code so singular.[tab] and x = - singular(...), x.[tab] includes all singular commands. - -- Martin Albrecht (2006-03-06): This patch adds the equality symbol to - singular. Also fix a problem in which " " as prompt means comparison - will break all further communication with Singular. - -- Martin Albrecht (2006-03-13): added current_ring() and - current_ring_name() - -- William Stein (2006-04-10): Fixed problems with ideal constructor - -- Martin Albrecht (2006-05-18): added sage_poly. - -- Simon King (2010-11-23): Reduce the overhead caused by waiting for - the Singular prompt by doing garbage collection differently. - -- Simon King (2011-06-06): Make conversion from Singular to Sage more flexible. - -- Simon King (2015): Extend pickling capabilities. - Introduction ------------ @@ -37,7 +12,6 @@ your computer; this should be the case, since Singular is included with Sage. The interface offers three pieces of functionality: - #. ``singular_console()`` - A function that dumps you into an interactive command-line Singular session. @@ -238,10 +212,6 @@ An Important Concept -------------------- -AUTHORS: - -- Neal Harris - The following illustrates an important concept: how Sage interacts with the data being used and returned by Singular. Let's compute a Groebner basis for some ideal, using Singular through Sage. @@ -325,6 +295,34 @@ [Ideal (z) of Multivariate Polynomial Ring in x, z over Number Field in p with defining polynomial p^2 - p - 1] sage: [ J.gens() for J in I.primary_decomposition("gtz")] [[z]] + +AUTHORS: + +- David Joyner and William Stein (2005): first version + +- Neal Harris (unknown): perhaps added "An Important Concept" + +- Martin Albrecht (2006-03-05): code so singular.[tab] and x = + singular(...), x.[tab] includes all singular commands. + +- Martin Albrecht (2006-03-06): This patch adds the equality symbol to + singular. Also fix a problem in which " " as prompt means comparison + will break all further communication with Singular. + +- Martin Albrecht (2006-03-13): added current_ring() and + current_ring_name() + +- William Stein (2006-04-10): Fixed problems with ideal constructor + +- Martin Albrecht (2006-05-18): added sage_poly. + +- Simon King (2010-11-23): Reduce the overhead caused by waiting for + the Singular prompt by doing garbage collection differently. + +- Simon King (2011-06-06): Make conversion from Singular to Sage more flexible. + +- Simon King (2015): Extend pickling capabilities. + """ # **************************************************************************** @@ -1496,8 +1494,6 @@ def __bool__(self): P = self.parent() return P.eval('%s == 0' % self.name()) == '0' - - def sage_polystring(self): r""" If this Singular element is a polynomial, return a string @@ -2069,7 +2065,6 @@ def set_ring(self): """ self.parent().set_ring(self) - def sage_flattened_str_list(self): """ EXAMPLES:: @@ -2596,6 +2591,7 @@ def flush(self): """ sys.stdout.flush() + class SingularGBDefaultContext: """ Within this context all Singular Groebner basis calculations are diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index d5cf0444119..8eeec084bd4 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -1134,8 +1134,16 @@ cdef extern from "singular/kernel/GBEngine/kstd1.h": cdef extern int Kstd1_deg # degBound, default 0 cdef extern int Kstd1_mu # multBound, default 0 +cdef extern from "singular/kernel/ideals.h": + ctypedef ideal * resolvente + cdef extern from "singular/kernel/GBEngine/syz.h": ctypedef struct syStrategy "ssyStrategy": + resolvente fullres; + resolvente minres; + int length; + int regularity; + short list_length; short references cdef extern from "singular/polys/ext_fields/transext.h": diff --git a/src/sage/libs/singular/function.pxd b/src/sage/libs/singular/function.pxd index 232bf198538..503384004d5 100644 --- a/src/sage/libs/singular/function.pxd +++ b/src/sage/libs/singular/function.pxd @@ -19,6 +19,7 @@ from sage.libs.singular.decl cimport leftv, idhdl, syStrategy, matrix, poly, ide from sage.libs.singular.decl cimport ring as singular_ring from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular, MPolynomial_libsingular +cdef new_sage_polynomial(ring, poly *p) cdef poly* access_singular_poly(p) except -1 cdef singular_ring* access_singular_ring(r) except -1 diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 698cc81f2ad..3ecbeb4ff2a 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -7,16 +7,6 @@ or interprocess communication overhead. Users who do not want to call Singular functions directly, usually do not have to worry about this interface, since it is handled by higher level functions in Sage. -AUTHORS: - -- Michael Brickenstein (2009-07): initial implementation, overall design -- Martin Albrecht (2009-07): clean up, enhancements, etc. -- Michael Brickenstein (2009-10): extension to more Singular types -- Martin Albrecht (2010-01): clean up, support for attributes -- Simon King (2011-04): include the documentation provided by Singular as a code block. -- Burcin Erocal, Michael Brickenstein, Oleksandr Motsak, Alexander Dreyer, Simon King - (2011-09) plural support - EXAMPLES: The direct approach for loading a Singular function is to call the @@ -62,6 +52,21 @@ TESTS:: sage: std = singular_function('std') sage: loads(dumps(std)) == std True + +AUTHORS: + +- Michael Brickenstein (2009-07): initial implementation, overall design + +- Martin Albrecht (2009-07): clean up, enhancements, etc + +- Michael Brickenstein (2009-10): extension to more Singular types + +- Martin Albrecht (2010-01): clean up, support for attributes + +- Simon King (2011-04): include the documentation provided by Singular as a code block + +- Burcin Erocal, Michael Brickenstein, Oleksandr Motsak, Alexander Dreyer, Simon King (2011-09): plural support + """ #***************************************************************************** @@ -82,36 +87,29 @@ from sage.cpython.string cimport str_to_bytes, char_to_str from sage.structure.sage_object cimport SageObject from sage.structure.richcmp cimport richcmp - -from sage.rings.integer cimport Integer +from sage.structure.sequence import Sequence, Sequence_generic from sage.modules.free_module_element cimport FreeModuleElement_generic_dense +from sage.rings.integer cimport Integer from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, new_MP from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_libsingular - from sage.rings.polynomial.plural cimport NCPolynomialRing_plural, NCPolynomial_plural, new_NCP from sage.rings.polynomial.multi_polynomial_ideal import NCPolynomialIdeal - from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal - from sage.rings.polynomial.multi_polynomial_ideal_libsingular cimport sage_ideal_to_singular_ideal, singular_ideal_to_sage_sequence +from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, PolynomialSequence_generic from sage.libs.singular.decl cimport * - from sage.libs.singular.option import opt_ctx from sage.libs.singular.polynomial cimport singular_vector_maximal_component, singular_polynomial_check from sage.libs.singular.singular cimport sa2si, si2sa, si2sa_intvec - from sage.libs.singular.singular import error_messages from sage.interfaces.singular import get_docstring from sage.misc.verbose import get_verbose -from sage.structure.sequence import Sequence, Sequence_generic -from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, PolynomialSequence_generic - cdef poly* sage_vector_to_poly(v, ring *r) except -1: """ @@ -156,7 +154,7 @@ cdef class RingWrap: return "" def __dealloc__(self): - if self._ring!=NULL: + if self._ring != NULL: self._ring.ref -= 1 def ngens(self): @@ -292,6 +290,7 @@ cdef class RingWrap: """ rPrint(self._ring) + cdef class Resolution: """ A simple wrapper around Singular's resolutions. @@ -308,9 +307,10 @@ cdef class Resolution: sage: M = syz(I) sage: resolution = mres(M, 0) """ - #FIXME: still not working noncommutative + # FIXME: still not working noncommutative assert is_sage_wrapper_for_singular_ring(base_ring) self.base_ring = base_ring + def __repr__(self): """ EXAMPLES:: @@ -326,6 +326,7 @@ cdef class Resolution: """ return "" + def __dealloc__(self): """ EXAMPLES:: @@ -342,6 +343,7 @@ cdef class Resolution: if self._resolution != NULL: self._resolution.references -= 1 + cdef leftv* new_leftv(void *data, res_type): """ INPUT: @@ -487,6 +489,7 @@ def all_vectors(s): return False return True + cdef class Converter(SageObject): """ A :class:`Converter` interfaces between Sage objects and Singular @@ -686,8 +689,6 @@ cdef class Converter(SageObject): Convert singular matrix to matrix over the polynomial ring. """ from sage.matrix.constructor import Matrix - #cdef ring *singular_ring = (\ - # self._sage_ring)._ring ncols = mat.ncols nrows = mat.nrows result = Matrix(self._sage_ring, nrows, ncols) @@ -699,7 +700,6 @@ cdef class Converter(SageObject): return result cdef to_sage_vector_destructive(self, poly *p, free_module = None): - #cdef ring *r=self._ring._ring cdef int rank if free_module: rank = free_module.rank() @@ -716,20 +716,20 @@ cdef class Converter(SageObject): previous = NULL acc = NULL first = NULL - p_iter=p + p_iter = p while p_iter != NULL: if p_GetComp(p_iter, self._singular_ring) == i: - p_SetComp(p_iter,0, self._singular_ring) + p_SetComp(p_iter, 0, self._singular_ring) p_Setm(p_iter, self._singular_ring) if acc == NULL: first = p_iter else: acc.next = p_iter acc = p_iter - if p_iter==p: - p=pNext(p_iter) + if p_iter == p: + p = pNext(p_iter) if previous != NULL: - previous.next=pNext(p_iter) + previous.next = pNext(p_iter) p_iter = pNext(p_iter) acc.next = NULL else: @@ -750,7 +750,6 @@ cdef class Converter(SageObject): - ``r`` -- a SINGULAR ring - ``sage_ring`` -- a Sage ring matching r """ - #cdef MPolynomialRing_libsingular sage_ring = self._ring cdef int j cdef int rank=i.rank free_module = self._sage_ring ** rank @@ -758,12 +757,11 @@ cdef class Converter(SageObject): for j from 0 <= j < IDELEMS(i): p = self.to_sage_vector_destructive(i.m[j], free_module) - i.m[j]=NULL#save it from getting freed + i.m[j]=NULL # save it from getting freed l.append( p ) return Sequence(l, check=False, immutable=True) - cdef to_sage_integer_matrix(self, intvec* mat): """ Convert Singular matrix to matrix over the polynomial ring. @@ -780,7 +778,6 @@ cdef class Converter(SageObject): result[i,j] = mat.get(i*ncols+j) return result - cdef leftv *append_polynomial(self, p) except NULL: """ Append the polynomial ``p`` to the list. @@ -808,8 +805,6 @@ cdef class Converter(SageObject): cdef ideal *i cdef int j = 0 - - i = idInit(len(m),rank) for f in m: i.m[j] = sage_vector_to_poly(f, r) @@ -832,7 +827,6 @@ cdef class Converter(SageObject): return self._append(_r, RING_CMD) cdef leftv *append_matrix(self, mat) except NULL: - sage_ring = mat.base_ring() cdef ring *r= access_singular_ring(sage_ring) @@ -854,8 +848,6 @@ cdef class Converter(SageObject): cdef long _n = n return self._append(_n, INT_CMD) - - cdef leftv *append_list(self, l) except NULL: """ Append the list ``l`` to the list. @@ -948,31 +940,26 @@ cdef class Converter(SageObject): sage: sing_genus(I) # known bug -2 """ - #FIXME + # FIXME cdef MPolynomial_libsingular res_poly cdef int rtyp = to_convert.rtyp cdef lists *singular_list cdef Resolution res_resolution + if rtyp == IDEAL_CMD: return singular_ideal_to_sage_sequence(to_convert.data, self._singular_ring, self._sage_ring) - elif rtyp == POLY_CMD: - #FIXME + # FIXME res_poly = MPolynomial_libsingular(self._sage_ring) res_poly._poly = to_convert.data - to_convert.data = NULL - #prevent it getting free, when cleaning the leftv + to_convert.data = NULL # prevent it getting free, when cleaning the leftv return res_poly - elif rtyp == INT_CMD: return to_convert.data - elif rtyp == NUMBER_CMD: return si2sa(to_convert.data, self._singular_ring, self._sage_ring.base_ring()) - elif rtyp == INTVEC_CMD: - return si2sa_intvec(to_convert.data) - + return si2sa_intvec( to_convert.data) elif rtyp == STRING_CMD: # TODO: Need to determine what kind of data can be returned by a # STRING_CMD--is it just ASCII strings or can it be an arbitrary @@ -980,38 +967,26 @@ cdef class Converter(SageObject): ret = char_to_str(to_convert.data) return ret elif rtyp == VECTOR_CMD: - result = self.to_sage_vector_destructive( - to_convert.data) + result = self.to_sage_vector_destructive( to_convert.data) to_convert.data = NULL return result - - elif rtyp == RING_CMD or rtyp==QRING_CMD: return new_RingWrap( to_convert.data ) - elif rtyp == MATRIX_CMD: - return self.to_sage_matrix( to_convert.data ) - + return self.to_sage_matrix( to_convert.data ) elif rtyp == LIST_CMD: singular_list = to_convert.data ret = [] for i in xrange(singular_list.nr+1): - ret.append( - self.to_python( - &(singular_list.m[i]))) + ret.append(self.to_python(&(singular_list.m[i]))) return ret - - elif rtyp == MODUL_CMD: - return self.to_sage_module_element_sequence_destructive( - to_convert.data - ) + return self.to_sage_module_element_sequence_destructive( to_convert.data) elif rtyp == INTMAT_CMD: - return self.to_sage_integer_matrix( - to_convert.data) + return self.to_sage_integer_matrix( to_convert.data) elif rtyp == RESOLUTION_CMD: res_resolution = Resolution(self._sage_ring) - res_resolution._resolution = to_convert.data + res_resolution._resolution = to_convert.data res_resolution._resolution.references += 1 return res_resolution elif rtyp == NONE: @@ -1019,6 +994,7 @@ cdef class Converter(SageObject): else: raise NotImplementedError("rtyp %d not implemented."%(rtyp)) + cdef class BaseCallHandler: """ A call handler is an abstraction which hides the details of the @@ -1036,6 +1012,7 @@ cdef class BaseCallHandler: """ return False + cdef class LibraryCallHandler(BaseCallHandler): """ A call handler is an abstraction which hides the details of the @@ -1077,6 +1054,7 @@ cdef class LibraryCallHandler(BaseCallHandler): """ return False + cdef class KernelCallHandler(BaseCallHandler): """ A call handler is an abstraction which hides the details of the @@ -1163,9 +1141,11 @@ cdef class KernelCallHandler(BaseCallHandler): """ return True + # The Sage ring used as a dummy for singular function calls. cdef object dummy_ring + cdef class SingularFunction(SageObject): """ The base class for Singular functions either from the kernel or @@ -1540,6 +1520,7 @@ cdef inline call_function(SingularFunction self, tuple args, object R, bint sign return res + cdef class SingularLibraryFunction(SingularFunction): """ EXAMPLES:: @@ -1582,6 +1563,7 @@ cdef class SingularLibraryFunction(SingularFunction): cdef idhdl* singular_idhdl = ggetid(str_to_bytes(self._name)) return singular_idhdl!=NULL + cdef class SingularKernelFunction(SingularFunction): """ EXAMPLES:: @@ -1848,7 +1830,6 @@ def lib(name): if failure: raise NameError("Singular library {!r} not found".format(name)) - def list_of_functions(packages=False): """ Return a list of all function names currently available. @@ -1879,7 +1860,6 @@ def list_of_functions(packages=False): h = IDNEXT(h) return l - cdef inline RingWrap new_RingWrap(ring* r): cdef RingWrap ring_wrap_result = RingWrap.__new__(RingWrap) ring_wrap_result._ring = r @@ -1887,7 +1867,6 @@ cdef inline RingWrap new_RingWrap(ring* r): return ring_wrap_result - # Add support for _instancedoc_ from sage.misc.instancedoc import instancedoc instancedoc(SingularFunction) diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index fc6d8113d61..e012da4573c 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -25,7 +25,6 @@ import re plusminus_pattern = re.compile("([^\(^])([\+\-])") parenthvar_pattern = re.compile(r"\(([a-zA-Z][a-zA-Z0-9]*)\)") - from sage.cpython.string cimport bytes_to_str, str_to_bytes from sage.libs.singular.decl cimport number, ideal @@ -38,7 +37,6 @@ from sage.libs.singular.decl cimport p_GetComp, p_SetComp from sage.libs.singular.decl cimport pSubst from sage.libs.singular.decl cimport p_Normalize - from sage.libs.singular.singular cimport sa2si, si2sa, overflow_check from sage.misc.latex import latex @@ -80,7 +78,6 @@ cdef int singular_polynomial_add(poly **ret, poly *p, poly *q, ring *r): ret[0] = p_Add_q(p, q, r) return 0 - cdef int singular_polynomial_sub(poly **ret, poly *p, poly *q, ring *r): """ ``ret[0] = p-q`` where ``p`` and ``p`` in ``r``. @@ -108,7 +105,6 @@ cdef int singular_polynomial_sub(poly **ret, poly *p, poly *q, ring *r): ret[0] = p_Add_q(p, p_Neg(q, r), r) return 0 - cdef int singular_polynomial_rmul(poly **ret, poly *p, RingElement n, ring *r): """ ``ret[0] = n*p`` where ``n`` is a coefficient and ``p`` in ``r``. @@ -319,7 +315,6 @@ cdef int singular_polynomial_mul(poly** ret, poly *p, poly *q, ring *r) except - ret[0] = pp_Mult_qq(p, q, r) return 0 - cdef int singular_polynomial_div_coeff(poly** ret, poly *p, poly *q, ring *r) except -1: """ ``ret[0] = p/n`` where ``p`` and ``q`` in ``r`` and ``q`` constant. @@ -422,7 +417,6 @@ cdef int singular_polynomial_neg(poly **ret, poly *p, ring *r): ret[0] = p_Neg(p_Copy(p,r),r) return 0 - cdef object singular_polynomial_str(poly *p, ring *r): """ Return the string representation of ``p``. @@ -448,7 +442,6 @@ cdef object singular_polynomial_str(poly *p, ring *r): s = parenthvar_pattern.sub("\\1", s) return s - cdef object singular_polynomial_latex(poly *p, ring *r, object base, object latex_gens): r""" Return the LaTeX string representation of ``p``. diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index f57126a6be4..63cb6a9e19e 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -421,7 +421,6 @@ cdef object si2sa_transext_QQ(number *n, ring *_ring, object base): return snumer/sdenom - cdef object si2sa_transext_FF(number *n, ring *_ring, object base): """ Create a sage element of a transcendental extension of a prime field from a @@ -505,7 +504,6 @@ cdef object si2sa_transext_FF(number *n, ring *_ring, object base): return snumer/sdenom - cdef object si2sa_NF(number *n, ring *_ring, object base): """ Create a sage element of a number field from a singular one. @@ -1018,8 +1016,6 @@ cdef number *sa2si_transext_QQ(object elem, ring *_ring): return n1 - - cdef number *sa2si_transext_FF(object elem, ring *_ring): """ Create a singular number from a sage element of a transcendental extension @@ -1123,7 +1119,6 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring): return n1 - cdef number *sa2si_NF(object elem, ring *_ring): """ Create a singular number from a sage element of a number field. @@ -1430,7 +1425,6 @@ cdef number *sa2si(Element elem, ring * _ring): raise ValueError("cannot convert to SINGULAR number") - cdef object si2sa_intvec(intvec *v): r""" create a sage tuple from a singular vector of integers @@ -1495,7 +1489,6 @@ cdef int overflow_check(unsigned long e, ring *_ring) except -1: if unlikely(e > _ring.bitmask): raise OverflowError("exponent overflow (%d)"%(e)) - cdef init_libsingular(): """ This initializes the SINGULAR library. This is a hack to some