123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- """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):
- # the base ring for the algebra
- self.base = base
- # the operator representing shift i.e. `Sn`
- 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):
- # the parent ring for this operator
- # must be an RecurrenceOperatorAlgebra object
- self.parent = parent
- # sequence of polynomials in n for each power of Sn
- # represents the operator
- # convert the expressions into ring elements using from_sympy
- 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
- # multiply a polynomial `b` with a list of polynomials
- def _mul_dmp_diffop(b, listofother):
- if isinstance(listofother, list):
- sol = []
- for i in listofother:
- sol.append(i * b)
- return sol
- else:
- return [b * listofother]
- sol = _mul_dmp_diffop(listofself[0], listofother)
- # compute Sn^i * b
- 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)):
- # find Sn^i * b in ith iteration
- listofother = _mul_Sni_b(listofother)
- # solution = solution + listofself[i] * (Sn^i * b)
- 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 = []
- for j in self.listofpoly:
- sol.append(other * j)
- 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 = []
- sol.append(list_self[0] + list_other[0])
- sol += 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
- if n == 0:
- return RecurrenceOperator([self.parent.base.one], self.parent)
- # if self is `Sn`
- if self.listofpoly == self.parent.shift_operator.listofpoly:
- sol = []
- for i in range(0, n):
- sol.append(self.parent.base.zero)
- sol.append(self.parent.base.one)
- return RecurrenceOperator(sol, self.parent)
- else:
- if n % 2 == 1:
- powreduce = self**(n - 1)
- return powreduce * self
- elif n % 2 == 0:
- powreduce = self**(n / 2)
- return powreduce * powreduce
- def __str__(self):
- listofpoly = self.listofpoly
- print_str = ''
- for i, j in enumerate(listofpoly):
- if j == self.parent.base.zero:
- continue
- 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
- else:
- if self.listofpoly[0] == other:
- for i in self.listofpoly[1:]:
- if i is not self.parent.base.zero:
- return False
- return True
- else:
- return False
- 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:
- if self.n == other.n:
- if self._have_init_cond and other._have_init_cond:
- if self.u0 == other.u0:
- return True
- else:
- return False
- else:
- return True
- else:
- return False
- else:
- return False
|