passagemath-standard-no-symbolics 10.6.31rc3__cp314-cp314-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-standard-no-symbolics might be problematic. Click here for more details.
- passagemath_standard_no_symbolics-10.6.31rc3.data/scripts/sage-grep +5 -0
- passagemath_standard_no_symbolics-10.6.31rc3.data/scripts/sage-grepdoc +5 -0
- passagemath_standard_no_symbolics-10.6.31rc3.data/scripts/sage-list-packages +103 -0
- passagemath_standard_no_symbolics-10.6.31rc3.dist-info/METADATA +151 -0
- passagemath_standard_no_symbolics-10.6.31rc3.dist-info/RECORD +82 -0
- passagemath_standard_no_symbolics-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_standard_no_symbolics-10.6.31rc3.dist-info/top_level.txt +1 -0
- sage/all.py +207 -0
- sage/all_cmdline.py +36 -0
- sage/cli/__init__.py +61 -0
- sage/cli/__main__.py +5 -0
- sage/cli/eval_cmd.py +45 -0
- sage/cli/eval_cmd_test.py +25 -0
- sage/cli/interactive_shell_cmd.py +28 -0
- sage/cli/notebook_cmd.py +51 -0
- sage/cli/notebook_cmd_test.py +39 -0
- sage/cli/options.py +26 -0
- sage/cli/run_file_cmd.py +50 -0
- sage/cli/version_cmd.py +26 -0
- sage/databases/all.py +83 -0
- sage/databases/cubic_hecke_db.py +1527 -0
- sage/dynamics/all.py +31 -0
- sage/dynamics/surface_dynamics_deprecation.py +32 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/mwrank/PRIMES +1 -0
- sage/ext_data/nbconvert/postprocess.py +48 -0
- sage/ext_data/nbconvert/rst_sage.tpl +99 -0
- sage/ext_data/nodoctest +0 -0
- sage/ext_data/notebook-ipython/kernel.json.in +11 -0
- sage/ext_data/notebook-ipython/logo-64x64.png +0 -0
- sage/ext_data/notebook-ipython/logo.svg +352 -0
- sage/ext_data/valgrind/pyalloc.supp +58 -0
- sage/ext_data/valgrind/sage-additional.supp +417 -0
- sage/ext_data/valgrind/sage.supp +43 -0
- sage/ext_data/valgrind/valgrind-python.supp +480 -0
- sage/geometry/all.py +12 -0
- sage/groups/matrix_gps/pickling_overrides.py +110 -0
- sage/homology/tests.py +66 -0
- sage/interacts/algebra.py +20 -0
- sage/interacts/all.py +25 -0
- sage/interacts/calculus.py +24 -0
- sage/interacts/fractals.py +18 -0
- sage/interacts/geometry.py +19 -0
- sage/interacts/library.py +1950 -0
- sage/interacts/library_cython.cpython-314-darwin.so +0 -0
- sage/interacts/statistics.py +19 -0
- sage/interfaces/axiom.py +1002 -0
- sage/interfaces/kash.py +834 -0
- sage/interfaces/lie.py +950 -0
- sage/interfaces/matlab.py +413 -0
- sage/interfaces/mupad.py +686 -0
- sage/interfaces/octave.py +858 -0
- sage/interfaces/phc.py +943 -0
- sage/interfaces/psage.py +189 -0
- sage/interfaces/qsieve.py +4 -0
- sage/interfaces/r.py +2096 -0
- sage/interfaces/read_data.py +46 -0
- sage/interfaces/scilab.py +576 -0
- sage/interfaces/tests.py +81 -0
- sage/libs/all.py +11 -0
- sage/libs/cremona/__init__.py +0 -0
- sage/libs/mwrank/__init__.py +0 -0
- sage/logic/all.py +3 -0
- sage/logic/booleval.py +160 -0
- sage/logic/boolformula.py +1490 -0
- sage/logic/logic.py +856 -0
- sage/logic/logicparser.py +696 -0
- sage/logic/logictable.py +272 -0
- sage/logic/propcalc.py +311 -0
- sage/misc/all.py +28 -0
- sage/misc/lazy_attribute.pyi +11 -0
- sage/rings/all.py +48 -0
- sage/rings/commutative_algebra.py +38 -0
- sage/rings/finite_rings/all.py +21 -0
- sage/rings/numbers_abc.py +58 -0
- sage/rings/polynomial/all.py +22 -0
- sage/rings/polynomial/convolution.py +421 -0
- sage/symbolic/all__sagemath_standard_no_symbolics.py +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract base class for commutative algebras
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
#*****************************************************************************
|
|
6
|
+
# Copyright (C) 2005 William Stein <wstein@gmail.com>
|
|
7
|
+
#
|
|
8
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
9
|
+
#
|
|
10
|
+
# This code is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13
|
+
# General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# The full text of the GPL is available at:
|
|
16
|
+
#
|
|
17
|
+
# https://www.gnu.org/licenses/
|
|
18
|
+
#*****************************************************************************
|
|
19
|
+
|
|
20
|
+
from sage.categories.commutative_algebras import CommutativeAlgebras
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def is_CommutativeAlgebra(x):
|
|
24
|
+
"""
|
|
25
|
+
Check to see if ``x`` is in the category of ``CommutativeAlgebras``.
|
|
26
|
+
|
|
27
|
+
EXAMPLES::
|
|
28
|
+
|
|
29
|
+
sage: from sage.rings.commutative_algebra import is_CommutativeAlgebra
|
|
30
|
+
sage: is_CommutativeAlgebra(QQ['x'])
|
|
31
|
+
doctest:warning...
|
|
32
|
+
DeprecationWarning: the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead
|
|
33
|
+
See https://github.com/sagemath/sage/issues/35999 for details.
|
|
34
|
+
True
|
|
35
|
+
"""
|
|
36
|
+
from sage.misc.superseded import deprecation
|
|
37
|
+
deprecation(35999, "the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead")
|
|
38
|
+
return x in CommutativeAlgebras(x.base_ring())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Finite Fields
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# *****************************************************************************
|
|
6
|
+
# Copyright (C) 2010 David Roe <roed@math.harvard.edu>
|
|
7
|
+
# William Stein <wstein@gmail.com>
|
|
8
|
+
#
|
|
9
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
10
|
+
#
|
|
11
|
+
# This code is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
14
|
+
# General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# The full text of the GPL is available at:
|
|
17
|
+
#
|
|
18
|
+
# https://www.gnu.org/licenses/
|
|
19
|
+
# *****************************************************************************
|
|
20
|
+
|
|
21
|
+
from sage.rings.finite_rings.all__sagemath_categories import *
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Support Python's numbers abstract base class
|
|
3
|
+
|
|
4
|
+
.. SEEALSO:: :pep:`3141` for more information about :class:`numbers`.
|
|
5
|
+
|
|
6
|
+
TESTS::
|
|
7
|
+
|
|
8
|
+
sage: import numbers
|
|
9
|
+
sage: isinstance(5, numbers.Integral)
|
|
10
|
+
True
|
|
11
|
+
sage: isinstance(5, numbers.Number)
|
|
12
|
+
True
|
|
13
|
+
sage: isinstance(5/1, numbers.Integral)
|
|
14
|
+
False
|
|
15
|
+
sage: isinstance(22/7, numbers.Rational)
|
|
16
|
+
True
|
|
17
|
+
sage: isinstance(1.3, numbers.Real)
|
|
18
|
+
True
|
|
19
|
+
sage: isinstance(CC(1.3), numbers.Real)
|
|
20
|
+
False
|
|
21
|
+
sage: isinstance(CC(1.3 + I), numbers.Complex)
|
|
22
|
+
True
|
|
23
|
+
sage: isinstance(RDF(1.3), numbers.Real)
|
|
24
|
+
True
|
|
25
|
+
sage: isinstance(CDF(1.3, 4), numbers.Complex)
|
|
26
|
+
True
|
|
27
|
+
sage: isinstance(AA(sqrt(2)), numbers.Real) # needs sage.rings.number_field sage.symbolic
|
|
28
|
+
True
|
|
29
|
+
sage: isinstance(QQbar(I), numbers.Complex) # needs sage.rings.number_field
|
|
30
|
+
True
|
|
31
|
+
|
|
32
|
+
This doesn't work with symbolic expressions at all::
|
|
33
|
+
|
|
34
|
+
sage: isinstance(pi, numbers.Real) # needs sage.symbolic
|
|
35
|
+
False
|
|
36
|
+
sage: isinstance(I, numbers.Complex) # needs sage.rings.number_field
|
|
37
|
+
False
|
|
38
|
+
sage: isinstance(sqrt(2), numbers.Real) # needs sage.rings.number_field sage.symbolic
|
|
39
|
+
False
|
|
40
|
+
|
|
41
|
+
Because we do this, NumPy's ``isscalar()`` recognizes Sage types::
|
|
42
|
+
|
|
43
|
+
sage: from numpy import isscalar # needs numpy
|
|
44
|
+
sage: isscalar(3.141) # needs numpy
|
|
45
|
+
True
|
|
46
|
+
sage: isscalar(4/17) # needs numpy
|
|
47
|
+
True
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
#*****************************************************************************
|
|
51
|
+
# Copyright (C) 2015 Jeroen Demeyer <jdemeyer@cage.ugent.be>
|
|
52
|
+
#
|
|
53
|
+
# This program is free software: you can redistribute it and/or modify
|
|
54
|
+
# it under the terms of the GNU General Public License as published by
|
|
55
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
56
|
+
# (at your option) any later version.
|
|
57
|
+
# http://www.gnu.org/licenses/
|
|
58
|
+
#*****************************************************************************
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Polynomials
|
|
3
|
+
"""
|
|
4
|
+
# ****************************************************************************
|
|
5
|
+
# Copyright (C) 2005 William Stein <wstein@gmail.com>
|
|
6
|
+
#
|
|
7
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
8
|
+
#
|
|
9
|
+
# This code is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+
# General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# The full text of the GPL is available at:
|
|
15
|
+
#
|
|
16
|
+
# https://www.gnu.org/licenses/
|
|
17
|
+
# ****************************************************************************
|
|
18
|
+
|
|
19
|
+
from sage.rings.polynomial.all__sagemath_polyhedra import *
|
|
20
|
+
|
|
21
|
+
# Generic convolution
|
|
22
|
+
from sage.rings.polynomial.convolution import convolution
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Generic Convolution
|
|
3
|
+
|
|
4
|
+
Asymptotically fast convolution of lists over any commutative ring
|
|
5
|
+
in which the multiply-by-two map is injective. (More precisely, if
|
|
6
|
+
`x \in R`, and `x = 2^k*y` for some `k \geq 0`, we require that
|
|
7
|
+
`R(x/2^k)` returns `y`.)
|
|
8
|
+
|
|
9
|
+
The main function to be exported is :func:`convolution`.
|
|
10
|
+
|
|
11
|
+
EXAMPLES::
|
|
12
|
+
|
|
13
|
+
sage: convolution([1, 2, 3, 4, 5], [6, 7])
|
|
14
|
+
[6, 19, 32, 45, 58, 35]
|
|
15
|
+
|
|
16
|
+
The convolution function is reasonably fast, even though it is written
|
|
17
|
+
in pure Python. For example, the following takes less than a second::
|
|
18
|
+
|
|
19
|
+
sage: v = convolution(list(range(1000)), list(range(1000)))
|
|
20
|
+
|
|
21
|
+
ALGORITHM:
|
|
22
|
+
|
|
23
|
+
Converts the problem to multiplication in the ring
|
|
24
|
+
`S[x]/(x^M - 1)`, where `S = R[y]/(y^K + 1)` (where
|
|
25
|
+
`R` is the original base ring). Performs FFT with respect
|
|
26
|
+
to the roots of unity `1, y, y^2, \ldots, y^{2K-1}` in
|
|
27
|
+
`S`. The FFT/IFFT are accomplished with just additions and
|
|
28
|
+
subtractions and rotating python lists. (I think this algorithm is
|
|
29
|
+
essentially due to Schonhage, not completely sure.) The pointwise
|
|
30
|
+
multiplications are handled recursively, switching to a classical
|
|
31
|
+
algorithm at some point.
|
|
32
|
+
|
|
33
|
+
Complexity is O(n log(n) log(log(n))) additions/subtractions in R
|
|
34
|
+
and O(n log(n)) multiplications in R.
|
|
35
|
+
|
|
36
|
+
AUTHORS:
|
|
37
|
+
|
|
38
|
+
- David Harvey (2007-07): first implementation
|
|
39
|
+
|
|
40
|
+
- William Stein: editing the docstrings for inclusion in Sage.
|
|
41
|
+
"""
|
|
42
|
+
# ****************************************************************************
|
|
43
|
+
# Copyright (C) 2007 William Stein <wstein@gmail.com>
|
|
44
|
+
# David Harvey <dmharvey@math.harvard.edu>
|
|
45
|
+
#
|
|
46
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
47
|
+
#
|
|
48
|
+
# https://www.gnu.org/licenses/
|
|
49
|
+
# ****************************************************************************
|
|
50
|
+
from sage.structure.all import parent
|
|
51
|
+
from math import log, ceil
|
|
52
|
+
|
|
53
|
+
# -------------------------------------------------------------------
|
|
54
|
+
# main exported routine
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def convolution(L1, L2):
|
|
58
|
+
"""
|
|
59
|
+
Return convolution of non-empty lists L1 and L2.
|
|
60
|
+
|
|
61
|
+
L1 and L2 may have arbitrary lengths.
|
|
62
|
+
|
|
63
|
+
EXAMPLES::
|
|
64
|
+
|
|
65
|
+
sage: convolution([1, 2, 3], [4, 5, 6, 7])
|
|
66
|
+
[4, 13, 28, 34, 32, 21]
|
|
67
|
+
|
|
68
|
+
TESTS::
|
|
69
|
+
|
|
70
|
+
sage: R = Integers(47)
|
|
71
|
+
sage: L1 = [R.random_element() for _ in range(1000)]
|
|
72
|
+
sage: L2 = [R.random_element() for _ in range(3756)]
|
|
73
|
+
sage: L3 = convolution(L1, L2)
|
|
74
|
+
sage: L3[2000] == sum([L1[i] * L2[2000-i] for i in range(1000)])
|
|
75
|
+
True
|
|
76
|
+
sage: len(L3) == 1000 + 3756 - 1
|
|
77
|
+
True
|
|
78
|
+
"""
|
|
79
|
+
if not L1 or not L2:
|
|
80
|
+
raise ValueError("cannot compute convolution of empty lists")
|
|
81
|
+
if len(L1) <= 100 and len(L2) <= 100: # very arbitrary cutoff
|
|
82
|
+
return _convolution_naive(L1, L2)
|
|
83
|
+
return _convolution_fft(L1, L2)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# -------------------------------------------------------------------
|
|
87
|
+
# naive convolution and negacyclic convolutions
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _convolution_naive(L1, L2):
|
|
91
|
+
"""
|
|
92
|
+
Return convolution of non-empty lists L1 and L2, using naive algorithm.
|
|
93
|
+
|
|
94
|
+
L1 and L2 may have arbitrary lengths.
|
|
95
|
+
|
|
96
|
+
EXAMPLES::
|
|
97
|
+
|
|
98
|
+
sage: from sage.rings.polynomial.convolution import _convolution_naive
|
|
99
|
+
sage: _convolution_naive([2], [3])
|
|
100
|
+
[6]
|
|
101
|
+
sage: _convolution_naive([2, 5], [3])
|
|
102
|
+
[6, 15]
|
|
103
|
+
sage: _convolution_naive([2], [3, 6])
|
|
104
|
+
[6, 12]
|
|
105
|
+
sage: _convolution_naive([1, 2, 3], [4, 5, 6, 7])
|
|
106
|
+
[4, 13, 28, 34, 32, 21]
|
|
107
|
+
sage: _convolution_naive([4, 5, 6, 7], [1, 2, 3])
|
|
108
|
+
[4, 13, 28, 34, 32, 21]
|
|
109
|
+
"""
|
|
110
|
+
assert len(L1) and len(L2)
|
|
111
|
+
|
|
112
|
+
m1 = len(L1)
|
|
113
|
+
m2 = len(L2)
|
|
114
|
+
|
|
115
|
+
return [sum([L1[i] * L2[k - i]
|
|
116
|
+
for i in range(max(0, k - m2 + 1), min(k + 1, m1))])
|
|
117
|
+
for k in range(m1 + m2 - 1)]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _negaconvolution_naive(L1, L2):
|
|
121
|
+
"""
|
|
122
|
+
Negacyclic convolution of L1 and L2, using naive algorithm.
|
|
123
|
+
|
|
124
|
+
L1 and L2 must be the same length.
|
|
125
|
+
|
|
126
|
+
EXAMPLES::
|
|
127
|
+
|
|
128
|
+
sage: from sage.rings.polynomial.convolution import _negaconvolution_naive
|
|
129
|
+
sage: from sage.rings.polynomial.convolution import _convolution_naive
|
|
130
|
+
sage: _negaconvolution_naive([2], [3])
|
|
131
|
+
[6]
|
|
132
|
+
sage: _convolution_naive([1, 2, 3], [3, 4, 5])
|
|
133
|
+
[3, 10, 22, 22, 15]
|
|
134
|
+
sage: _negaconvolution_naive([1, 2, 3], [3, 4, 5])
|
|
135
|
+
[-19, -5, 22]
|
|
136
|
+
"""
|
|
137
|
+
assert len(L1)
|
|
138
|
+
assert len(L1) == len(L2)
|
|
139
|
+
|
|
140
|
+
N = len(L1)
|
|
141
|
+
return [sum([L1[i] * L2[j - i] for i in range(j + 1)]) -
|
|
142
|
+
sum([L1[i] * L2[N + j - i]
|
|
143
|
+
for i in range(j + 1, N)]) for j in range(N)]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# -------------------------------------------------------------------
|
|
147
|
+
# FFT/IFFT routines
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _forward_butterfly(L1, L2, r):
|
|
151
|
+
r"""
|
|
152
|
+
L1 and L2 are both lists of length K, and
|
|
153
|
+
`0 \leq r \leq K`. They represent polynomials in
|
|
154
|
+
`S = R[y]/(y^K + 1)`. This function returns
|
|
155
|
+
`(L_1 + y^r L_2, L_1 - y^r L_2)`, as a list.
|
|
156
|
+
"""
|
|
157
|
+
assert len(L1) == len(L2)
|
|
158
|
+
assert 0 <= r <= len(L1)
|
|
159
|
+
|
|
160
|
+
K = len(L1)
|
|
161
|
+
return [L1[i] - L2[i + K - r] for i in range(r)] + \
|
|
162
|
+
[L1[i] + L2[i - r] for i in range(r, K)], \
|
|
163
|
+
[L1[i] + L2[i + K - r] for i in range(r)] + \
|
|
164
|
+
[L1[i] - L2[i - r] for i in range(r, K)]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _inverse_butterfly(L1, L2, r):
|
|
168
|
+
r"""
|
|
169
|
+
L1 and L2 are both lists of length `K`, and
|
|
170
|
+
`0 \leq r \leq K`. They represent polynomials in
|
|
171
|
+
`S = R[y]/(y^K + 1)`. This function returns
|
|
172
|
+
`(L_1 + L_2, y^{-r}*(L_1 - L_2))`, as a list.
|
|
173
|
+
"""
|
|
174
|
+
assert len(L1) == len(L2)
|
|
175
|
+
assert 0 <= r <= len(L1)
|
|
176
|
+
|
|
177
|
+
K = len(L1)
|
|
178
|
+
return [L1[i] + L2[i] for i in range(K)], \
|
|
179
|
+
[L1[i] - L2[i] for i in range(r, K)] + \
|
|
180
|
+
[L2[i] - L1[i] for i in range(r)]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _fft(L, K, start, depth, root):
|
|
184
|
+
r"""
|
|
185
|
+
L is a list of length `M = 2^m`, each entry a list of
|
|
186
|
+
length `K = 2^k`.
|
|
187
|
+
|
|
188
|
+
This function only operates on the [start, start + D) portion of L,
|
|
189
|
+
where `D = 2^\text{depth}`. This portion is interpreted as
|
|
190
|
+
a polynomial in `S[x]/(x^D - y^(2*root))`, where
|
|
191
|
+
`S = R[y]/(y^K + 1)`.
|
|
192
|
+
|
|
193
|
+
This function performs an inplace FFT, i.e. evaluates the
|
|
194
|
+
polynomial at x = each `D`-th root of unity in S (namely the powers
|
|
195
|
+
of `y^{2K/D}`), with results in bit-reversed order.
|
|
196
|
+
"""
|
|
197
|
+
half = 1 << (depth - 1)
|
|
198
|
+
start2 = start + half
|
|
199
|
+
|
|
200
|
+
# reduce mod (x^(D/2) - y^root) and mod (x^(D/2) + y^root)
|
|
201
|
+
for i in range(half):
|
|
202
|
+
L[start + i], L[start2 + i] = \
|
|
203
|
+
_forward_butterfly(L[start + i], L[start2 + i], root)
|
|
204
|
+
|
|
205
|
+
# recurse into each half
|
|
206
|
+
if depth >= 2:
|
|
207
|
+
_fft(L, K, start, depth - 1, root >> 1)
|
|
208
|
+
_fft(L, K, start2, depth - 1, (root + K) >> 1)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _ifft(L, K, start, depth, root):
|
|
212
|
+
r"""
|
|
213
|
+
Inverse operation of ``_fft_trunc()`` (except that
|
|
214
|
+
result is a factor of ``2^depth`` too big)
|
|
215
|
+
"""
|
|
216
|
+
half = 1 << (depth - 1)
|
|
217
|
+
start2 = start + half
|
|
218
|
+
|
|
219
|
+
# recurse into each half
|
|
220
|
+
if depth >= 2:
|
|
221
|
+
_ifft(L, K, start, depth - 1, root >> 1)
|
|
222
|
+
_ifft(L, K, start2, depth - 1, (root + K) >> 1)
|
|
223
|
+
|
|
224
|
+
# CRT together (x^(D/2) - y^root) and mod (x^(D/2) + y^root)
|
|
225
|
+
for i in range(half):
|
|
226
|
+
L[start + i], L[start2 + i] = \
|
|
227
|
+
_inverse_butterfly(L[start + i], L[start2 + i], root)
|
|
228
|
+
|
|
229
|
+
# -------------------------------------------------------------------
|
|
230
|
+
# splitting and recombining routines
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _split(L, m, k):
|
|
234
|
+
"""
|
|
235
|
+
Assumes L is a list of length `2^{m+k-1}`. Splits it into
|
|
236
|
+
`2^m` lists of length `2^{k-1}`, returned as a list
|
|
237
|
+
of lists. Each list is zero padded up to length `2^k`.
|
|
238
|
+
|
|
239
|
+
EXAMPLES::
|
|
240
|
+
|
|
241
|
+
sage: from sage.rings.polynomial.convolution import _split
|
|
242
|
+
sage: _split([1, 2, 3, 4, 5, 6, 7, 8], 2, 2)
|
|
243
|
+
[[1, 2, 0, 0], [3, 4, 0, 0], [5, 6, 0, 0], [7, 8, 0, 0]]
|
|
244
|
+
sage: _split([1, 2, 3, 4, 5, 6, 7, 8], 1, 3)
|
|
245
|
+
[[1, 2, 3, 4, 0, 0, 0, 0], [5, 6, 7, 8, 0, 0, 0, 0]]
|
|
246
|
+
sage: _split([1, 2, 3, 4, 5, 6, 7, 8], 3, 1)
|
|
247
|
+
[[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0]]
|
|
248
|
+
"""
|
|
249
|
+
K = 1 << (k - 1)
|
|
250
|
+
zero = parent(L[0])(0)
|
|
251
|
+
zeroes = [zero] * K
|
|
252
|
+
return [[L[i + j] for j in range(K)] + zeroes for i in range(0, K << m, K)]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _combine(L, m, k):
|
|
256
|
+
r"""
|
|
257
|
+
Assumes L is a list of length `2^m`, each entry a list of
|
|
258
|
+
length `2^k`. Combines together into a single list,
|
|
259
|
+
effectively inverting ``_split()``, but overlaying
|
|
260
|
+
coefficients, i.e. list #i gets added in starting at position
|
|
261
|
+
`2^{k-1} i`. Note that the second half of the last list is
|
|
262
|
+
ignored.
|
|
263
|
+
"""
|
|
264
|
+
M = 1 << m
|
|
265
|
+
half_K = 1 << (k - 1)
|
|
266
|
+
return [L[0][j] for j in range(half_K)] + \
|
|
267
|
+
[L[i + 1][j] + L[i][j + half_K]
|
|
268
|
+
for i in range(M - 1) for j in range(half_K)]
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _nega_combine(L, m, k):
|
|
272
|
+
r"""
|
|
273
|
+
Same as ``_combine()``, but doesn't ignore the second
|
|
274
|
+
half of the last list; instead it makes that piece wrap around
|
|
275
|
+
negacyclically.
|
|
276
|
+
"""
|
|
277
|
+
M = 1 << m
|
|
278
|
+
half_K = 1 << (k - 1)
|
|
279
|
+
return [L[0][j] - L[M - 1][j + half_K] for j in range(half_K)] + \
|
|
280
|
+
[L[i + 1][j] + L[i][j + half_K]
|
|
281
|
+
for i in range(M - 1) for j in range(half_K)]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# -------------------------------------------------------------------
|
|
285
|
+
# FFT-based convolution and negacyclic convolutions
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _negaconvolution(L1, L2, n):
|
|
289
|
+
"""
|
|
290
|
+
Negacyclic convolution of L1 and L2.
|
|
291
|
+
|
|
292
|
+
L1 and L2 must both have length `2^n`.
|
|
293
|
+
"""
|
|
294
|
+
if n <= 3: # arbitrary cutoff
|
|
295
|
+
return _negaconvolution_naive(L1, L2)
|
|
296
|
+
return _negaconvolution_fft(L1, L2, n)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _negaconvolution_fft(L1, L2, n):
|
|
300
|
+
r"""
|
|
301
|
+
Return negacyclic convolution of lists L1 and L2, using FFT algorithm.
|
|
302
|
+
|
|
303
|
+
L1 and L2 must both have length `2^n`, where `n \geq 3`.
|
|
304
|
+
Assumes all entries of L1 and L2 belong to the same ring.
|
|
305
|
+
|
|
306
|
+
EXAMPLES::
|
|
307
|
+
|
|
308
|
+
sage: from sage.rings.polynomial.convolution import _negaconvolution_naive
|
|
309
|
+
sage: from sage.rings.polynomial.convolution import _negaconvolution_fft
|
|
310
|
+
sage: _negaconvolution_naive(list(range(8)), list(range(5, 13)))
|
|
311
|
+
[-224, -234, -224, -192, -136, -54, 56, 196]
|
|
312
|
+
sage: _negaconvolution_fft(list(range(8)), list(range(5, 13)), 3)
|
|
313
|
+
[-224, -234, -224, -192, -136, -54, 56, 196]
|
|
314
|
+
|
|
315
|
+
TESTS::
|
|
316
|
+
|
|
317
|
+
sage: for n in range(3, 10):
|
|
318
|
+
....: L1 = [ZZ.random_element(100) for _ in range(1 << n)]
|
|
319
|
+
....: L2 = [ZZ.random_element(100) for _ in range(1 << n)]
|
|
320
|
+
....: assert _negaconvolution_naive(L1, L2) == _negaconvolution_fft(L1, L2, n)
|
|
321
|
+
"""
|
|
322
|
+
assert n >= 3
|
|
323
|
+
|
|
324
|
+
R = parent(L1[0])
|
|
325
|
+
|
|
326
|
+
# split into 2^m pieces of 2^(k-1) coefficients each, with k as small
|
|
327
|
+
# as possible, subject to m <= k (so that the ring of Fourier coefficients
|
|
328
|
+
# has enough roots of unity)
|
|
329
|
+
m = (n + 1) >> 1
|
|
330
|
+
k = n + 1 - m
|
|
331
|
+
|
|
332
|
+
M = 1 << m
|
|
333
|
+
K = 1 << k
|
|
334
|
+
|
|
335
|
+
# split inputs into polynomials
|
|
336
|
+
L1 = _split(L1, m, k)
|
|
337
|
+
L2 = _split(L2, m, k)
|
|
338
|
+
|
|
339
|
+
# fft each input
|
|
340
|
+
_fft(L1, K, 0, m, K >> 1)
|
|
341
|
+
_fft(L2, K, 0, m, K >> 1)
|
|
342
|
+
|
|
343
|
+
# pointwise multiply
|
|
344
|
+
L3 = [_negaconvolution(L1[i], L2[i], k) for i in range(M)]
|
|
345
|
+
|
|
346
|
+
# inverse fft
|
|
347
|
+
_ifft(L3, K, 0, m, K >> 1)
|
|
348
|
+
|
|
349
|
+
# combine back into a single list
|
|
350
|
+
L3 = _nega_combine(L3, m, k)
|
|
351
|
+
|
|
352
|
+
# normalise
|
|
353
|
+
return [R(x / M) for x in L3]
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _convolution_fft(L1, L2):
|
|
357
|
+
r"""
|
|
358
|
+
Return convolution of non-empty lists L1 and L2, using FFT algorithm.
|
|
359
|
+
|
|
360
|
+
L1 and L2 may have arbitrary lengths `\geq 4`.
|
|
361
|
+
Assumes all entries of L1 and L2 belong to the same ring.
|
|
362
|
+
|
|
363
|
+
EXAMPLES::
|
|
364
|
+
|
|
365
|
+
sage: from sage.rings.polynomial.convolution import _convolution_naive
|
|
366
|
+
sage: from sage.rings.polynomial.convolution import _convolution_fft
|
|
367
|
+
sage: _convolution_naive([1, 2, 3], [4, 5, 6])
|
|
368
|
+
[4, 13, 28, 27, 18]
|
|
369
|
+
sage: _convolution_fft([1, 2, 3], [4, 5, 6])
|
|
370
|
+
[4, 13, 28, 27, 18]
|
|
371
|
+
|
|
372
|
+
TESTS::
|
|
373
|
+
|
|
374
|
+
sage: for len1 in range(4, 30):
|
|
375
|
+
....: for len2 in range(4, 30):
|
|
376
|
+
....: L1 = [ZZ.random_element(100) for _ in range(len1)]
|
|
377
|
+
....: L2 = [ZZ.random_element(100) for _ in range(len2)]
|
|
378
|
+
....: assert _convolution_naive(L1, L2) == _convolution_fft(L1, L2)
|
|
379
|
+
"""
|
|
380
|
+
R = parent(L1[0])
|
|
381
|
+
|
|
382
|
+
# choose n so that output convolution length is 2^n
|
|
383
|
+
len1 = len(L1)
|
|
384
|
+
len2 = len(L2)
|
|
385
|
+
outlen = len1 + len2 - 1
|
|
386
|
+
n = int(ceil(log(outlen) / log(2.0)))
|
|
387
|
+
|
|
388
|
+
# split into 2^m pieces of 2^(k-1) coefficients each, with k as small
|
|
389
|
+
# as possible, subject to m <= k + 1 (so that the ring of Fourier
|
|
390
|
+
# coefficients has enough roots of unity)
|
|
391
|
+
m = (n >> 1) + 1
|
|
392
|
+
k = n + 1 - m
|
|
393
|
+
|
|
394
|
+
N = 1 << n
|
|
395
|
+
M = 1 << m
|
|
396
|
+
K = 1 << k
|
|
397
|
+
|
|
398
|
+
# zero pad inputs up to length N
|
|
399
|
+
zero = R(0)
|
|
400
|
+
L1 = L1 + [zero] * (N - len(L1))
|
|
401
|
+
L2 = L2 + [zero] * (N - len(L2))
|
|
402
|
+
|
|
403
|
+
# split inputs into polynomials
|
|
404
|
+
L1 = _split(L1, m, k)
|
|
405
|
+
L2 = _split(L2, m, k)
|
|
406
|
+
|
|
407
|
+
# fft each input
|
|
408
|
+
_fft(L1, K, 0, m, K)
|
|
409
|
+
_fft(L2, K, 0, m, K)
|
|
410
|
+
|
|
411
|
+
# pointwise multiply
|
|
412
|
+
L3 = [_negaconvolution(L1[i], L2[i], k) for i in range(M)]
|
|
413
|
+
|
|
414
|
+
# inverse fft
|
|
415
|
+
_ifft(L3, K, 0, m, K)
|
|
416
|
+
|
|
417
|
+
# combine back into a single list
|
|
418
|
+
L3 = _combine(L3, m, k)
|
|
419
|
+
|
|
420
|
+
# normalise, and truncate to correct length
|
|
421
|
+
return [R(L3[i] / M) for i in range(outlen)]
|
|
File without changes
|