|
"""Recurrence Operators""" |
|
|
|
from sympy.core.singleton import S |
|
from sympy.core.symbol import (Symbol, symbols) |
|
from sympy.printing import sstr |
|
from sympy.core.sympify import sympify |
|
|
|
|
|
def RecurrenceOperators(base, generator): |
|
""" |
|
Returns an Algebra of Recurrence Operators and the operator for |
|
shifting i.e. the `Sn` operator. |
|
The first argument needs to be the base polynomial ring for the algebra |
|
and the second argument must be a generator which can be either a |
|
noncommutative Symbol or a string. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import ZZ |
|
>>> from sympy import symbols |
|
>>> from sympy.holonomic.recurrence import RecurrenceOperators |
|
>>> n = symbols('n', integer=True) |
|
>>> R, Sn = RecurrenceOperators(ZZ.old_poly_ring(n), 'Sn') |
|
""" |
|
|
|
ring = RecurrenceOperatorAlgebra(base, generator) |
|
return (ring, ring.shift_operator) |
|
|
|
|
|
class RecurrenceOperatorAlgebra: |
|
""" |
|
A Recurrence Operator Algebra is a set of noncommutative polynomials |
|
in intermediate `Sn` and coefficients in a base ring A. It follows the |
|
commutation rule: |
|
Sn * a(n) = a(n + 1) * Sn |
|
|
|
This class represents a Recurrence Operator Algebra and serves as the parent ring |
|
for Recurrence Operators. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import ZZ |
|
>>> from sympy import symbols |
|
>>> from sympy.holonomic.recurrence import RecurrenceOperators |
|
>>> n = symbols('n', integer=True) |
|
>>> R, Sn = RecurrenceOperators(ZZ.old_poly_ring(n), 'Sn') |
|
>>> R |
|
Univariate Recurrence Operator Algebra in intermediate Sn over the base ring |
|
ZZ[n] |
|
|
|
See Also |
|
======== |
|
|
|
RecurrenceOperator |
|
""" |
|
|
|
def __init__(self, base, generator): |
|
|
|
self.base = base |
|
|
|
self.shift_operator = RecurrenceOperator( |
|
[base.zero, base.one], self) |
|
|
|
if generator is None: |
|
self.gen_symbol = symbols('Sn', commutative=False) |
|
else: |
|
if isinstance(generator, str): |
|
self.gen_symbol = symbols(generator, commutative=False) |
|
elif isinstance(generator, Symbol): |
|
self.gen_symbol = generator |
|
|
|
def __str__(self): |
|
string = 'Univariate Recurrence Operator Algebra in intermediate '\ |
|
+ sstr(self.gen_symbol) + ' over the base ring ' + \ |
|
(self.base).__str__() |
|
|
|
return string |
|
|
|
__repr__ = __str__ |
|
|
|
def __eq__(self, other): |
|
if self.base == other.base and self.gen_symbol == other.gen_symbol: |
|
return True |
|
else: |
|
return False |
|
|
|
|
|
def _add_lists(list1, list2): |
|
if len(list1) <= len(list2): |
|
sol = [a + b for a, b in zip(list1, list2)] + list2[len(list1):] |
|
else: |
|
sol = [a + b for a, b in zip(list1, list2)] + list1[len(list2):] |
|
return sol |
|
|
|
|
|
class RecurrenceOperator: |
|
""" |
|
The Recurrence Operators are defined by a list of polynomials |
|
in the base ring and the parent ring of the Operator. |
|
|
|
Explanation |
|
=========== |
|
|
|
Takes a list of polynomials for each power of Sn and the |
|
parent ring which must be an instance of RecurrenceOperatorAlgebra. |
|
|
|
A Recurrence Operator can be created easily using |
|
the operator `Sn`. See examples below. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.holonomic.recurrence import RecurrenceOperator, RecurrenceOperators |
|
>>> from sympy import ZZ |
|
>>> from sympy import symbols |
|
>>> n = symbols('n', integer=True) |
|
>>> R, Sn = RecurrenceOperators(ZZ.old_poly_ring(n),'Sn') |
|
|
|
>>> RecurrenceOperator([0, 1, n**2], R) |
|
(1)Sn + (n**2)Sn**2 |
|
|
|
>>> Sn*n |
|
(n + 1)Sn |
|
|
|
>>> n*Sn*n + 1 - Sn**2*n |
|
(1) + (n**2 + n)Sn + (-n - 2)Sn**2 |
|
|
|
See Also |
|
======== |
|
|
|
DifferentialOperatorAlgebra |
|
""" |
|
|
|
_op_priority = 20 |
|
|
|
def __init__(self, list_of_poly, parent): |
|
|
|
|
|
self.parent = parent |
|
|
|
|
|
|
|
if isinstance(list_of_poly, list): |
|
for i, j in enumerate(list_of_poly): |
|
if isinstance(j, int): |
|
list_of_poly[i] = self.parent.base.from_sympy(S(j)) |
|
elif not isinstance(j, self.parent.base.dtype): |
|
list_of_poly[i] = self.parent.base.from_sympy(j) |
|
|
|
self.listofpoly = list_of_poly |
|
self.order = len(self.listofpoly) - 1 |
|
|
|
def __mul__(self, other): |
|
""" |
|
Multiplies two Operators and returns another |
|
RecurrenceOperator instance using the commutation rule |
|
Sn * a(n) = a(n + 1) * Sn |
|
""" |
|
|
|
listofself = self.listofpoly |
|
base = self.parent.base |
|
|
|
if not isinstance(other, RecurrenceOperator): |
|
if not isinstance(other, self.parent.base.dtype): |
|
listofother = [self.parent.base.from_sympy(sympify(other))] |
|
|
|
else: |
|
listofother = [other] |
|
else: |
|
listofother = other.listofpoly |
|
|
|
|
|
def _mul_dmp_diffop(b, listofother): |
|
if isinstance(listofother, list): |
|
return [i * b for i in listofother] |
|
return [b * listofother] |
|
|
|
sol = _mul_dmp_diffop(listofself[0], listofother) |
|
|
|
|
|
def _mul_Sni_b(b): |
|
sol = [base.zero] |
|
|
|
if isinstance(b, list): |
|
for i in b: |
|
j = base.to_sympy(i).subs(base.gens[0], base.gens[0] + S.One) |
|
sol.append(base.from_sympy(j)) |
|
|
|
else: |
|
j = b.subs(base.gens[0], base.gens[0] + S.One) |
|
sol.append(base.from_sympy(j)) |
|
|
|
return sol |
|
|
|
for i in range(1, len(listofself)): |
|
|
|
listofother = _mul_Sni_b(listofother) |
|
|
|
sol = _add_lists(sol, _mul_dmp_diffop(listofself[i], listofother)) |
|
|
|
return RecurrenceOperator(sol, self.parent) |
|
|
|
def __rmul__(self, other): |
|
if not isinstance(other, RecurrenceOperator): |
|
|
|
if isinstance(other, int): |
|
other = S(other) |
|
|
|
if not isinstance(other, self.parent.base.dtype): |
|
other = (self.parent.base).from_sympy(other) |
|
|
|
sol = [other * j for j in self.listofpoly] |
|
return RecurrenceOperator(sol, self.parent) |
|
|
|
def __add__(self, other): |
|
if isinstance(other, RecurrenceOperator): |
|
|
|
sol = _add_lists(self.listofpoly, other.listofpoly) |
|
return RecurrenceOperator(sol, self.parent) |
|
|
|
else: |
|
|
|
if isinstance(other, int): |
|
other = S(other) |
|
list_self = self.listofpoly |
|
if not isinstance(other, self.parent.base.dtype): |
|
list_other = [((self.parent).base).from_sympy(other)] |
|
else: |
|
list_other = [other] |
|
sol = [list_self[0] + list_other[0]] + list_self[1:] |
|
|
|
return RecurrenceOperator(sol, self.parent) |
|
|
|
__radd__ = __add__ |
|
|
|
def __sub__(self, other): |
|
return self + (-1) * other |
|
|
|
def __rsub__(self, other): |
|
return (-1) * self + other |
|
|
|
def __pow__(self, n): |
|
if n == 1: |
|
return self |
|
result = RecurrenceOperator([self.parent.base.one], self.parent) |
|
if n == 0: |
|
return result |
|
|
|
if self.listofpoly == self.parent.shift_operator.listofpoly: |
|
sol = [self.parent.base.zero] * n + [self.parent.base.one] |
|
return RecurrenceOperator(sol, self.parent) |
|
x = self |
|
while True: |
|
if n % 2: |
|
result *= x |
|
n >>= 1 |
|
if not n: |
|
break |
|
x *= x |
|
return result |
|
|
|
def __str__(self): |
|
listofpoly = self.listofpoly |
|
print_str = '' |
|
|
|
for i, j in enumerate(listofpoly): |
|
if j == self.parent.base.zero: |
|
continue |
|
|
|
j = self.parent.base.to_sympy(j) |
|
|
|
if i == 0: |
|
print_str += '(' + sstr(j) + ')' |
|
continue |
|
|
|
if print_str: |
|
print_str += ' + ' |
|
|
|
if i == 1: |
|
print_str += '(' + sstr(j) + ')Sn' |
|
continue |
|
|
|
print_str += '(' + sstr(j) + ')' + 'Sn**' + sstr(i) |
|
|
|
return print_str |
|
|
|
__repr__ = __str__ |
|
|
|
def __eq__(self, other): |
|
if isinstance(other, RecurrenceOperator): |
|
if self.listofpoly == other.listofpoly and self.parent == other.parent: |
|
return True |
|
else: |
|
return False |
|
return self.listofpoly[0] == other and \ |
|
all(i is self.parent.base.zero for i in self.listofpoly[1:]) |
|
|
|
|
|
class HolonomicSequence: |
|
""" |
|
A Holonomic Sequence is a type of sequence satisfying a linear homogeneous |
|
recurrence relation with Polynomial coefficients. Alternatively, A sequence |
|
is Holonomic if and only if its generating function is a Holonomic Function. |
|
""" |
|
|
|
def __init__(self, recurrence, u0=[]): |
|
self.recurrence = recurrence |
|
if not isinstance(u0, list): |
|
self.u0 = [u0] |
|
else: |
|
self.u0 = u0 |
|
|
|
if len(self.u0) == 0: |
|
self._have_init_cond = False |
|
else: |
|
self._have_init_cond = True |
|
self.n = recurrence.parent.base.gens[0] |
|
|
|
def __repr__(self): |
|
str_sol = 'HolonomicSequence(%s, %s)' % ((self.recurrence).__repr__(), sstr(self.n)) |
|
if not self._have_init_cond: |
|
return str_sol |
|
else: |
|
cond_str = '' |
|
seq_str = 0 |
|
for i in self.u0: |
|
cond_str += ', u(%s) = %s' % (sstr(seq_str), sstr(i)) |
|
seq_str += 1 |
|
|
|
sol = str_sol + cond_str |
|
return sol |
|
|
|
__str__ = __repr__ |
|
|
|
def __eq__(self, other): |
|
if self.recurrence != other.recurrence or self.n != other.n: |
|
return False |
|
if self._have_init_cond and other._have_init_cond: |
|
return self.u0 == other.u0 |
|
return True |
|
|