chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
"""This module contains functions which operate on discrete sequences.
|
||||
|
||||
Transforms - ``fft``, ``ifft``, ``ntt``, ``intt``, ``fwht``, ``ifwht``,
|
||||
``mobius_transform``, ``inverse_mobius_transform``
|
||||
|
||||
Convolutions - ``convolution``, ``convolution_fft``, ``convolution_ntt``,
|
||||
``convolution_fwht``, ``convolution_subset``,
|
||||
``covering_product``, ``intersecting_product``
|
||||
"""
|
||||
|
||||
from .transforms import (fft, ifft, ntt, intt, fwht, ifwht,
|
||||
mobius_transform, inverse_mobius_transform)
|
||||
from .convolutions import convolution, covering_product, intersecting_product
|
||||
|
||||
__all__ = [
|
||||
'fft', 'ifft', 'ntt', 'intt', 'fwht', 'ifwht', 'mobius_transform',
|
||||
'inverse_mobius_transform',
|
||||
|
||||
'convolution', 'covering_product', 'intersecting_product',
|
||||
]
|
||||
@@ -0,0 +1,597 @@
|
||||
"""
|
||||
Convolution (using **FFT**, **NTT**, **FWHT**), Subset Convolution,
|
||||
Covering Product, Intersecting Product
|
||||
"""
|
||||
|
||||
from sympy.core import S, sympify, Rational
|
||||
from sympy.core.function import expand_mul
|
||||
from sympy.discrete.transforms import (
|
||||
fft, ifft, ntt, intt, fwht, ifwht,
|
||||
mobius_transform, inverse_mobius_transform)
|
||||
from sympy.external.gmpy import MPZ, lcm
|
||||
from sympy.utilities.iterables import iterable
|
||||
from sympy.utilities.misc import as_int
|
||||
|
||||
|
||||
def convolution(a, b, cycle=0, dps=None, prime=None, dyadic=None, subset=None):
|
||||
"""
|
||||
Performs convolution by determining the type of desired
|
||||
convolution using hints.
|
||||
|
||||
Exactly one of ``dps``, ``prime``, ``dyadic``, ``subset`` arguments
|
||||
should be specified explicitly for identifying the type of convolution,
|
||||
and the argument ``cycle`` can be specified optionally.
|
||||
|
||||
For the default arguments, linear convolution is performed using **FFT**.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which convolution is performed.
|
||||
cycle : Integer
|
||||
Specifies the length for doing cyclic convolution.
|
||||
dps : Integer
|
||||
Specifies the number of decimal digits for precision for
|
||||
performing **FFT** on the sequence.
|
||||
prime : Integer
|
||||
Prime modulus of the form `(m 2^k + 1)` to be used for
|
||||
performing **NTT** on the sequence.
|
||||
dyadic : bool
|
||||
Identifies the convolution type as dyadic (*bitwise-XOR*)
|
||||
convolution, which is performed using **FWHT**.
|
||||
subset : bool
|
||||
Identifies the convolution type as subset convolution.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import convolution, symbols, S, I
|
||||
>>> u, v, w, x, y, z = symbols('u v w x y z')
|
||||
|
||||
>>> convolution([1 + 2*I, 4 + 3*I], [S(5)/4, 6], dps=3)
|
||||
[1.25 + 2.5*I, 11.0 + 15.8*I, 24.0 + 18.0*I]
|
||||
>>> convolution([1, 2, 3], [4, 5, 6], cycle=3)
|
||||
[31, 31, 28]
|
||||
|
||||
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1)
|
||||
[1283, 19351, 14219]
|
||||
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1, cycle=2)
|
||||
[15502, 19351]
|
||||
|
||||
>>> convolution([u, v], [x, y, z], dyadic=True)
|
||||
[u*x + v*y, u*y + v*x, u*z, v*z]
|
||||
>>> convolution([u, v], [x, y, z], dyadic=True, cycle=2)
|
||||
[u*x + u*z + v*y, u*y + v*x + v*z]
|
||||
|
||||
>>> convolution([u, v, w], [x, y, z], subset=True)
|
||||
[u*x, u*y + v*x, u*z + w*x, v*z + w*y]
|
||||
>>> convolution([u, v, w], [x, y, z], subset=True, cycle=3)
|
||||
[u*x + v*z + w*y, u*y + v*x, u*z + w*x]
|
||||
|
||||
"""
|
||||
|
||||
c = as_int(cycle)
|
||||
if c < 0:
|
||||
raise ValueError("The length for cyclic convolution "
|
||||
"must be non-negative")
|
||||
|
||||
dyadic = True if dyadic else None
|
||||
subset = True if subset else None
|
||||
if sum(x is not None for x in (prime, dps, dyadic, subset)) > 1:
|
||||
raise TypeError("Ambiguity in determining the type of convolution")
|
||||
|
||||
if prime is not None:
|
||||
ls = convolution_ntt(a, b, prime=prime)
|
||||
return ls if not c else [sum(ls[i::c]) % prime for i in range(c)]
|
||||
|
||||
if dyadic:
|
||||
ls = convolution_fwht(a, b)
|
||||
elif subset:
|
||||
ls = convolution_subset(a, b)
|
||||
else:
|
||||
def loop(a):
|
||||
dens = []
|
||||
for i in a:
|
||||
if isinstance(i, Rational) and i.q - 1:
|
||||
dens.append(i.q)
|
||||
elif not isinstance(i, int):
|
||||
return
|
||||
if dens:
|
||||
l = lcm(*dens)
|
||||
return [i*l if type(i) is int else i.p*(l//i.q) for i in a], l
|
||||
# no lcm of den to deal with
|
||||
return a, 1
|
||||
ls = None
|
||||
da = loop(a)
|
||||
if da is not None:
|
||||
db = loop(b)
|
||||
if db is not None:
|
||||
(ia, ma), (ib, mb) = da, db
|
||||
den = ma*mb
|
||||
ls = convolution_int(ia, ib)
|
||||
if den != 1:
|
||||
ls = [Rational(i, den) for i in ls]
|
||||
if ls is None:
|
||||
ls = convolution_fft(a, b, dps)
|
||||
|
||||
return ls if not c else [sum(ls[i::c]) for i in range(c)]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Convolution for Complex domain #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def convolution_fft(a, b, dps=None):
|
||||
"""
|
||||
Performs linear convolution using Fast Fourier Transform.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which convolution is performed.
|
||||
dps : Integer
|
||||
Specifies the number of decimal digits for precision.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import S, I
|
||||
>>> from sympy.discrete.convolutions import convolution_fft
|
||||
|
||||
>>> convolution_fft([2, 3], [4, 5])
|
||||
[8, 22, 15]
|
||||
>>> convolution_fft([2, 5], [6, 7, 3])
|
||||
[12, 44, 41, 15]
|
||||
>>> convolution_fft([1 + 2*I, 4 + 3*I], [S(5)/4, 6])
|
||||
[5/4 + 5*I/2, 11 + 63*I/4, 24 + 18*I]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Convolution_theorem
|
||||
.. [2] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
||||
|
||||
"""
|
||||
|
||||
a, b = a[:], b[:]
|
||||
n = m = len(a) + len(b) - 1 # convolution size
|
||||
|
||||
if n > 0 and n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [S.Zero]*(n - len(a))
|
||||
b += [S.Zero]*(n - len(b))
|
||||
|
||||
a, b = fft(a, dps), fft(b, dps)
|
||||
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
||||
a = ifft(a, dps)[:m]
|
||||
|
||||
return a
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Convolution for GF(p) #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def convolution_ntt(a, b, prime):
|
||||
"""
|
||||
Performs linear convolution using Number Theoretic Transform.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which convolution is performed.
|
||||
prime : Integer
|
||||
Prime modulus of the form `(m 2^k + 1)` to be used for performing
|
||||
**NTT** on the sequence.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.discrete.convolutions import convolution_ntt
|
||||
>>> convolution_ntt([2, 3], [4, 5], prime=19*2**10 + 1)
|
||||
[8, 22, 15]
|
||||
>>> convolution_ntt([2, 5], [6, 7, 3], prime=19*2**10 + 1)
|
||||
[12, 44, 41, 15]
|
||||
>>> convolution_ntt([333, 555], [222, 666], prime=19*2**10 + 1)
|
||||
[15555, 14219, 19404]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Convolution_theorem
|
||||
.. [2] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
||||
|
||||
"""
|
||||
|
||||
a, b, p = a[:], b[:], as_int(prime)
|
||||
n = m = len(a) + len(b) - 1 # convolution size
|
||||
|
||||
if n > 0 and n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [0]*(n - len(a))
|
||||
b += [0]*(n - len(b))
|
||||
|
||||
a, b = ntt(a, p), ntt(b, p)
|
||||
a = [x*y % p for x, y in zip(a, b)]
|
||||
a = intt(a, p)[:m]
|
||||
|
||||
return a
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Convolution for 2**n-group #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def convolution_fwht(a, b):
|
||||
"""
|
||||
Performs dyadic (*bitwise-XOR*) convolution using Fast Walsh Hadamard
|
||||
Transform.
|
||||
|
||||
The convolution is automatically padded to the right with zeros, as the
|
||||
*radix-2 FWHT* requires the number of sample points to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which convolution is performed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, S, I
|
||||
>>> from sympy.discrete.convolutions import convolution_fwht
|
||||
|
||||
>>> u, v, x, y = symbols('u v x y')
|
||||
>>> convolution_fwht([u, v], [x, y])
|
||||
[u*x + v*y, u*y + v*x]
|
||||
|
||||
>>> convolution_fwht([2, 3], [4, 5])
|
||||
[23, 22]
|
||||
>>> convolution_fwht([2, 5 + 4*I, 7], [6*I, 7, 3 + 4*I])
|
||||
[56 + 68*I, -10 + 30*I, 6 + 50*I, 48 + 32*I]
|
||||
|
||||
>>> convolution_fwht([S(33)/7, S(55)/6, S(7)/4], [S(2)/3, 5])
|
||||
[2057/42, 1870/63, 7/6, 35/4]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://www.radioeng.cz/fulltexts/2002/02_03_40_42.pdf
|
||||
.. [2] https://en.wikipedia.org/wiki/Hadamard_transform
|
||||
|
||||
"""
|
||||
|
||||
if not a or not b:
|
||||
return []
|
||||
|
||||
a, b = a[:], b[:]
|
||||
n = max(len(a), len(b))
|
||||
|
||||
if n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [S.Zero]*(n - len(a))
|
||||
b += [S.Zero]*(n - len(b))
|
||||
|
||||
a, b = fwht(a), fwht(b)
|
||||
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
||||
a = ifwht(a)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Subset Convolution #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def convolution_subset(a, b):
|
||||
"""
|
||||
Performs Subset Convolution of given sequences.
|
||||
|
||||
The indices of each argument, considered as bit strings, correspond to
|
||||
subsets of a finite set.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
definition of subset based on bitmasks (indices) requires the size of
|
||||
sequence to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which convolution is performed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, S
|
||||
>>> from sympy.discrete.convolutions import convolution_subset
|
||||
>>> u, v, x, y, z = symbols('u v x y z')
|
||||
|
||||
>>> convolution_subset([u, v], [x, y])
|
||||
[u*x, u*y + v*x]
|
||||
>>> convolution_subset([u, v, x], [y, z])
|
||||
[u*y, u*z + v*y, x*y, x*z]
|
||||
|
||||
>>> convolution_subset([1, S(2)/3], [3, 4])
|
||||
[3, 6]
|
||||
>>> convolution_subset([1, 3, S(5)/7], [7])
|
||||
[7, 21, 5, 0]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
||||
|
||||
"""
|
||||
|
||||
if not a or not b:
|
||||
return []
|
||||
|
||||
if not iterable(a) or not iterable(b):
|
||||
raise TypeError("Expected a sequence of coefficients for convolution")
|
||||
|
||||
a = [sympify(arg) for arg in a]
|
||||
b = [sympify(arg) for arg in b]
|
||||
n = max(len(a), len(b))
|
||||
|
||||
if n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [S.Zero]*(n - len(a))
|
||||
b += [S.Zero]*(n - len(b))
|
||||
|
||||
c = [S.Zero]*n
|
||||
|
||||
for mask in range(n):
|
||||
smask = mask
|
||||
while smask > 0:
|
||||
c[mask] += expand_mul(a[smask] * b[mask^smask])
|
||||
smask = (smask - 1)&mask
|
||||
|
||||
c[mask] += expand_mul(a[smask] * b[mask^smask])
|
||||
|
||||
return c
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Covering Product #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def covering_product(a, b):
|
||||
"""
|
||||
Returns the covering product of given sequences.
|
||||
|
||||
The indices of each argument, considered as bit strings, correspond to
|
||||
subsets of a finite set.
|
||||
|
||||
The covering product of given sequences is a sequence which contains
|
||||
the sum of products of the elements of the given sequences grouped by
|
||||
the *bitwise-OR* of the corresponding indices.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
definition of subset based on bitmasks (indices) requires the size of
|
||||
sequence to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which covering product is to be obtained.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, S, I, covering_product
|
||||
>>> u, v, x, y, z = symbols('u v x y z')
|
||||
|
||||
>>> covering_product([u, v], [x, y])
|
||||
[u*x, u*y + v*x + v*y]
|
||||
>>> covering_product([u, v, x], [y, z])
|
||||
[u*y, u*z + v*y + v*z, x*y, x*z]
|
||||
|
||||
>>> covering_product([1, S(2)/3], [3, 4 + 5*I])
|
||||
[3, 26/3 + 25*I/3]
|
||||
>>> covering_product([1, 3, S(5)/7], [7, 8])
|
||||
[7, 53, 5, 40/7]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
||||
|
||||
"""
|
||||
|
||||
if not a or not b:
|
||||
return []
|
||||
|
||||
a, b = a[:], b[:]
|
||||
n = max(len(a), len(b))
|
||||
|
||||
if n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [S.Zero]*(n - len(a))
|
||||
b += [S.Zero]*(n - len(b))
|
||||
|
||||
a, b = mobius_transform(a), mobius_transform(b)
|
||||
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
||||
a = inverse_mobius_transform(a)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Intersecting Product #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def intersecting_product(a, b):
|
||||
"""
|
||||
Returns the intersecting product of given sequences.
|
||||
|
||||
The indices of each argument, considered as bit strings, correspond to
|
||||
subsets of a finite set.
|
||||
|
||||
The intersecting product of given sequences is the sequence which
|
||||
contains the sum of products of the elements of the given sequences
|
||||
grouped by the *bitwise-AND* of the corresponding indices.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
definition of subset based on bitmasks (indices) requires the size of
|
||||
sequence to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : iterables
|
||||
The sequences for which intersecting product is to be obtained.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, S, I, intersecting_product
|
||||
>>> u, v, x, y, z = symbols('u v x y z')
|
||||
|
||||
>>> intersecting_product([u, v], [x, y])
|
||||
[u*x + u*y + v*x, v*y]
|
||||
>>> intersecting_product([u, v, x], [y, z])
|
||||
[u*y + u*z + v*y + x*y + x*z, v*z, 0, 0]
|
||||
|
||||
>>> intersecting_product([1, S(2)/3], [3, 4 + 5*I])
|
||||
[9 + 5*I, 8/3 + 10*I/3]
|
||||
>>> intersecting_product([1, 3, S(5)/7], [7, 8])
|
||||
[327/7, 24, 0, 0]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
||||
|
||||
"""
|
||||
|
||||
if not a or not b:
|
||||
return []
|
||||
|
||||
a, b = a[:], b[:]
|
||||
n = max(len(a), len(b))
|
||||
|
||||
if n&(n - 1): # not a power of 2
|
||||
n = 2**n.bit_length()
|
||||
|
||||
# padding with zeros
|
||||
a += [S.Zero]*(n - len(a))
|
||||
b += [S.Zero]*(n - len(b))
|
||||
|
||||
a, b = mobius_transform(a, subset=False), mobius_transform(b, subset=False)
|
||||
a = [expand_mul(x*y) for x, y in zip(a, b)]
|
||||
a = inverse_mobius_transform(a, subset=False)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Integer Convolutions #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def convolution_int(a, b):
|
||||
"""Return the convolution of two sequences as a list.
|
||||
|
||||
The iterables must consist solely of integers.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a, b : Sequence
|
||||
The sequences for which convolution is performed.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function performs the convolution of ``a`` and ``b`` by packing
|
||||
each into a single integer, multiplying them together, and then
|
||||
unpacking the result from the product. The intuition behind this is
|
||||
that if we evaluate some polynomial [1]:
|
||||
|
||||
.. math ::
|
||||
1156x^6 + 3808x^5 + 8440x^4 + 14856x^3 + 16164x^2 + 14040x + 8100
|
||||
|
||||
at say $x = 10^5$ we obtain $1156038080844014856161641404008100$.
|
||||
Note we can read of the coefficients for each term every five digits.
|
||||
If the $x$ we chose to evaluate at is large enough, the same will hold
|
||||
for the product.
|
||||
|
||||
The idea now is since big integer multiplication in libraries such
|
||||
as GMP is highly optimised, this will be reasonably fast.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.discrete.convolutions import convolution_int
|
||||
|
||||
>>> convolution_int([2, 3], [4, 5])
|
||||
[8, 22, 15]
|
||||
>>> convolution_int([1, 1, -1], [1, 1])
|
||||
[1, 2, 0, -1]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Fateman, Richard J.
|
||||
Can you save time in multiplying polynomials by encoding them as integers?
|
||||
University of California, Berkeley, California (2004).
|
||||
https://people.eecs.berkeley.edu/~fateman/papers/polysbyGMP.pdf
|
||||
"""
|
||||
# An upper bound on the largest coefficient in p(x)q(x) is given by (1 + min(dp, dq))N(p)N(q)
|
||||
# where dp = deg(p), dq = deg(q), N(f) denotes the coefficient of largest modulus in f [1]
|
||||
B = max(abs(c) for c in a)*max(abs(c) for c in b)*(1 + min(len(a) - 1, len(b) - 1))
|
||||
x, power = MPZ(1), 0
|
||||
while x <= (2*B): # multiply by two for negative coefficients, see [1]
|
||||
x <<= 1
|
||||
power += 1
|
||||
|
||||
def to_integer(poly):
|
||||
n, mul = MPZ(0), 0
|
||||
for c in reversed(poly):
|
||||
if c and not mul: mul = -1 if c < 0 else 1
|
||||
n <<= power
|
||||
n += mul*int(c)
|
||||
return mul, n
|
||||
|
||||
# Perform packing and multiplication
|
||||
(a_mul, a_packed), (b_mul, b_packed) = to_integer(a), to_integer(b)
|
||||
result = a_packed * b_packed
|
||||
|
||||
# Perform unpacking
|
||||
mul = a_mul * b_mul
|
||||
mask, half, borrow, poly = x - 1, x >> 1, 0, []
|
||||
while result or borrow:
|
||||
coeff = (result & mask) + borrow
|
||||
result >>= power
|
||||
borrow = coeff >= half
|
||||
poly.append(mul * int(coeff if coeff < half else coeff - x))
|
||||
return poly or [0]
|
||||
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Recurrences
|
||||
"""
|
||||
|
||||
from sympy.core import S, sympify
|
||||
from sympy.utilities.iterables import iterable
|
||||
from sympy.utilities.misc import as_int
|
||||
|
||||
|
||||
def linrec(coeffs, init, n):
|
||||
r"""
|
||||
Evaluation of univariate linear recurrences of homogeneous type
|
||||
having coefficients independent of the recurrence variable.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
coeffs : iterable
|
||||
Coefficients of the recurrence
|
||||
init : iterable
|
||||
Initial values of the recurrence
|
||||
n : Integer
|
||||
Point of evaluation for the recurrence
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Let `y(n)` be the recurrence of given type, ``c`` be the sequence
|
||||
of coefficients, ``b`` be the sequence of initial/base values of the
|
||||
recurrence and ``k`` (equal to ``len(c)``) be the order of recurrence.
|
||||
Then,
|
||||
|
||||
.. math :: y(n) = \begin{cases} b_n & 0 \le n < k \\
|
||||
c_0 y(n-1) + c_1 y(n-2) + \cdots + c_{k-1} y(n-k) & n \ge k
|
||||
\end{cases}
|
||||
|
||||
Let `x_0, x_1, \ldots, x_n` be a sequence and consider the transformation
|
||||
that maps each polynomial `f(x)` to `T(f(x))` where each power `x^i` is
|
||||
replaced by the corresponding value `x_i`. The sequence is then a solution
|
||||
of the recurrence if and only if `T(x^i p(x)) = 0` for each `i \ge 0` where
|
||||
`p(x) = x^k - c_0 x^(k-1) - \cdots - c_{k-1}` is the characteristic
|
||||
polynomial.
|
||||
|
||||
Then `T(f(x)p(x)) = 0` for each polynomial `f(x)` (as it is a linear
|
||||
combination of powers `x^i`). Now, if `x^n` is congruent to
|
||||
`g(x) = a_0 x^0 + a_1 x^1 + \cdots + a_{k-1} x^{k-1}` modulo `p(x)`, then
|
||||
`T(x^n) = x_n` is equal to
|
||||
`T(g(x)) = a_0 x_0 + a_1 x_1 + \cdots + a_{k-1} x_{k-1}`.
|
||||
|
||||
Computation of `x^n`,
|
||||
given `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
|
||||
is performed using exponentiation by squaring (refer to [1_]) with
|
||||
an additional reduction step performed to retain only first `k` powers
|
||||
of `x` in the representation of `x^n`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.discrete.recurrences import linrec
|
||||
>>> from sympy.abc import x, y, z
|
||||
|
||||
>>> linrec(coeffs=[1, 1], init=[0, 1], n=10)
|
||||
55
|
||||
|
||||
>>> linrec(coeffs=[1, 1], init=[x, y], n=10)
|
||||
34*x + 55*y
|
||||
|
||||
>>> linrec(coeffs=[x, y], init=[0, 1], n=5)
|
||||
x**2*y + x*(x**3 + 2*x*y) + y**2
|
||||
|
||||
>>> linrec(coeffs=[1, 2, 3, 0, 0, 4], init=[x, y, z], n=16)
|
||||
13576*x + 5676*y + 2356*z
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
||||
.. [2] https://en.wikipedia.org/w/index.php?title=Modular_exponentiation§ion=6#Matrices
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.polys.agca.extensions.ExtensionElement.__pow__
|
||||
|
||||
"""
|
||||
|
||||
if not coeffs:
|
||||
return S.Zero
|
||||
|
||||
if not iterable(coeffs):
|
||||
raise TypeError("Expected a sequence of coefficients for"
|
||||
" the recurrence")
|
||||
|
||||
if not iterable(init):
|
||||
raise TypeError("Expected a sequence of values for the initialization"
|
||||
" of the recurrence")
|
||||
|
||||
n = as_int(n)
|
||||
if n < 0:
|
||||
raise ValueError("Point of evaluation of recurrence must be a "
|
||||
"non-negative integer")
|
||||
|
||||
c = [sympify(arg) for arg in coeffs]
|
||||
b = [sympify(arg) for arg in init]
|
||||
k = len(c)
|
||||
|
||||
if len(b) > k:
|
||||
raise TypeError("Count of initial values should not exceed the "
|
||||
"order of the recurrence")
|
||||
else:
|
||||
b += [S.Zero]*(k - len(b)) # remaining initial values default to zero
|
||||
|
||||
if n < k:
|
||||
return b[n]
|
||||
terms = [u*v for u, v in zip(linrec_coeffs(c, n), b)]
|
||||
return sum(terms[:-1], terms[-1])
|
||||
|
||||
|
||||
def linrec_coeffs(c, n):
|
||||
r"""
|
||||
Compute the coefficients of n'th term in linear recursion
|
||||
sequence defined by c.
|
||||
|
||||
`x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`.
|
||||
|
||||
It computes the coefficients by using binary exponentiation.
|
||||
This function is used by `linrec` and `_eval_pow_by_cayley`.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
c = coefficients of the divisor polynomial
|
||||
n = exponent of x, so dividend is x^n
|
||||
|
||||
"""
|
||||
|
||||
k = len(c)
|
||||
|
||||
def _square_and_reduce(u, offset):
|
||||
# squares `(u_0 + u_1 x + u_2 x^2 + \cdots + u_{k-1} x^k)` (and
|
||||
# multiplies by `x` if offset is 1) and reduces the above result of
|
||||
# length upto `2k` to `k` using the characteristic equation of the
|
||||
# recurrence given by, `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
|
||||
|
||||
w = [S.Zero]*(2*len(u) - 1 + offset)
|
||||
for i, p in enumerate(u):
|
||||
for j, q in enumerate(u):
|
||||
w[offset + i + j] += p*q
|
||||
|
||||
for j in range(len(w) - 1, k - 1, -1):
|
||||
for i in range(k):
|
||||
w[j - i - 1] += w[j]*c[i]
|
||||
|
||||
return w[:k]
|
||||
|
||||
def _final_coeffs(n):
|
||||
# computes the final coefficient list - `cf` corresponding to the
|
||||
# point at which recurrence is to be evalauted - `n`, such that,
|
||||
# `y(n) = cf_0 y(k-1) + cf_1 y(k-2) + \cdots + cf_{k-1} y(0)`
|
||||
|
||||
if n < k:
|
||||
return [S.Zero]*n + [S.One] + [S.Zero]*(k - n - 1)
|
||||
else:
|
||||
return _square_and_reduce(_final_coeffs(n // 2), n % 2)
|
||||
|
||||
return _final_coeffs(n)
|
||||
@@ -0,0 +1,392 @@
|
||||
from sympy.core.numbers import (E, Rational, pi)
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.core import S, symbols, I
|
||||
from sympy.discrete.convolutions import (
|
||||
convolution, convolution_fft, convolution_ntt, convolution_fwht,
|
||||
convolution_subset, covering_product, intersecting_product,
|
||||
convolution_int)
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.abc import x, y
|
||||
|
||||
def test_convolution():
|
||||
# fft
|
||||
a = [1, Rational(5, 3), sqrt(3), Rational(7, 5)]
|
||||
b = [9, 5, 5, 4, 3, 2]
|
||||
c = [3, 5, 3, 7, 8]
|
||||
d = [1422, 6572, 3213, 5552]
|
||||
e = [-1, Rational(5, 3), Rational(7, 5)]
|
||||
|
||||
assert convolution(a, b) == convolution_fft(a, b)
|
||||
assert convolution(a, b, dps=9) == convolution_fft(a, b, dps=9)
|
||||
assert convolution(a, d, dps=7) == convolution_fft(d, a, dps=7)
|
||||
assert convolution(a, d[1:], dps=3) == convolution_fft(d[1:], a, dps=3)
|
||||
|
||||
# prime moduli of the form (m*2**k + 1), sequence length
|
||||
# should be a divisor of 2**k
|
||||
p = 7*17*2**23 + 1
|
||||
q = 19*2**10 + 1
|
||||
|
||||
# ntt
|
||||
assert convolution(d, b, prime=q) == convolution_ntt(b, d, prime=q)
|
||||
assert convolution(c, b, prime=p) == convolution_ntt(b, c, prime=p)
|
||||
assert convolution(d, c, prime=p) == convolution_ntt(c, d, prime=p)
|
||||
raises(TypeError, lambda: convolution(b, d, dps=5, prime=q))
|
||||
raises(TypeError, lambda: convolution(b, d, dps=6, prime=q))
|
||||
|
||||
# fwht
|
||||
assert convolution(a, b, dyadic=True) == convolution_fwht(a, b)
|
||||
assert convolution(a, b, dyadic=False) == convolution(a, b)
|
||||
raises(TypeError, lambda: convolution(b, d, dps=2, dyadic=True))
|
||||
raises(TypeError, lambda: convolution(b, d, prime=p, dyadic=True))
|
||||
raises(TypeError, lambda: convolution(a, b, dps=2, dyadic=True))
|
||||
raises(TypeError, lambda: convolution(b, c, prime=p, dyadic=True))
|
||||
|
||||
# subset
|
||||
assert convolution(a, b, subset=True) == convolution_subset(a, b) == \
|
||||
convolution(a, b, subset=True, dyadic=False) == \
|
||||
convolution(a, b, subset=True)
|
||||
assert convolution(a, b, subset=False) == convolution(a, b)
|
||||
raises(TypeError, lambda: convolution(a, b, subset=True, dyadic=True))
|
||||
raises(TypeError, lambda: convolution(c, d, subset=True, dps=6))
|
||||
raises(TypeError, lambda: convolution(a, c, subset=True, prime=q))
|
||||
|
||||
# integer
|
||||
assert convolution([0], [0]) == convolution_int([0], [0])
|
||||
assert convolution(b, c) == convolution_int(b, c)
|
||||
|
||||
# rational
|
||||
assert convolution([Rational(1,2)], [Rational(1,2)]) == [Rational(1, 4)]
|
||||
assert convolution(b, e) == [-9, 10, Rational(239, 15), Rational(34, 3),
|
||||
Rational(32, 3), Rational(43, 5), Rational(113, 15),
|
||||
Rational(14, 5)]
|
||||
|
||||
|
||||
def test_cyclic_convolution():
|
||||
# fft
|
||||
a = [1, Rational(5, 3), sqrt(3), Rational(7, 5)]
|
||||
b = [9, 5, 5, 4, 3, 2]
|
||||
|
||||
assert convolution([1, 2, 3], [4, 5, 6], cycle=0) == \
|
||||
convolution([1, 2, 3], [4, 5, 6], cycle=5) == \
|
||||
convolution([1, 2, 3], [4, 5, 6])
|
||||
|
||||
assert convolution([1, 2, 3], [4, 5, 6], cycle=3) == [31, 31, 28]
|
||||
|
||||
a = [Rational(1, 3), Rational(7, 3), Rational(5, 9), Rational(2, 7), Rational(5, 8)]
|
||||
b = [Rational(3, 5), Rational(4, 7), Rational(7, 8), Rational(8, 9)]
|
||||
|
||||
assert convolution(a, b, cycle=0) == \
|
||||
convolution(a, b, cycle=len(a) + len(b) - 1)
|
||||
|
||||
assert convolution(a, b, cycle=4) == [Rational(87277, 26460), Rational(30521, 11340),
|
||||
Rational(11125, 4032), Rational(3653, 1080)]
|
||||
|
||||
assert convolution(a, b, cycle=6) == [Rational(20177, 20160), Rational(676, 315), Rational(47, 24),
|
||||
Rational(3053, 1080), Rational(16397, 5292), Rational(2497, 2268)]
|
||||
|
||||
assert convolution(a, b, cycle=9) == \
|
||||
convolution(a, b, cycle=0) + [S.Zero]
|
||||
|
||||
# ntt
|
||||
a = [2313, 5323532, S(3232), 42142, 42242421]
|
||||
b = [S(33456), 56757, 45754, 432423]
|
||||
|
||||
assert convolution(a, b, prime=19*2**10 + 1, cycle=0) == \
|
||||
convolution(a, b, prime=19*2**10 + 1, cycle=8) == \
|
||||
convolution(a, b, prime=19*2**10 + 1)
|
||||
|
||||
assert convolution(a, b, prime=19*2**10 + 1, cycle=5) == [96, 17146, 2664,
|
||||
15534, 3517]
|
||||
|
||||
assert convolution(a, b, prime=19*2**10 + 1, cycle=7) == [4643, 3458, 1260,
|
||||
15534, 3517, 16314, 13688]
|
||||
|
||||
assert convolution(a, b, prime=19*2**10 + 1, cycle=9) == \
|
||||
convolution(a, b, prime=19*2**10 + 1) + [0]
|
||||
|
||||
# fwht
|
||||
u, v, w, x, y = symbols('u v w x y')
|
||||
p, q, r, s, t = symbols('p q r s t')
|
||||
c = [u, v, w, x, y]
|
||||
d = [p, q, r, s, t]
|
||||
|
||||
assert convolution(a, b, dyadic=True, cycle=3) == \
|
||||
[2499522285783, 19861417974796, 4702176579021]
|
||||
|
||||
assert convolution(a, b, dyadic=True, cycle=5) == [2718149225143,
|
||||
2114320852171, 20571217906407, 246166418903, 1413262436976]
|
||||
|
||||
assert convolution(c, d, dyadic=True, cycle=4) == \
|
||||
[p*u + p*y + q*v + r*w + s*x + t*u + t*y,
|
||||
p*v + q*u + q*y + r*x + s*w + t*v,
|
||||
p*w + q*x + r*u + r*y + s*v + t*w,
|
||||
p*x + q*w + r*v + s*u + s*y + t*x]
|
||||
|
||||
assert convolution(c, d, dyadic=True, cycle=6) == \
|
||||
[p*u + q*v + r*w + r*y + s*x + t*w + t*y,
|
||||
p*v + q*u + r*x + s*w + s*y + t*x,
|
||||
p*w + q*x + r*u + s*v,
|
||||
p*x + q*w + r*v + s*u,
|
||||
p*y + t*u,
|
||||
q*y + t*v]
|
||||
|
||||
# subset
|
||||
assert convolution(a, b, subset=True, cycle=7) == [18266671799811,
|
||||
178235365533, 213958794, 246166418903, 1413262436976,
|
||||
2397553088697, 1932759730434]
|
||||
|
||||
assert convolution(a[1:], b, subset=True, cycle=4) == \
|
||||
[178104086592, 302255835516, 244982785880, 3717819845434]
|
||||
|
||||
assert convolution(a, b[:-1], subset=True, cycle=6) == [1932837114162,
|
||||
178235365533, 213958794, 245166224504, 1413262436976, 2397553088697]
|
||||
|
||||
assert convolution(c, d, subset=True, cycle=3) == \
|
||||
[p*u + p*x + q*w + r*v + r*y + s*u + t*w,
|
||||
p*v + p*y + q*u + s*y + t*u + t*x,
|
||||
p*w + q*y + r*u + t*v]
|
||||
|
||||
assert convolution(c, d, subset=True, cycle=5) == \
|
||||
[p*u + q*y + t*v,
|
||||
p*v + q*u + r*y + t*w,
|
||||
p*w + r*u + s*y + t*x,
|
||||
p*x + q*w + r*v + s*u,
|
||||
p*y + t*u]
|
||||
|
||||
raises(ValueError, lambda: convolution([1, 2, 3], [4, 5, 6], cycle=-1))
|
||||
|
||||
|
||||
def test_convolution_fft():
|
||||
assert all(convolution_fft([], x, dps=y) == [] for x in ([], [1]) for y in (None, 3))
|
||||
assert convolution_fft([1, 2, 3], [4, 5, 6]) == [4, 13, 28, 27, 18]
|
||||
assert convolution_fft([1], [5, 6, 7]) == [5, 6, 7]
|
||||
assert convolution_fft([1, 3], [5, 6, 7]) == [5, 21, 25, 21]
|
||||
|
||||
assert convolution_fft([1 + 2*I], [2 + 3*I]) == [-4 + 7*I]
|
||||
assert convolution_fft([1 + 2*I, 3 + 4*I, 5 + 3*I/5], [Rational(2, 5) + 4*I/7]) == \
|
||||
[Rational(-26, 35) + I*48/35, Rational(-38, 35) + I*116/35, Rational(58, 35) + I*542/175]
|
||||
|
||||
assert convolution_fft([Rational(3, 4), Rational(5, 6)], [Rational(7, 8), Rational(1, 3), Rational(2, 5)]) == \
|
||||
[Rational(21, 32), Rational(47, 48), Rational(26, 45), Rational(1, 3)]
|
||||
|
||||
assert convolution_fft([Rational(1, 9), Rational(2, 3), Rational(3, 5)], [Rational(2, 5), Rational(3, 7), Rational(4, 9)]) == \
|
||||
[Rational(2, 45), Rational(11, 35), Rational(8152, 14175), Rational(523, 945), Rational(4, 15)]
|
||||
|
||||
assert convolution_fft([pi, E, sqrt(2)], [sqrt(3), 1/pi, 1/E]) == \
|
||||
[sqrt(3)*pi, 1 + sqrt(3)*E, E/pi + pi*exp(-1) + sqrt(6),
|
||||
sqrt(2)/pi + 1, sqrt(2)*exp(-1)]
|
||||
|
||||
assert convolution_fft([2321, 33123], [5321, 6321, 71323]) == \
|
||||
[12350041, 190918524, 374911166, 2362431729]
|
||||
|
||||
assert convolution_fft([312313, 31278232], [32139631, 319631]) == \
|
||||
[10037624576503, 1005370659728895, 9997492572392]
|
||||
|
||||
raises(TypeError, lambda: convolution_fft(x, y))
|
||||
raises(ValueError, lambda: convolution_fft([x, y], [y, x]))
|
||||
|
||||
|
||||
def test_convolution_ntt():
|
||||
# prime moduli of the form (m*2**k + 1), sequence length
|
||||
# should be a divisor of 2**k
|
||||
p = 7*17*2**23 + 1
|
||||
q = 19*2**10 + 1
|
||||
r = 2*500000003 + 1 # only for sequences of length 1 or 2
|
||||
# s = 2*3*5*7 # composite modulus
|
||||
|
||||
assert all(convolution_ntt([], x, prime=y) == [] for x in ([], [1]) for y in (p, q, r))
|
||||
assert convolution_ntt([2], [3], r) == [6]
|
||||
assert convolution_ntt([2, 3], [4], r) == [8, 12]
|
||||
|
||||
assert convolution_ntt([32121, 42144, 4214, 4241], [32132, 3232, 87242], p) == [33867619,
|
||||
459741727, 79180879, 831885249, 381344700, 369993322]
|
||||
assert convolution_ntt([121913, 3171831, 31888131, 12], [17882, 21292, 29921, 312], q) == \
|
||||
[8158, 3065, 3682, 7090, 1239, 2232, 3744]
|
||||
|
||||
assert convolution_ntt([12, 19, 21, 98, 67], [2, 6, 7, 8, 9], p) == \
|
||||
convolution_ntt([12, 19, 21, 98, 67], [2, 6, 7, 8, 9], q)
|
||||
assert convolution_ntt([12, 19, 21, 98, 67], [21, 76, 17, 78, 69], p) == \
|
||||
convolution_ntt([12, 19, 21, 98, 67], [21, 76, 17, 78, 69], q)
|
||||
|
||||
raises(ValueError, lambda: convolution_ntt([2, 3], [4, 5], r))
|
||||
raises(ValueError, lambda: convolution_ntt([x, y], [y, x], q))
|
||||
raises(TypeError, lambda: convolution_ntt(x, y, p))
|
||||
|
||||
|
||||
def test_convolution_fwht():
|
||||
assert convolution_fwht([], []) == []
|
||||
assert convolution_fwht([], [1]) == []
|
||||
assert convolution_fwht([1, 2, 3], [4, 5, 6]) == [32, 13, 18, 27]
|
||||
|
||||
assert convolution_fwht([Rational(5, 7), Rational(6, 8), Rational(7, 3)], [2, 4, Rational(6, 7)]) == \
|
||||
[Rational(45, 7), Rational(61, 14), Rational(776, 147), Rational(419, 42)]
|
||||
|
||||
a = [1, Rational(5, 3), sqrt(3), Rational(7, 5), 4 + 5*I]
|
||||
b = [94, 51, 53, 45, 31, 27, 13]
|
||||
c = [3 + 4*I, 5 + 7*I, 3, Rational(7, 6), 8]
|
||||
|
||||
assert convolution_fwht(a, b) == [53*sqrt(3) + 366 + 155*I,
|
||||
45*sqrt(3) + Rational(5848, 15) + 135*I,
|
||||
94*sqrt(3) + Rational(1257, 5) + 65*I,
|
||||
51*sqrt(3) + Rational(3974, 15),
|
||||
13*sqrt(3) + 452 + 470*I,
|
||||
Rational(4513, 15) + 255*I,
|
||||
31*sqrt(3) + Rational(1314, 5) + 265*I,
|
||||
27*sqrt(3) + Rational(3676, 15) + 225*I]
|
||||
|
||||
assert convolution_fwht(b, c) == [Rational(1993, 2) + 733*I, Rational(6215, 6) + 862*I,
|
||||
Rational(1659, 2) + 527*I, Rational(1988, 3) + 551*I, 1019 + 313*I, Rational(3955, 6) + 325*I,
|
||||
Rational(1175, 2) + 52*I, Rational(3253, 6) + 91*I]
|
||||
|
||||
assert convolution_fwht(a[3:], c) == [Rational(-54, 5) + I*293/5, -1 + I*204/5,
|
||||
Rational(133, 15) + I*35/6, Rational(409, 30) + 15*I, Rational(56, 5), 32 + 40*I, 0, 0]
|
||||
|
||||
u, v, w, x, y, z = symbols('u v w x y z')
|
||||
|
||||
assert convolution_fwht([u, v], [x, y]) == [u*x + v*y, u*y + v*x]
|
||||
|
||||
assert convolution_fwht([u, v, w], [x, y]) == \
|
||||
[u*x + v*y, u*y + v*x, w*x, w*y]
|
||||
|
||||
assert convolution_fwht([u, v, w], [x, y, z]) == \
|
||||
[u*x + v*y + w*z, u*y + v*x, u*z + w*x, v*z + w*y]
|
||||
|
||||
raises(TypeError, lambda: convolution_fwht(x, y))
|
||||
raises(TypeError, lambda: convolution_fwht(x*y, u + v))
|
||||
|
||||
|
||||
def test_convolution_subset():
|
||||
assert convolution_subset([], []) == []
|
||||
assert convolution_subset([], [Rational(1, 3)]) == []
|
||||
assert convolution_subset([6 + I*3/7], [Rational(2, 3)]) == [4 + I*2/7]
|
||||
|
||||
a = [1, Rational(5, 3), sqrt(3), 4 + 5*I]
|
||||
b = [64, 71, 55, 47, 33, 29, 15]
|
||||
c = [3 + I*2/3, 5 + 7*I, 7, Rational(7, 5), 9]
|
||||
|
||||
assert convolution_subset(a, b) == [64, Rational(533, 3), 55 + 64*sqrt(3),
|
||||
71*sqrt(3) + Rational(1184, 3) + 320*I, 33, 84,
|
||||
15 + 33*sqrt(3), 29*sqrt(3) + 157 + 165*I]
|
||||
|
||||
assert convolution_subset(b, c) == [192 + I*128/3, 533 + I*1486/3,
|
||||
613 + I*110/3, Rational(5013, 5) + I*1249/3,
|
||||
675 + 22*I, 891 + I*751/3,
|
||||
771 + 10*I, Rational(3736, 5) + 105*I]
|
||||
|
||||
assert convolution_subset(a, c) == convolution_subset(c, a)
|
||||
assert convolution_subset(a[:2], b) == \
|
||||
[64, Rational(533, 3), 55, Rational(416, 3), 33, 84, 15, 25]
|
||||
|
||||
assert convolution_subset(a[:2], c) == \
|
||||
[3 + I*2/3, 10 + I*73/9, 7, Rational(196, 15), 9, 15, 0, 0]
|
||||
|
||||
u, v, w, x, y, z = symbols('u v w x y z')
|
||||
|
||||
assert convolution_subset([u, v, w], [x, y]) == [u*x, u*y + v*x, w*x, w*y]
|
||||
assert convolution_subset([u, v, w, x], [y, z]) == \
|
||||
[u*y, u*z + v*y, w*y, w*z + x*y]
|
||||
|
||||
assert convolution_subset([u, v], [x, y, z]) == \
|
||||
convolution_subset([x, y, z], [u, v])
|
||||
|
||||
raises(TypeError, lambda: convolution_subset(x, z))
|
||||
raises(TypeError, lambda: convolution_subset(Rational(7, 3), u))
|
||||
|
||||
|
||||
def test_covering_product():
|
||||
assert covering_product([], []) == []
|
||||
assert covering_product([], [Rational(1, 3)]) == []
|
||||
assert covering_product([6 + I*3/7], [Rational(2, 3)]) == [4 + I*2/7]
|
||||
|
||||
a = [1, Rational(5, 8), sqrt(7), 4 + 9*I]
|
||||
b = [66, 81, 95, 49, 37, 89, 17]
|
||||
c = [3 + I*2/3, 51 + 72*I, 7, Rational(7, 15), 91]
|
||||
|
||||
assert covering_product(a, b) == [66, Rational(1383, 8), 95 + 161*sqrt(7),
|
||||
130*sqrt(7) + 1303 + 2619*I, 37,
|
||||
Rational(671, 4), 17 + 54*sqrt(7),
|
||||
89*sqrt(7) + Rational(4661, 8) + 1287*I]
|
||||
|
||||
assert covering_product(b, c) == [198 + 44*I, 7740 + 10638*I,
|
||||
1412 + I*190/3, Rational(42684, 5) + I*31202/3,
|
||||
9484 + I*74/3, 22163 + I*27394/3,
|
||||
10621 + I*34/3, Rational(90236, 15) + 1224*I]
|
||||
|
||||
assert covering_product(a, c) == covering_product(c, a)
|
||||
assert covering_product(b, c[:-1]) == [198 + 44*I, 7740 + 10638*I,
|
||||
1412 + I*190/3, Rational(42684, 5) + I*31202/3,
|
||||
111 + I*74/3, 6693 + I*27394/3,
|
||||
429 + I*34/3, Rational(23351, 15) + 1224*I]
|
||||
|
||||
assert covering_product(a, c[:-1]) == [3 + I*2/3,
|
||||
Rational(339, 4) + I*1409/12, 7 + 10*sqrt(7) + 2*sqrt(7)*I/3,
|
||||
-403 + 772*sqrt(7)/15 + 72*sqrt(7)*I + I*12658/15]
|
||||
|
||||
u, v, w, x, y, z = symbols('u v w x y z')
|
||||
|
||||
assert covering_product([u, v, w], [x, y]) == \
|
||||
[u*x, u*y + v*x + v*y, w*x, w*y]
|
||||
|
||||
assert covering_product([u, v, w, x], [y, z]) == \
|
||||
[u*y, u*z + v*y + v*z, w*y, w*z + x*y + x*z]
|
||||
|
||||
assert covering_product([u, v], [x, y, z]) == \
|
||||
covering_product([x, y, z], [u, v])
|
||||
|
||||
raises(TypeError, lambda: covering_product(x, z))
|
||||
raises(TypeError, lambda: covering_product(Rational(7, 3), u))
|
||||
|
||||
|
||||
def test_intersecting_product():
|
||||
assert intersecting_product([], []) == []
|
||||
assert intersecting_product([], [Rational(1, 3)]) == []
|
||||
assert intersecting_product([6 + I*3/7], [Rational(2, 3)]) == [4 + I*2/7]
|
||||
|
||||
a = [1, sqrt(5), Rational(3, 8) + 5*I, 4 + 7*I]
|
||||
b = [67, 51, 65, 48, 36, 79, 27]
|
||||
c = [3 + I*2/5, 5 + 9*I, 7, Rational(7, 19), 13]
|
||||
|
||||
assert intersecting_product(a, b) == [195*sqrt(5) + Rational(6979, 8) + 1886*I,
|
||||
178*sqrt(5) + 520 + 910*I, Rational(841, 2) + 1344*I,
|
||||
192 + 336*I, 0, 0, 0, 0]
|
||||
|
||||
assert intersecting_product(b, c) == [Rational(128553, 19) + I*9521/5,
|
||||
Rational(17820, 19) + 1602*I, Rational(19264, 19), Rational(336, 19), 1846, 0, 0, 0]
|
||||
|
||||
assert intersecting_product(a, c) == intersecting_product(c, a)
|
||||
assert intersecting_product(b[1:], c[:-1]) == [Rational(64788, 19) + I*8622/5,
|
||||
Rational(12804, 19) + 1152*I, Rational(11508, 19), Rational(252, 19), 0, 0, 0, 0]
|
||||
|
||||
assert intersecting_product(a, c[:-2]) == \
|
||||
[Rational(-99, 5) + 10*sqrt(5) + 2*sqrt(5)*I/5 + I*3021/40,
|
||||
-43 + 5*sqrt(5) + 9*sqrt(5)*I + 71*I, Rational(245, 8) + 84*I, 0]
|
||||
|
||||
u, v, w, x, y, z = symbols('u v w x y z')
|
||||
|
||||
assert intersecting_product([u, v, w], [x, y]) == \
|
||||
[u*x + u*y + v*x + w*x + w*y, v*y, 0, 0]
|
||||
|
||||
assert intersecting_product([u, v, w, x], [y, z]) == \
|
||||
[u*y + u*z + v*y + w*y + w*z + x*y, v*z + x*z, 0, 0]
|
||||
|
||||
assert intersecting_product([u, v], [x, y, z]) == \
|
||||
intersecting_product([x, y, z], [u, v])
|
||||
|
||||
raises(TypeError, lambda: intersecting_product(x, z))
|
||||
raises(TypeError, lambda: intersecting_product(u, Rational(8, 3)))
|
||||
|
||||
|
||||
def test_convolution_int():
|
||||
assert convolution_int([1], [1]) == [1]
|
||||
assert convolution_int([1, 1], [0]) == [0]
|
||||
assert convolution_int([1, 2, 3], [4, 5, 6]) == [4, 13, 28, 27, 18]
|
||||
assert convolution_int([1], [5, 6, 7]) == [5, 6, 7]
|
||||
assert convolution_int([1, 3], [5, 6, 7]) == [5, 21, 25, 21]
|
||||
assert convolution_int([10, -5, 1, 3], [-5, 6, 7]) == [-50, 85, 35, -44, 25, 21]
|
||||
assert convolution_int([0, 1, 0, -1], [1, 0, -1, 0]) == [0, 1, 0, -2, 0, 1]
|
||||
assert convolution_int(
|
||||
[-341, -5, 1, 3, -71, -99, 43, 87],
|
||||
[5, 6, 7, 12, 345, 21, -78, -7, -89]
|
||||
) == [-1705, -2071, -2412, -4106, -118035, -9774, 25998, 2981, 5509,
|
||||
-34317, 19228, 38870, 5485, 1724, -4436, -7743]
|
||||
@@ -0,0 +1,59 @@
|
||||
from sympy.core.numbers import Rational
|
||||
from sympy.functions.combinatorial.numbers import fibonacci
|
||||
from sympy.core import S, symbols
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.discrete.recurrences import linrec
|
||||
|
||||
def test_linrec():
|
||||
assert linrec(coeffs=[1, 1], init=[1, 1], n=20) == 10946
|
||||
assert linrec(coeffs=[1, 2, 3, 4, 5], init=[1, 1, 0, 2], n=10) == 1040
|
||||
assert linrec(coeffs=[0, 0, 11, 13], init=[23, 27], n=25) == 59628567384
|
||||
assert linrec(coeffs=[0, 0, 1, 1, 2], init=[1, 5, 3], n=15) == 165
|
||||
assert linrec(coeffs=[11, 13, 15, 17], init=[1, 2, 3, 4], n=70) == \
|
||||
56889923441670659718376223533331214868804815612050381493741233489928913241
|
||||
assert linrec(coeffs=[0]*55 + [1, 1, 2, 3], init=[0]*50 + [1, 2, 3], n=4000) == \
|
||||
702633573874937994980598979769135096432444135301118916539
|
||||
|
||||
assert linrec(coeffs=[11, 13, 15, 17], init=[1, 2, 3, 4], n=10**4)
|
||||
assert linrec(coeffs=[11, 13, 15, 17], init=[1, 2, 3, 4], n=10**5)
|
||||
|
||||
assert all(linrec(coeffs=[1, 1], init=[0, 1], n=n) == fibonacci(n)
|
||||
for n in range(95, 115))
|
||||
|
||||
assert all(linrec(coeffs=[1, 1], init=[1, 1], n=n) == fibonacci(n + 1)
|
||||
for n in range(595, 615))
|
||||
|
||||
a = [S.Half, Rational(3, 4), Rational(5, 6), 7, Rational(8, 9), Rational(3, 5)]
|
||||
b = [1, 2, 8, Rational(5, 7), Rational(3, 7), Rational(2, 9), 6]
|
||||
x, y, z = symbols('x y z')
|
||||
|
||||
assert linrec(coeffs=a[:5], init=b[:4], n=80) == \
|
||||
Rational(1726244235456268979436592226626304376013002142588105090705187189,
|
||||
1960143456748895967474334873705475211264)
|
||||
|
||||
assert linrec(coeffs=a[:4], init=b[:4], n=50) == \
|
||||
Rational(368949940033050147080268092104304441, 504857282956046106624)
|
||||
|
||||
assert linrec(coeffs=a[3:], init=b[:3], n=35) == \
|
||||
Rational(97409272177295731943657945116791049305244422833125109,
|
||||
814315512679031689453125)
|
||||
|
||||
assert linrec(coeffs=[0]*60 + [Rational(2, 3), Rational(4, 5)], init=b, n=3000) == \
|
||||
Rational(26777668739896791448594650497024, 48084516708184142230517578125)
|
||||
|
||||
raises(TypeError, lambda: linrec(coeffs=[11, 13, 15, 17], init=[1, 2, 3, 4, 5], n=1))
|
||||
raises(TypeError, lambda: linrec(coeffs=a[:4], init=b[:5], n=10000))
|
||||
raises(ValueError, lambda: linrec(coeffs=a[:4], init=b[:4], n=-10000))
|
||||
raises(TypeError, lambda: linrec(x, b, n=10000))
|
||||
raises(TypeError, lambda: linrec(a, y, n=10000))
|
||||
|
||||
assert linrec(coeffs=[x, y, z], init=[1, 1, 1], n=4) == \
|
||||
x**2 + x*y + x*z + y + z
|
||||
assert linrec(coeffs=[1, 2, 1], init=[x, y, z], n=20) == \
|
||||
269542*x + 664575*y + 578949*z
|
||||
assert linrec(coeffs=[0, 3, 1, 2], init=[x, y], n=30) == \
|
||||
58516436*x + 56372788*y
|
||||
assert linrec(coeffs=[0]*50 + [1, 2, 3], init=[x, y, z], n=1000) == \
|
||||
11477135884896*x + 25999077948732*y + 41975630244216*z
|
||||
assert linrec(coeffs=[], init=[1, 1], n=20) == 0
|
||||
assert linrec(coeffs=[x, y, z], init=[1, 2, 3], n=2) == 3
|
||||
@@ -0,0 +1,154 @@
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.core import S, Symbol, symbols, I, Rational
|
||||
from sympy.discrete import (fft, ifft, ntt, intt, fwht, ifwht,
|
||||
mobius_transform, inverse_mobius_transform)
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
def test_fft_ifft():
|
||||
assert all(tf(ls) == ls for tf in (fft, ifft)
|
||||
for ls in ([], [Rational(5, 3)]))
|
||||
|
||||
ls = list(range(6))
|
||||
fls = [15, -7*sqrt(2)/2 - 4 - sqrt(2)*I/2 + 2*I, 2 + 3*I,
|
||||
-4 + 7*sqrt(2)/2 - 2*I - sqrt(2)*I/2, -3,
|
||||
-4 + 7*sqrt(2)/2 + sqrt(2)*I/2 + 2*I,
|
||||
2 - 3*I, -7*sqrt(2)/2 - 4 - 2*I + sqrt(2)*I/2]
|
||||
|
||||
assert fft(ls) == fls
|
||||
assert ifft(fls) == ls + [S.Zero]*2
|
||||
|
||||
ls = [1 + 2*I, 3 + 4*I, 5 + 6*I]
|
||||
ifls = [Rational(9, 4) + 3*I, I*Rational(-7, 4), Rational(3, 4) + I, -2 - I/4]
|
||||
|
||||
assert ifft(ls) == ifls
|
||||
assert fft(ifls) == ls + [S.Zero]
|
||||
|
||||
x = Symbol('x', real=True)
|
||||
raises(TypeError, lambda: fft(x))
|
||||
raises(ValueError, lambda: ifft([x, 2*x, 3*x**2, 4*x**3]))
|
||||
|
||||
|
||||
def test_ntt_intt():
|
||||
# prime moduli of the form (m*2**k + 1), sequence length
|
||||
# should be a divisor of 2**k
|
||||
p = 7*17*2**23 + 1
|
||||
q = 2*500000003 + 1 # only for sequences of length 1 or 2
|
||||
r = 2*3*5*7 # composite modulus
|
||||
|
||||
assert all(tf(ls, p) == ls for tf in (ntt, intt)
|
||||
for ls in ([], [5]))
|
||||
|
||||
ls = list(range(6))
|
||||
nls = [15, 801133602, 738493201, 334102277, 998244350, 849020224,
|
||||
259751156, 12232587]
|
||||
|
||||
assert ntt(ls, p) == nls
|
||||
assert intt(nls, p) == ls + [0]*2
|
||||
|
||||
ls = [1 + 2*I, 3 + 4*I, 5 + 6*I]
|
||||
x = Symbol('x', integer=True)
|
||||
|
||||
raises(TypeError, lambda: ntt(x, p))
|
||||
raises(ValueError, lambda: intt([x, 2*x, 3*x**2, 4*x**3], p))
|
||||
raises(ValueError, lambda: intt(ls, p))
|
||||
raises(ValueError, lambda: ntt([1.2, 2.1, 3.5], p))
|
||||
raises(ValueError, lambda: ntt([3, 5, 6], q))
|
||||
raises(ValueError, lambda: ntt([4, 5, 7], r))
|
||||
raises(ValueError, lambda: ntt([1.0, 2.0, 3.0], p))
|
||||
|
||||
|
||||
def test_fwht_ifwht():
|
||||
assert all(tf(ls) == ls for tf in (fwht, ifwht) \
|
||||
for ls in ([], [Rational(7, 4)]))
|
||||
|
||||
ls = [213, 321, 43235, 5325, 312, 53]
|
||||
fls = [49459, 38061, -47661, -37759, 48729, 37543, -48391, -38277]
|
||||
|
||||
assert fwht(ls) == fls
|
||||
assert ifwht(fls) == ls + [S.Zero]*2
|
||||
|
||||
ls = [S.Half + 2*I, Rational(3, 7) + 4*I, Rational(5, 6) + 6*I, Rational(7, 3), Rational(9, 4)]
|
||||
ifls = [Rational(533, 672) + I*3/2, Rational(23, 224) + I/2, Rational(1, 672), Rational(107, 224) - I,
|
||||
Rational(155, 672) + I*3/2, Rational(-103, 224) + I/2, Rational(-377, 672), Rational(-19, 224) - I]
|
||||
|
||||
assert ifwht(ls) == ifls
|
||||
assert fwht(ifls) == ls + [S.Zero]*3
|
||||
|
||||
x, y = symbols('x y')
|
||||
|
||||
raises(TypeError, lambda: fwht(x))
|
||||
|
||||
ls = [x, 2*x, 3*x**2, 4*x**3]
|
||||
ifls = [x**3 + 3*x**2/4 + x*Rational(3, 4),
|
||||
-x**3 + 3*x**2/4 - x/4,
|
||||
-x**3 - 3*x**2/4 + x*Rational(3, 4),
|
||||
x**3 - 3*x**2/4 - x/4]
|
||||
|
||||
assert ifwht(ls) == ifls
|
||||
assert fwht(ifls) == ls
|
||||
|
||||
ls = [x, y, x**2, y**2, x*y]
|
||||
fls = [x**2 + x*y + x + y**2 + y,
|
||||
x**2 + x*y + x - y**2 - y,
|
||||
-x**2 + x*y + x - y**2 + y,
|
||||
-x**2 + x*y + x + y**2 - y,
|
||||
x**2 - x*y + x + y**2 + y,
|
||||
x**2 - x*y + x - y**2 - y,
|
||||
-x**2 - x*y + x - y**2 + y,
|
||||
-x**2 - x*y + x + y**2 - y]
|
||||
|
||||
assert fwht(ls) == fls
|
||||
assert ifwht(fls) == ls + [S.Zero]*3
|
||||
|
||||
ls = list(range(6))
|
||||
|
||||
assert fwht(ls) == [x*8 for x in ifwht(ls)]
|
||||
|
||||
|
||||
def test_mobius_transform():
|
||||
assert all(tf(ls, subset=subset) == ls
|
||||
for ls in ([], [Rational(7, 4)]) for subset in (True, False)
|
||||
for tf in (mobius_transform, inverse_mobius_transform))
|
||||
|
||||
w, x, y, z = symbols('w x y z')
|
||||
|
||||
assert mobius_transform([x, y]) == [x, x + y]
|
||||
assert inverse_mobius_transform([x, x + y]) == [x, y]
|
||||
assert mobius_transform([x, y], subset=False) == [x + y, y]
|
||||
assert inverse_mobius_transform([x + y, y], subset=False) == [x, y]
|
||||
|
||||
assert mobius_transform([w, x, y, z]) == [w, w + x, w + y, w + x + y + z]
|
||||
assert inverse_mobius_transform([w, w + x, w + y, w + x + y + z]) == \
|
||||
[w, x, y, z]
|
||||
assert mobius_transform([w, x, y, z], subset=False) == \
|
||||
[w + x + y + z, x + z, y + z, z]
|
||||
assert inverse_mobius_transform([w + x + y + z, x + z, y + z, z], subset=False) == \
|
||||
[w, x, y, z]
|
||||
|
||||
ls = [Rational(2, 3), Rational(6, 7), Rational(5, 8), 9, Rational(5, 3) + 7*I]
|
||||
mls = [Rational(2, 3), Rational(32, 21), Rational(31, 24), Rational(1873, 168),
|
||||
Rational(7, 3) + 7*I, Rational(67, 21) + 7*I, Rational(71, 24) + 7*I,
|
||||
Rational(2153, 168) + 7*I]
|
||||
|
||||
assert mobius_transform(ls) == mls
|
||||
assert inverse_mobius_transform(mls) == ls + [S.Zero]*3
|
||||
|
||||
mls = [Rational(2153, 168) + 7*I, Rational(69, 7), Rational(77, 8), 9, Rational(5, 3) + 7*I, 0, 0, 0]
|
||||
|
||||
assert mobius_transform(ls, subset=False) == mls
|
||||
assert inverse_mobius_transform(mls, subset=False) == ls + [S.Zero]*3
|
||||
|
||||
ls = ls[:-1]
|
||||
mls = [Rational(2, 3), Rational(32, 21), Rational(31, 24), Rational(1873, 168)]
|
||||
|
||||
assert mobius_transform(ls) == mls
|
||||
assert inverse_mobius_transform(mls) == ls
|
||||
|
||||
mls = [Rational(1873, 168), Rational(69, 7), Rational(77, 8), 9]
|
||||
|
||||
assert mobius_transform(ls, subset=False) == mls
|
||||
assert inverse_mobius_transform(mls, subset=False) == ls
|
||||
|
||||
raises(TypeError, lambda: mobius_transform(x, subset=True))
|
||||
raises(TypeError, lambda: inverse_mobius_transform(y, subset=False))
|
||||
@@ -0,0 +1,425 @@
|
||||
"""
|
||||
Discrete Fourier Transform, Number Theoretic Transform,
|
||||
Walsh Hadamard Transform, Mobius Transform
|
||||
"""
|
||||
|
||||
from sympy.core import S, Symbol, sympify
|
||||
from sympy.core.function import expand_mul
|
||||
from sympy.core.numbers import pi, I
|
||||
from sympy.functions.elementary.trigonometric import sin, cos
|
||||
from sympy.ntheory import isprime, primitive_root
|
||||
from sympy.utilities.iterables import ibin, iterable
|
||||
from sympy.utilities.misc import as_int
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Discrete Fourier Transform #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def _fourier_transform(seq, dps, inverse=False):
|
||||
"""Utility function for the Discrete Fourier Transform"""
|
||||
|
||||
if not iterable(seq):
|
||||
raise TypeError("Expected a sequence of numeric coefficients "
|
||||
"for Fourier Transform")
|
||||
|
||||
a = [sympify(arg) for arg in seq]
|
||||
if any(x.has(Symbol) for x in a):
|
||||
raise ValueError("Expected non-symbolic coefficients")
|
||||
|
||||
n = len(a)
|
||||
if n < 2:
|
||||
return a
|
||||
|
||||
b = n.bit_length() - 1
|
||||
if n&(n - 1): # not a power of 2
|
||||
b += 1
|
||||
n = 2**b
|
||||
|
||||
a += [S.Zero]*(n - len(a))
|
||||
for i in range(1, n):
|
||||
j = int(ibin(i, b, str=True)[::-1], 2)
|
||||
if i < j:
|
||||
a[i], a[j] = a[j], a[i]
|
||||
|
||||
ang = -2*pi/n if inverse else 2*pi/n
|
||||
|
||||
if dps is not None:
|
||||
ang = ang.evalf(dps + 2)
|
||||
|
||||
w = [cos(ang*i) + I*sin(ang*i) for i in range(n // 2)]
|
||||
|
||||
h = 2
|
||||
while h <= n:
|
||||
hf, ut = h // 2, n // h
|
||||
for i in range(0, n, h):
|
||||
for j in range(hf):
|
||||
u, v = a[i + j], expand_mul(a[i + j + hf]*w[ut * j])
|
||||
a[i + j], a[i + j + hf] = u + v, u - v
|
||||
h *= 2
|
||||
|
||||
if inverse:
|
||||
a = [(x/n).evalf(dps) for x in a] if dps is not None \
|
||||
else [x/n for x in a]
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def fft(seq, dps=None):
|
||||
r"""
|
||||
Performs the Discrete Fourier Transform (**DFT**) in the complex domain.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
*radix-2 FFT* requires the number of sample points to be a power of 2.
|
||||
|
||||
This method should be used with default arguments only for short sequences
|
||||
as the complexity of expressions increases with the size of the sequence.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : iterable
|
||||
The sequence on which **DFT** is to be applied.
|
||||
dps : Integer
|
||||
Specifies the number of decimal digits for precision.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fft, ifft
|
||||
|
||||
>>> fft([1, 2, 3, 4])
|
||||
[10, -2 - 2*I, -2, -2 + 2*I]
|
||||
>>> ifft(_)
|
||||
[1, 2, 3, 4]
|
||||
|
||||
>>> ifft([1, 2, 3, 4])
|
||||
[5/2, -1/2 + I/2, -1/2, -1/2 - I/2]
|
||||
>>> fft(_)
|
||||
[1, 2, 3, 4]
|
||||
|
||||
>>> ifft([1, 7, 3, 4], dps=15)
|
||||
[3.75, -0.5 - 0.75*I, -1.75, -0.5 + 0.75*I]
|
||||
>>> fft(_)
|
||||
[1.0, 7.0, 3.0, 4.0]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
|
||||
.. [2] https://mathworld.wolfram.com/FastFourierTransform.html
|
||||
|
||||
"""
|
||||
|
||||
return _fourier_transform(seq, dps=dps)
|
||||
|
||||
|
||||
def ifft(seq, dps=None):
|
||||
return _fourier_transform(seq, dps=dps, inverse=True)
|
||||
|
||||
ifft.__doc__ = fft.__doc__
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Number Theoretic Transform #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def _number_theoretic_transform(seq, prime, inverse=False):
|
||||
"""Utility function for the Number Theoretic Transform"""
|
||||
|
||||
if not iterable(seq):
|
||||
raise TypeError("Expected a sequence of integer coefficients "
|
||||
"for Number Theoretic Transform")
|
||||
|
||||
p = as_int(prime)
|
||||
if not isprime(p):
|
||||
raise ValueError("Expected prime modulus for "
|
||||
"Number Theoretic Transform")
|
||||
|
||||
a = [as_int(x) % p for x in seq]
|
||||
|
||||
n = len(a)
|
||||
if n < 1:
|
||||
return a
|
||||
|
||||
b = n.bit_length() - 1
|
||||
if n&(n - 1):
|
||||
b += 1
|
||||
n = 2**b
|
||||
|
||||
if (p - 1) % n:
|
||||
raise ValueError("Expected prime modulus of the form (m*2**k + 1)")
|
||||
|
||||
a += [0]*(n - len(a))
|
||||
for i in range(1, n):
|
||||
j = int(ibin(i, b, str=True)[::-1], 2)
|
||||
if i < j:
|
||||
a[i], a[j] = a[j], a[i]
|
||||
|
||||
pr = primitive_root(p)
|
||||
|
||||
rt = pow(pr, (p - 1) // n, p)
|
||||
if inverse:
|
||||
rt = pow(rt, p - 2, p)
|
||||
|
||||
w = [1]*(n // 2)
|
||||
for i in range(1, n // 2):
|
||||
w[i] = w[i - 1]*rt % p
|
||||
|
||||
h = 2
|
||||
while h <= n:
|
||||
hf, ut = h // 2, n // h
|
||||
for i in range(0, n, h):
|
||||
for j in range(hf):
|
||||
u, v = a[i + j], a[i + j + hf]*w[ut * j]
|
||||
a[i + j], a[i + j + hf] = (u + v) % p, (u - v) % p
|
||||
h *= 2
|
||||
|
||||
if inverse:
|
||||
rv = pow(n, p - 2, p)
|
||||
a = [x*rv % p for x in a]
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def ntt(seq, prime):
|
||||
r"""
|
||||
Performs the Number Theoretic Transform (**NTT**), which specializes the
|
||||
Discrete Fourier Transform (**DFT**) over quotient ring `Z/pZ` for prime
|
||||
`p` instead of complex numbers `C`.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
*radix-2 NTT* requires the number of sample points to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : iterable
|
||||
The sequence on which **DFT** is to be applied.
|
||||
prime : Integer
|
||||
Prime modulus of the form `(m 2^k + 1)` to be used for performing
|
||||
**NTT** on the sequence.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import ntt, intt
|
||||
>>> ntt([1, 2, 3, 4], prime=3*2**8 + 1)
|
||||
[10, 643, 767, 122]
|
||||
>>> intt(_, 3*2**8 + 1)
|
||||
[1, 2, 3, 4]
|
||||
>>> intt([1, 2, 3, 4], prime=3*2**8 + 1)
|
||||
[387, 415, 384, 353]
|
||||
>>> ntt(_, prime=3*2**8 + 1)
|
||||
[1, 2, 3, 4]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] http://www.apfloat.org/ntt.html
|
||||
.. [2] https://mathworld.wolfram.com/NumberTheoreticTransform.html
|
||||
.. [3] https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
|
||||
|
||||
"""
|
||||
|
||||
return _number_theoretic_transform(seq, prime=prime)
|
||||
|
||||
|
||||
def intt(seq, prime):
|
||||
return _number_theoretic_transform(seq, prime=prime, inverse=True)
|
||||
|
||||
intt.__doc__ = ntt.__doc__
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Walsh Hadamard Transform #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def _walsh_hadamard_transform(seq, inverse=False):
|
||||
"""Utility function for the Walsh Hadamard Transform"""
|
||||
|
||||
if not iterable(seq):
|
||||
raise TypeError("Expected a sequence of coefficients "
|
||||
"for Walsh Hadamard Transform")
|
||||
|
||||
a = [sympify(arg) for arg in seq]
|
||||
n = len(a)
|
||||
if n < 2:
|
||||
return a
|
||||
|
||||
if n&(n - 1):
|
||||
n = 2**n.bit_length()
|
||||
|
||||
a += [S.Zero]*(n - len(a))
|
||||
h = 2
|
||||
while h <= n:
|
||||
hf = h // 2
|
||||
for i in range(0, n, h):
|
||||
for j in range(hf):
|
||||
u, v = a[i + j], a[i + j + hf]
|
||||
a[i + j], a[i + j + hf] = u + v, u - v
|
||||
h *= 2
|
||||
|
||||
if inverse:
|
||||
a = [x/n for x in a]
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def fwht(seq):
|
||||
r"""
|
||||
Performs the Walsh Hadamard Transform (**WHT**), and uses Hadamard
|
||||
ordering for the sequence.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
*radix-2 FWHT* requires the number of sample points to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : iterable
|
||||
The sequence on which WHT is to be applied.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import fwht, ifwht
|
||||
>>> fwht([4, 2, 2, 0, 0, 2, -2, 0])
|
||||
[8, 0, 8, 0, 8, 8, 0, 0]
|
||||
>>> ifwht(_)
|
||||
[4, 2, 2, 0, 0, 2, -2, 0]
|
||||
|
||||
>>> ifwht([19, -1, 11, -9, -7, 13, -15, 5])
|
||||
[2, 0, 4, 0, 3, 10, 0, 0]
|
||||
>>> fwht(_)
|
||||
[19, -1, 11, -9, -7, 13, -15, 5]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Hadamard_transform
|
||||
.. [2] https://en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
|
||||
|
||||
"""
|
||||
|
||||
return _walsh_hadamard_transform(seq)
|
||||
|
||||
|
||||
def ifwht(seq):
|
||||
return _walsh_hadamard_transform(seq, inverse=True)
|
||||
|
||||
ifwht.__doc__ = fwht.__doc__
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# #
|
||||
# Mobius Transform for Subset Lattice #
|
||||
# #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
def _mobius_transform(seq, sgn, subset):
|
||||
r"""Utility function for performing Mobius Transform using
|
||||
Yate's Dynamic Programming method"""
|
||||
|
||||
if not iterable(seq):
|
||||
raise TypeError("Expected a sequence of coefficients")
|
||||
|
||||
a = [sympify(arg) for arg in seq]
|
||||
|
||||
n = len(a)
|
||||
if n < 2:
|
||||
return a
|
||||
|
||||
if n&(n - 1):
|
||||
n = 2**n.bit_length()
|
||||
|
||||
a += [S.Zero]*(n - len(a))
|
||||
|
||||
if subset:
|
||||
i = 1
|
||||
while i < n:
|
||||
for j in range(n):
|
||||
if j & i:
|
||||
a[j] += sgn*a[j ^ i]
|
||||
i *= 2
|
||||
|
||||
else:
|
||||
i = 1
|
||||
while i < n:
|
||||
for j in range(n):
|
||||
if j & i:
|
||||
continue
|
||||
a[j] += sgn*a[j ^ i]
|
||||
i *= 2
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def mobius_transform(seq, subset=True):
|
||||
r"""
|
||||
Performs the Mobius Transform for subset lattice with indices of
|
||||
sequence as bitmasks.
|
||||
|
||||
The indices of each argument, considered as bit strings, correspond
|
||||
to subsets of a finite set.
|
||||
|
||||
The sequence is automatically padded to the right with zeros, as the
|
||||
definition of subset/superset based on bitmasks (indices) requires
|
||||
the size of sequence to be a power of 2.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
seq : iterable
|
||||
The sequence on which Mobius Transform is to be applied.
|
||||
subset : bool
|
||||
Specifies if Mobius Transform is applied by enumerating subsets
|
||||
or supersets of the given set.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy import mobius_transform, inverse_mobius_transform
|
||||
>>> x, y, z = symbols('x y z')
|
||||
|
||||
>>> mobius_transform([x, y, z])
|
||||
[x, x + y, x + z, x + y + z]
|
||||
>>> inverse_mobius_transform(_)
|
||||
[x, y, z, 0]
|
||||
|
||||
>>> mobius_transform([x, y, z], subset=False)
|
||||
[x + y + z, y, z, 0]
|
||||
>>> inverse_mobius_transform(_, subset=False)
|
||||
[x, y, z, 0]
|
||||
|
||||
>>> mobius_transform([1, 2, 3, 4])
|
||||
[1, 3, 4, 10]
|
||||
>>> inverse_mobius_transform(_)
|
||||
[1, 2, 3, 4]
|
||||
>>> mobius_transform([1, 2, 3, 4], subset=False)
|
||||
[10, 6, 7, 4]
|
||||
>>> inverse_mobius_transform(_, subset=False)
|
||||
[1, 2, 3, 4]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/M%C3%B6bius_inversion_formula
|
||||
.. [2] https://people.csail.mit.edu/rrw/presentations/subset-conv.pdf
|
||||
.. [3] https://arxiv.org/pdf/1211.0189.pdf
|
||||
|
||||
"""
|
||||
|
||||
return _mobius_transform(seq, sgn=+1, subset=subset)
|
||||
|
||||
def inverse_mobius_transform(seq, subset=True):
|
||||
return _mobius_transform(seq, sgn=-1, subset=subset)
|
||||
|
||||
inverse_mobius_transform.__doc__ = mobius_transform.__doc__
|
||||
Reference in New Issue
Block a user