Skip to content

Commit 316c48c

Browse files
author
Release Manager
committed
sagemathgh-36456: implement .interpolation() method for ProductTree The `ProductTree` class has a `.remainders()` method which can, among other things, be used to implement the Fast Fourier Transform. In this patch we add a corresponding `.interpolation()` method, which can, among other things, be used to implement the *inverse* Fast Fourier Transform. Its functionality is equivalent to `CRT_list()`, but caching the product-tree structure makes it significantly faster for repeated invocations. URL: sagemath#36456 Reported by: Lorenz Panny Reviewer(s): Kwankyu Lee
2 parents e88cb46 + 07e2c29 commit 316c48c

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

src/sage/rings/generic.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,17 @@ class ProductTree:
2828
sage: R.<x> = F[]
2929
sage: ms = [x - a^i for i in range(1024)] # roots of unity
3030
sage: ys = [F.random_element() for _ in range(1024)] # input vector
31-
sage: zs = ProductTree(ms).remainders(R(ys)) # compute FFT!
31+
sage: tree = ProductTree(ms)
32+
sage: zs = tree.remainders(R(ys)) # compute FFT!
3233
sage: zs == [R(ys) % m for m in ms]
3334
True
3435
36+
Similarly, the :meth:`interpolation` method can be used to implement
37+
the inverse Fast Fourier Transform::
38+
39+
sage: tree.interpolation(zs).padded_list(len(ys)) == ys
40+
True
41+
3542
This class encodes the tree as *layers*: Layer `0` is just a tuple
3643
of the leaves. Layer `i+1` is obtained from layer `i` by replacing
3744
each pair of two adjacent elements by their product, starting from
@@ -177,7 +184,6 @@ def remainders(self, x):
177184
The base ring must support the ``%`` operator for this
178185
method to work.
179186
180-
181187
INPUT:
182188
183189
- ``x`` -- an element of the base ring of this product tree
@@ -199,6 +205,55 @@ def remainders(self, x):
199205
X = [X[i // 2] % V[i] for i in range(len(V))]
200206
return X
201207

208+
_crt_bases = None
209+
210+
def interpolation(self, xs):
211+
r"""
212+
Given a sequence ``xs`` of values, one per leaf, return a
213+
single element `x` which is congruent to the `i`\th value in
214+
``xs`` modulo the `i`\th leaf, for all `i`.
215+
216+
This is an explicit version of the Chinese remainder theorem;
217+
see also :meth:`CRT`. Using this product tree is faster for
218+
repeated calls since the required CRT bases are cached after
219+
the first run.
220+
221+
The base ring must support the :func:`xgcd` function for this
222+
method to work.
223+
224+
EXAMPLES::
225+
226+
sage: from sage.rings.generic import ProductTree
227+
sage: vs = prime_range(100)
228+
sage: tree = ProductTree(vs)
229+
sage: tree.interpolation([1, 1, 2, 1, 9, 1, 7, 15, 8, 20, 15, 6, 27, 11, 2, 6, 0, 25, 49, 5, 51, 4, 19, 74, 13])
230+
1085749272377676749812331719267
231+
232+
This method is faster than :func:`CRT` for repeated calls with
233+
the same moduli::
234+
235+
sage: vs = prime_range(1000,2000)
236+
sage: rs = lambda: [randrange(1,100) for _ in vs]
237+
sage: tree = ProductTree(vs)
238+
sage: %timeit CRT(rs(), vs) # not tested
239+
372 µs ± 3.34 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
240+
sage: %timeit tree.interpolation(rs()) # not tested
241+
146 µs ± 479 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
242+
"""
243+
if self._crt_bases is None:
244+
from sage.arith.misc import CRT_basis
245+
self._crt_bases = []
246+
for V in self.layers[:-1]:
247+
B = tuple(CRT_basis(V[i:i+2]) for i in range(0, len(V), 2))
248+
self._crt_bases.append(B)
249+
if len(xs) != len(self.layers[0]):
250+
raise ValueError('number of given elements must equal the number of leaves')
251+
for basis, layer in zip(self._crt_bases, self.layers[1:]):
252+
xs = [sum(c*x for c, x in zip(cs, xs[2*i:2*i+2])) % mod
253+
for i, (cs, mod) in enumerate(zip(basis, layer))]
254+
assert len(xs) == 1
255+
return xs[0]
256+
202257

203258
def prod_with_derivative(pairs):
204259
r"""

0 commit comments

Comments
 (0)