holonomic.py 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899
  1. """
  2. This module implements Holonomic Functions and
  3. various operations on them.
  4. """
  5. from sympy.core import Add, Mul, Pow
  6. from sympy.core.numbers import (NaN, Infinity, NegativeInfinity, Float, I, pi,
  7. equal_valued)
  8. from sympy.core.singleton import S
  9. from sympy.core.sorting import ordered
  10. from sympy.core.symbol import Dummy, Symbol
  11. from sympy.core.sympify import sympify
  12. from sympy.functions.combinatorial.factorials import binomial, factorial, rf
  13. from sympy.functions.elementary.exponential import exp_polar, exp, log
  14. from sympy.functions.elementary.hyperbolic import (cosh, sinh)
  15. from sympy.functions.elementary.miscellaneous import sqrt
  16. from sympy.functions.elementary.trigonometric import (cos, sin, sinc)
  17. from sympy.functions.special.error_functions import (Ci, Shi, Si, erf, erfc, erfi)
  18. from sympy.functions.special.gamma_functions import gamma
  19. from sympy.functions.special.hyper import hyper, meijerg
  20. from sympy.integrals import meijerint
  21. from sympy.matrices import Matrix
  22. from sympy.polys.rings import PolyElement
  23. from sympy.polys.fields import FracElement
  24. from sympy.polys.domains import QQ, RR
  25. from sympy.polys.polyclasses import DMF
  26. from sympy.polys.polyroots import roots
  27. from sympy.polys.polytools import Poly
  28. from sympy.polys.matrices import DomainMatrix
  29. from sympy.printing import sstr
  30. from sympy.series.limits import limit
  31. from sympy.series.order import Order
  32. from sympy.simplify.hyperexpand import hyperexpand
  33. from sympy.simplify.simplify import nsimplify
  34. from sympy.solvers.solvers import solve
  35. from .recurrence import HolonomicSequence, RecurrenceOperator, RecurrenceOperators
  36. from .holonomicerrors import (NotPowerSeriesError, NotHyperSeriesError,
  37. SingularityError, NotHolonomicError)
  38. def _find_nonzero_solution(r, homosys):
  39. ones = lambda shape: DomainMatrix.ones(shape, r.domain)
  40. particular, nullspace = r._solve(homosys)
  41. nullity = nullspace.shape[0]
  42. nullpart = ones((1, nullity)) * nullspace
  43. sol = (particular + nullpart).transpose()
  44. return sol
  45. def DifferentialOperators(base, generator):
  46. r"""
  47. This function is used to create annihilators using ``Dx``.
  48. Explanation
  49. ===========
  50. Returns an Algebra of Differential Operators also called Weyl Algebra
  51. and the operator for differentiation i.e. the ``Dx`` operator.
  52. Parameters
  53. ==========
  54. base:
  55. Base polynomial ring for the algebra.
  56. The base polynomial ring is the ring of polynomials in :math:`x` that
  57. will appear as coefficients in the operators.
  58. generator:
  59. Generator of the algebra which can
  60. be either a noncommutative ``Symbol`` or a string. e.g. "Dx" or "D".
  61. Examples
  62. ========
  63. >>> from sympy import ZZ
  64. >>> from sympy.abc import x
  65. >>> from sympy.holonomic.holonomic import DifferentialOperators
  66. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
  67. >>> R
  68. Univariate Differential Operator Algebra in intermediate Dx over the base ring ZZ[x]
  69. >>> Dx*x
  70. (1) + (x)*Dx
  71. """
  72. ring = DifferentialOperatorAlgebra(base, generator)
  73. return (ring, ring.derivative_operator)
  74. class DifferentialOperatorAlgebra:
  75. r"""
  76. An Ore Algebra is a set of noncommutative polynomials in the
  77. intermediate ``Dx`` and coefficients in a base polynomial ring :math:`A`.
  78. It follows the commutation rule:
  79. .. math ::
  80. Dxa = \sigma(a)Dx + \delta(a)
  81. for :math:`a \subset A`.
  82. Where :math:`\sigma: A \Rightarrow A` is an endomorphism and :math:`\delta: A \rightarrow A`
  83. is a skew-derivation i.e. :math:`\delta(ab) = \delta(a) b + \sigma(a) \delta(b)`.
  84. If one takes the sigma as identity map and delta as the standard derivation
  85. then it becomes the algebra of Differential Operators also called
  86. a Weyl Algebra i.e. an algebra whose elements are Differential Operators.
  87. This class represents a Weyl Algebra and serves as the parent ring for
  88. Differential Operators.
  89. Examples
  90. ========
  91. >>> from sympy import ZZ
  92. >>> from sympy import symbols
  93. >>> from sympy.holonomic.holonomic import DifferentialOperators
  94. >>> x = symbols('x')
  95. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
  96. >>> R
  97. Univariate Differential Operator Algebra in intermediate Dx over the base ring
  98. ZZ[x]
  99. See Also
  100. ========
  101. DifferentialOperator
  102. """
  103. def __init__(self, base, generator):
  104. # the base polynomial ring for the algebra
  105. self.base = base
  106. # the operator representing differentiation i.e. `Dx`
  107. self.derivative_operator = DifferentialOperator(
  108. [base.zero, base.one], self)
  109. if generator is None:
  110. self.gen_symbol = Symbol('Dx', commutative=False)
  111. else:
  112. if isinstance(generator, str):
  113. self.gen_symbol = Symbol(generator, commutative=False)
  114. elif isinstance(generator, Symbol):
  115. self.gen_symbol = generator
  116. def __str__(self):
  117. string = 'Univariate Differential Operator Algebra in intermediate '\
  118. + sstr(self.gen_symbol) + ' over the base ring ' + \
  119. (self.base).__str__()
  120. return string
  121. __repr__ = __str__
  122. def __eq__(self, other):
  123. if self.base == other.base and self.gen_symbol == other.gen_symbol:
  124. return True
  125. else:
  126. return False
  127. class DifferentialOperator:
  128. """
  129. Differential Operators are elements of Weyl Algebra. The Operators
  130. are defined by a list of polynomials in the base ring and the
  131. parent ring of the Operator i.e. the algebra it belongs to.
  132. Explanation
  133. ===========
  134. Takes a list of polynomials for each power of ``Dx`` and the
  135. parent ring which must be an instance of DifferentialOperatorAlgebra.
  136. A Differential Operator can be created easily using
  137. the operator ``Dx``. See examples below.
  138. Examples
  139. ========
  140. >>> from sympy.holonomic.holonomic import DifferentialOperator, DifferentialOperators
  141. >>> from sympy import ZZ
  142. >>> from sympy import symbols
  143. >>> x = symbols('x')
  144. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x),'Dx')
  145. >>> DifferentialOperator([0, 1, x**2], R)
  146. (1)*Dx + (x**2)*Dx**2
  147. >>> (x*Dx*x + 1 - Dx**2)**2
  148. (2*x**2 + 2*x + 1) + (4*x**3 + 2*x**2 - 4)*Dx + (x**4 - 6*x - 2)*Dx**2 + (-2*x**2)*Dx**3 + (1)*Dx**4
  149. See Also
  150. ========
  151. DifferentialOperatorAlgebra
  152. """
  153. _op_priority = 20
  154. def __init__(self, list_of_poly, parent):
  155. """
  156. Parameters
  157. ==========
  158. list_of_poly:
  159. List of polynomials belonging to the base ring of the algebra.
  160. parent:
  161. Parent algebra of the operator.
  162. """
  163. # the parent ring for this operator
  164. # must be an DifferentialOperatorAlgebra object
  165. self.parent = parent
  166. base = self.parent.base
  167. self.x = base.gens[0] if isinstance(base.gens[0], Symbol) else base.gens[0][0]
  168. # sequence of polynomials in x for each power of Dx
  169. # the list should not have trailing zeroes
  170. # represents the operator
  171. # convert the expressions into ring elements using from_sympy
  172. for i, j in enumerate(list_of_poly):
  173. if not isinstance(j, base.dtype):
  174. list_of_poly[i] = base.from_sympy(sympify(j))
  175. else:
  176. list_of_poly[i] = base.from_sympy(base.to_sympy(j))
  177. self.listofpoly = list_of_poly
  178. # highest power of `Dx`
  179. self.order = len(self.listofpoly) - 1
  180. def __mul__(self, other):
  181. """
  182. Multiplies two DifferentialOperator and returns another
  183. DifferentialOperator instance using the commutation rule
  184. Dx*a = a*Dx + a'
  185. """
  186. listofself = self.listofpoly
  187. if not isinstance(other, DifferentialOperator):
  188. if not isinstance(other, self.parent.base.dtype):
  189. listofother = [self.parent.base.from_sympy(sympify(other))]
  190. else:
  191. listofother = [other]
  192. else:
  193. listofother = other.listofpoly
  194. # multiplies a polynomial `b` with a list of polynomials
  195. def _mul_dmp_diffop(b, listofother):
  196. if isinstance(listofother, list):
  197. sol = []
  198. for i in listofother:
  199. sol.append(i * b)
  200. return sol
  201. else:
  202. return [b * listofother]
  203. sol = _mul_dmp_diffop(listofself[0], listofother)
  204. # compute Dx^i * b
  205. def _mul_Dxi_b(b):
  206. sol1 = [self.parent.base.zero]
  207. sol2 = []
  208. if isinstance(b, list):
  209. for i in b:
  210. sol1.append(i)
  211. sol2.append(i.diff())
  212. else:
  213. sol1.append(self.parent.base.from_sympy(b))
  214. sol2.append(self.parent.base.from_sympy(b).diff())
  215. return _add_lists(sol1, sol2)
  216. for i in range(1, len(listofself)):
  217. # find Dx^i * b in ith iteration
  218. listofother = _mul_Dxi_b(listofother)
  219. # solution = solution + listofself[i] * (Dx^i * b)
  220. sol = _add_lists(sol, _mul_dmp_diffop(listofself[i], listofother))
  221. return DifferentialOperator(sol, self.parent)
  222. def __rmul__(self, other):
  223. if not isinstance(other, DifferentialOperator):
  224. if not isinstance(other, self.parent.base.dtype):
  225. other = (self.parent.base).from_sympy(sympify(other))
  226. sol = []
  227. for j in self.listofpoly:
  228. sol.append(other * j)
  229. return DifferentialOperator(sol, self.parent)
  230. def __add__(self, other):
  231. if isinstance(other, DifferentialOperator):
  232. sol = _add_lists(self.listofpoly, other.listofpoly)
  233. return DifferentialOperator(sol, self.parent)
  234. else:
  235. list_self = self.listofpoly
  236. if not isinstance(other, self.parent.base.dtype):
  237. list_other = [((self.parent).base).from_sympy(sympify(other))]
  238. else:
  239. list_other = [other]
  240. sol = []
  241. sol.append(list_self[0] + list_other[0])
  242. sol += list_self[1:]
  243. return DifferentialOperator(sol, self.parent)
  244. __radd__ = __add__
  245. def __sub__(self, other):
  246. return self + (-1) * other
  247. def __rsub__(self, other):
  248. return (-1) * self + other
  249. def __neg__(self):
  250. return -1 * self
  251. def __truediv__(self, other):
  252. return self * (S.One / other)
  253. def __pow__(self, n):
  254. if n == 1:
  255. return self
  256. if n == 0:
  257. return DifferentialOperator([self.parent.base.one], self.parent)
  258. # if self is `Dx`
  259. if self.listofpoly == self.parent.derivative_operator.listofpoly:
  260. sol = [self.parent.base.zero]*n
  261. sol.append(self.parent.base.one)
  262. return DifferentialOperator(sol, self.parent)
  263. # the general case
  264. else:
  265. if n % 2 == 1:
  266. powreduce = self**(n - 1)
  267. return powreduce * self
  268. elif n % 2 == 0:
  269. powreduce = self**(n / 2)
  270. return powreduce * powreduce
  271. def __str__(self):
  272. listofpoly = self.listofpoly
  273. print_str = ''
  274. for i, j in enumerate(listofpoly):
  275. if j == self.parent.base.zero:
  276. continue
  277. if i == 0:
  278. print_str += '(' + sstr(j) + ')'
  279. continue
  280. if print_str:
  281. print_str += ' + '
  282. if i == 1:
  283. print_str += '(' + sstr(j) + ')*%s' %(self.parent.gen_symbol)
  284. continue
  285. print_str += '(' + sstr(j) + ')' + '*%s**' %(self.parent.gen_symbol) + sstr(i)
  286. return print_str
  287. __repr__ = __str__
  288. def __eq__(self, other):
  289. if isinstance(other, DifferentialOperator):
  290. if self.listofpoly == other.listofpoly and self.parent == other.parent:
  291. return True
  292. else:
  293. return False
  294. else:
  295. if self.listofpoly[0] == other:
  296. for i in self.listofpoly[1:]:
  297. if i is not self.parent.base.zero:
  298. return False
  299. return True
  300. else:
  301. return False
  302. def is_singular(self, x0):
  303. """
  304. Checks if the differential equation is singular at x0.
  305. """
  306. base = self.parent.base
  307. return x0 in roots(base.to_sympy(self.listofpoly[-1]), self.x)
  308. class HolonomicFunction:
  309. r"""
  310. A Holonomic Function is a solution to a linear homogeneous ordinary
  311. differential equation with polynomial coefficients. This differential
  312. equation can also be represented by an annihilator i.e. a Differential
  313. Operator ``L`` such that :math:`L.f = 0`. For uniqueness of these functions,
  314. initial conditions can also be provided along with the annihilator.
  315. Explanation
  316. ===========
  317. Holonomic functions have closure properties and thus forms a ring.
  318. Given two Holonomic Functions f and g, their sum, product,
  319. integral and derivative is also a Holonomic Function.
  320. For ordinary points initial condition should be a vector of values of
  321. the derivatives i.e. :math:`[y(x_0), y'(x_0), y''(x_0) ... ]`.
  322. For regular singular points initial conditions can also be provided in this
  323. format:
  324. :math:`{s0: [C_0, C_1, ...], s1: [C^1_0, C^1_1, ...], ...}`
  325. where s0, s1, ... are the roots of indicial equation and vectors
  326. :math:`[C_0, C_1, ...], [C^0_0, C^0_1, ...], ...` are the corresponding initial
  327. terms of the associated power series. See Examples below.
  328. Examples
  329. ========
  330. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  331. >>> from sympy import QQ
  332. >>> from sympy import symbols, S
  333. >>> x = symbols('x')
  334. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  335. >>> p = HolonomicFunction(Dx - 1, x, 0, [1]) # e^x
  336. >>> q = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) # sin(x)
  337. >>> p + q # annihilator of e^x + sin(x)
  338. HolonomicFunction((-1) + (1)*Dx + (-1)*Dx**2 + (1)*Dx**3, x, 0, [1, 2, 1])
  339. >>> p * q # annihilator of e^x * sin(x)
  340. HolonomicFunction((2) + (-2)*Dx + (1)*Dx**2, x, 0, [0, 1])
  341. An example of initial conditions for regular singular points,
  342. the indicial equation has only one root `1/2`.
  343. >>> HolonomicFunction(-S(1)/2 + x*Dx, x, 0, {S(1)/2: [1]})
  344. HolonomicFunction((-1/2) + (x)*Dx, x, 0, {1/2: [1]})
  345. >>> HolonomicFunction(-S(1)/2 + x*Dx, x, 0, {S(1)/2: [1]}).to_expr()
  346. sqrt(x)
  347. To plot a Holonomic Function, one can use `.evalf()` for numerical
  348. computation. Here's an example on `sin(x)**2/x` using numpy and matplotlib.
  349. >>> import sympy.holonomic # doctest: +SKIP
  350. >>> from sympy import var, sin # doctest: +SKIP
  351. >>> import matplotlib.pyplot as plt # doctest: +SKIP
  352. >>> import numpy as np # doctest: +SKIP
  353. >>> var("x") # doctest: +SKIP
  354. >>> r = np.linspace(1, 5, 100) # doctest: +SKIP
  355. >>> y = sympy.holonomic.expr_to_holonomic(sin(x)**2/x, x0=1).evalf(r) # doctest: +SKIP
  356. >>> plt.plot(r, y, label="holonomic function") # doctest: +SKIP
  357. >>> plt.show() # doctest: +SKIP
  358. """
  359. _op_priority = 20
  360. def __init__(self, annihilator, x, x0=0, y0=None):
  361. """
  362. Parameters
  363. ==========
  364. annihilator:
  365. Annihilator of the Holonomic Function, represented by a
  366. `DifferentialOperator` object.
  367. x:
  368. Variable of the function.
  369. x0:
  370. The point at which initial conditions are stored.
  371. Generally an integer.
  372. y0:
  373. The initial condition. The proper format for the initial condition
  374. is described in class docstring. To make the function unique,
  375. length of the vector `y0` should be equal to or greater than the
  376. order of differential equation.
  377. """
  378. # initial condition
  379. self.y0 = y0
  380. # the point for initial conditions, default is zero.
  381. self.x0 = x0
  382. # differential operator L such that L.f = 0
  383. self.annihilator = annihilator
  384. self.x = x
  385. def __str__(self):
  386. if self._have_init_cond():
  387. str_sol = 'HolonomicFunction(%s, %s, %s, %s)' % (str(self.annihilator),\
  388. sstr(self.x), sstr(self.x0), sstr(self.y0))
  389. else:
  390. str_sol = 'HolonomicFunction(%s, %s)' % (str(self.annihilator),\
  391. sstr(self.x))
  392. return str_sol
  393. __repr__ = __str__
  394. def unify(self, other):
  395. """
  396. Unifies the base polynomial ring of a given two Holonomic
  397. Functions.
  398. """
  399. R1 = self.annihilator.parent.base
  400. R2 = other.annihilator.parent.base
  401. dom1 = R1.dom
  402. dom2 = R2.dom
  403. if R1 == R2:
  404. return (self, other)
  405. R = (dom1.unify(dom2)).old_poly_ring(self.x)
  406. newparent, _ = DifferentialOperators(R, str(self.annihilator.parent.gen_symbol))
  407. sol1 = [R1.to_sympy(i) for i in self.annihilator.listofpoly]
  408. sol2 = [R2.to_sympy(i) for i in other.annihilator.listofpoly]
  409. sol1 = DifferentialOperator(sol1, newparent)
  410. sol2 = DifferentialOperator(sol2, newparent)
  411. sol1 = HolonomicFunction(sol1, self.x, self.x0, self.y0)
  412. sol2 = HolonomicFunction(sol2, other.x, other.x0, other.y0)
  413. return (sol1, sol2)
  414. def is_singularics(self):
  415. """
  416. Returns True if the function have singular initial condition
  417. in the dictionary format.
  418. Returns False if the function have ordinary initial condition
  419. in the list format.
  420. Returns None for all other cases.
  421. """
  422. if isinstance(self.y0, dict):
  423. return True
  424. elif isinstance(self.y0, list):
  425. return False
  426. def _have_init_cond(self):
  427. """
  428. Checks if the function have initial condition.
  429. """
  430. return bool(self.y0)
  431. def _singularics_to_ord(self):
  432. """
  433. Converts a singular initial condition to ordinary if possible.
  434. """
  435. a = list(self.y0)[0]
  436. b = self.y0[a]
  437. if len(self.y0) == 1 and a == int(a) and a > 0:
  438. y0 = []
  439. a = int(a)
  440. for i in range(a):
  441. y0.append(S.Zero)
  442. y0 += [j * factorial(a + i) for i, j in enumerate(b)]
  443. return HolonomicFunction(self.annihilator, self.x, self.x0, y0)
  444. def __add__(self, other):
  445. # if the ground domains are different
  446. if self.annihilator.parent.base != other.annihilator.parent.base:
  447. a, b = self.unify(other)
  448. return a + b
  449. deg1 = self.annihilator.order
  450. deg2 = other.annihilator.order
  451. dim = max(deg1, deg2)
  452. R = self.annihilator.parent.base
  453. K = R.get_field()
  454. rowsself = [self.annihilator]
  455. rowsother = [other.annihilator]
  456. gen = self.annihilator.parent.derivative_operator
  457. # constructing annihilators up to order dim
  458. for i in range(dim - deg1):
  459. diff1 = (gen * rowsself[-1])
  460. rowsself.append(diff1)
  461. for i in range(dim - deg2):
  462. diff2 = (gen * rowsother[-1])
  463. rowsother.append(diff2)
  464. row = rowsself + rowsother
  465. # constructing the matrix of the ansatz
  466. r = []
  467. for expr in row:
  468. p = []
  469. for i in range(dim + 1):
  470. if i >= len(expr.listofpoly):
  471. p.append(K.zero)
  472. else:
  473. p.append(K.new(expr.listofpoly[i].rep))
  474. r.append(p)
  475. # solving the linear system using gauss jordan solver
  476. r = DomainMatrix(r, (len(row), dim+1), K).transpose()
  477. homosys = DomainMatrix.zeros((dim+1, 1), K)
  478. sol = _find_nonzero_solution(r, homosys)
  479. # if a solution is not obtained then increasing the order by 1 in each
  480. # iteration
  481. while sol.is_zero_matrix:
  482. dim += 1
  483. diff1 = (gen * rowsself[-1])
  484. rowsself.append(diff1)
  485. diff2 = (gen * rowsother[-1])
  486. rowsother.append(diff2)
  487. row = rowsself + rowsother
  488. r = []
  489. for expr in row:
  490. p = []
  491. for i in range(dim + 1):
  492. if i >= len(expr.listofpoly):
  493. p.append(K.zero)
  494. else:
  495. p.append(K.new(expr.listofpoly[i].rep))
  496. r.append(p)
  497. # solving the linear system using gauss jordan solver
  498. r = DomainMatrix(r, (len(row), dim+1), K).transpose()
  499. homosys = DomainMatrix.zeros((dim+1, 1), K)
  500. sol = _find_nonzero_solution(r, homosys)
  501. # taking only the coefficients needed to multiply with `self`
  502. # can be also be done the other way by taking R.H.S and multiplying with
  503. # `other`
  504. sol = sol.flat()[:dim + 1 - deg1]
  505. sol1 = _normalize(sol, self.annihilator.parent)
  506. # annihilator of the solution
  507. sol = sol1 * (self.annihilator)
  508. sol = _normalize(sol.listofpoly, self.annihilator.parent, negative=False)
  509. if not (self._have_init_cond() and other._have_init_cond()):
  510. return HolonomicFunction(sol, self.x)
  511. # both the functions have ordinary initial conditions
  512. if self.is_singularics() == False and other.is_singularics() == False:
  513. # directly add the corresponding value
  514. if self.x0 == other.x0:
  515. # try to extended the initial conditions
  516. # using the annihilator
  517. y1 = _extend_y0(self, sol.order)
  518. y2 = _extend_y0(other, sol.order)
  519. y0 = [a + b for a, b in zip(y1, y2)]
  520. return HolonomicFunction(sol, self.x, self.x0, y0)
  521. else:
  522. # change the initial conditions to a same point
  523. selfat0 = self.annihilator.is_singular(0)
  524. otherat0 = other.annihilator.is_singular(0)
  525. if self.x0 == 0 and not selfat0 and not otherat0:
  526. return self + other.change_ics(0)
  527. elif other.x0 == 0 and not selfat0 and not otherat0:
  528. return self.change_ics(0) + other
  529. else:
  530. selfatx0 = self.annihilator.is_singular(self.x0)
  531. otheratx0 = other.annihilator.is_singular(self.x0)
  532. if not selfatx0 and not otheratx0:
  533. return self + other.change_ics(self.x0)
  534. else:
  535. return self.change_ics(other.x0) + other
  536. if self.x0 != other.x0:
  537. return HolonomicFunction(sol, self.x)
  538. # if the functions have singular_ics
  539. y1 = None
  540. y2 = None
  541. if self.is_singularics() == False and other.is_singularics() == True:
  542. # convert the ordinary initial condition to singular.
  543. _y0 = [j / factorial(i) for i, j in enumerate(self.y0)]
  544. y1 = {S.Zero: _y0}
  545. y2 = other.y0
  546. elif self.is_singularics() == True and other.is_singularics() == False:
  547. _y0 = [j / factorial(i) for i, j in enumerate(other.y0)]
  548. y1 = self.y0
  549. y2 = {S.Zero: _y0}
  550. elif self.is_singularics() == True and other.is_singularics() == True:
  551. y1 = self.y0
  552. y2 = other.y0
  553. # computing singular initial condition for the result
  554. # taking union of the series terms of both functions
  555. y0 = {}
  556. for i in y1:
  557. # add corresponding initial terms if the power
  558. # on `x` is same
  559. if i in y2:
  560. y0[i] = [a + b for a, b in zip(y1[i], y2[i])]
  561. else:
  562. y0[i] = y1[i]
  563. for i in y2:
  564. if i not in y1:
  565. y0[i] = y2[i]
  566. return HolonomicFunction(sol, self.x, self.x0, y0)
  567. def integrate(self, limits, initcond=False):
  568. """
  569. Integrates the given holonomic function.
  570. Examples
  571. ========
  572. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  573. >>> from sympy import QQ
  574. >>> from sympy import symbols
  575. >>> x = symbols('x')
  576. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  577. >>> HolonomicFunction(Dx - 1, x, 0, [1]).integrate((x, 0, x)) # e^x - 1
  578. HolonomicFunction((-1)*Dx + (1)*Dx**2, x, 0, [0, 1])
  579. >>> HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]).integrate((x, 0, x))
  580. HolonomicFunction((1)*Dx + (1)*Dx**3, x, 0, [0, 1, 0])
  581. """
  582. # to get the annihilator, just multiply by Dx from right
  583. D = self.annihilator.parent.derivative_operator
  584. # if the function have initial conditions of the series format
  585. if self.is_singularics() == True:
  586. r = self._singularics_to_ord()
  587. if r:
  588. return r.integrate(limits, initcond=initcond)
  589. # computing singular initial condition for the function
  590. # produced after integration.
  591. y0 = {}
  592. for i in self.y0:
  593. c = self.y0[i]
  594. c2 = []
  595. for j, cj in enumerate(c):
  596. if cj == 0:
  597. c2.append(S.Zero)
  598. # if power on `x` is -1, the integration becomes log(x)
  599. # TODO: Implement this case
  600. elif i + j + 1 == 0:
  601. raise NotImplementedError("logarithmic terms in the series are not supported")
  602. else:
  603. c2.append(cj / S(i + j + 1))
  604. y0[i + 1] = c2
  605. if hasattr(limits, "__iter__"):
  606. raise NotImplementedError("Definite integration for singular initial conditions")
  607. return HolonomicFunction(self.annihilator * D, self.x, self.x0, y0)
  608. # if no initial conditions are available for the function
  609. if not self._have_init_cond():
  610. if initcond:
  611. return HolonomicFunction(self.annihilator * D, self.x, self.x0, [S.Zero])
  612. return HolonomicFunction(self.annihilator * D, self.x)
  613. # definite integral
  614. # initial conditions for the answer will be stored at point `a`,
  615. # where `a` is the lower limit of the integrand
  616. if hasattr(limits, "__iter__"):
  617. if len(limits) == 3 and limits[0] == self.x:
  618. x0 = self.x0
  619. a = limits[1]
  620. b = limits[2]
  621. definite = True
  622. else:
  623. definite = False
  624. y0 = [S.Zero]
  625. y0 += self.y0
  626. indefinite_integral = HolonomicFunction(self.annihilator * D, self.x, self.x0, y0)
  627. if not definite:
  628. return indefinite_integral
  629. # use evalf to get the values at `a`
  630. if x0 != a:
  631. try:
  632. indefinite_expr = indefinite_integral.to_expr()
  633. except (NotHyperSeriesError, NotPowerSeriesError):
  634. indefinite_expr = None
  635. if indefinite_expr:
  636. lower = indefinite_expr.subs(self.x, a)
  637. if isinstance(lower, NaN):
  638. lower = indefinite_expr.limit(self.x, a)
  639. else:
  640. lower = indefinite_integral.evalf(a)
  641. if b == self.x:
  642. y0[0] = y0[0] - lower
  643. return HolonomicFunction(self.annihilator * D, self.x, x0, y0)
  644. elif S(b).is_Number:
  645. if indefinite_expr:
  646. upper = indefinite_expr.subs(self.x, b)
  647. if isinstance(upper, NaN):
  648. upper = indefinite_expr.limit(self.x, b)
  649. else:
  650. upper = indefinite_integral.evalf(b)
  651. return upper - lower
  652. # if the upper limit is `x`, the answer will be a function
  653. if b == self.x:
  654. return HolonomicFunction(self.annihilator * D, self.x, a, y0)
  655. # if the upper limits is a Number, a numerical value will be returned
  656. elif S(b).is_Number:
  657. try:
  658. s = HolonomicFunction(self.annihilator * D, self.x, a,\
  659. y0).to_expr()
  660. indefinite = s.subs(self.x, b)
  661. if not isinstance(indefinite, NaN):
  662. return indefinite
  663. else:
  664. return s.limit(self.x, b)
  665. except (NotHyperSeriesError, NotPowerSeriesError):
  666. return HolonomicFunction(self.annihilator * D, self.x, a, y0).evalf(b)
  667. return HolonomicFunction(self.annihilator * D, self.x)
  668. def diff(self, *args, **kwargs):
  669. r"""
  670. Differentiation of the given Holonomic function.
  671. Examples
  672. ========
  673. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  674. >>> from sympy import ZZ
  675. >>> from sympy import symbols
  676. >>> x = symbols('x')
  677. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x),'Dx')
  678. >>> HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).diff().to_expr()
  679. cos(x)
  680. >>> HolonomicFunction(Dx - 2, x, 0, [1]).diff().to_expr()
  681. 2*exp(2*x)
  682. See Also
  683. ========
  684. integrate
  685. """
  686. kwargs.setdefault('evaluate', True)
  687. if args:
  688. if args[0] != self.x:
  689. return S.Zero
  690. elif len(args) == 2:
  691. sol = self
  692. for i in range(args[1]):
  693. sol = sol.diff(args[0])
  694. return sol
  695. ann = self.annihilator
  696. # if the function is constant.
  697. if ann.listofpoly[0] == ann.parent.base.zero and ann.order == 1:
  698. return S.Zero
  699. # if the coefficient of y in the differential equation is zero.
  700. # a shifting is done to compute the answer in this case.
  701. elif ann.listofpoly[0] == ann.parent.base.zero:
  702. sol = DifferentialOperator(ann.listofpoly[1:], ann.parent)
  703. if self._have_init_cond():
  704. # if ordinary initial condition
  705. if self.is_singularics() == False:
  706. return HolonomicFunction(sol, self.x, self.x0, self.y0[1:])
  707. # TODO: support for singular initial condition
  708. return HolonomicFunction(sol, self.x)
  709. else:
  710. return HolonomicFunction(sol, self.x)
  711. # the general algorithm
  712. R = ann.parent.base
  713. K = R.get_field()
  714. seq_dmf = [K.new(i.rep) for i in ann.listofpoly]
  715. # -y = a1*y'/a0 + a2*y''/a0 ... + an*y^n/a0
  716. rhs = [i / seq_dmf[0] for i in seq_dmf[1:]]
  717. rhs.insert(0, K.zero)
  718. # differentiate both lhs and rhs
  719. sol = _derivate_diff_eq(rhs)
  720. # add the term y' in lhs to rhs
  721. sol = _add_lists(sol, [K.zero, K.one])
  722. sol = _normalize(sol[1:], self.annihilator.parent, negative=False)
  723. if not self._have_init_cond() or self.is_singularics() == True:
  724. return HolonomicFunction(sol, self.x)
  725. y0 = _extend_y0(self, sol.order + 1)[1:]
  726. return HolonomicFunction(sol, self.x, self.x0, y0)
  727. def __eq__(self, other):
  728. if self.annihilator == other.annihilator:
  729. if self.x == other.x:
  730. if self._have_init_cond() and other._have_init_cond():
  731. if self.x0 == other.x0 and self.y0 == other.y0:
  732. return True
  733. else:
  734. return False
  735. else:
  736. return True
  737. else:
  738. return False
  739. else:
  740. return False
  741. def __mul__(self, other):
  742. ann_self = self.annihilator
  743. if not isinstance(other, HolonomicFunction):
  744. other = sympify(other)
  745. if other.has(self.x):
  746. raise NotImplementedError(" Can't multiply a HolonomicFunction and expressions/functions.")
  747. if not self._have_init_cond():
  748. return self
  749. else:
  750. y0 = _extend_y0(self, ann_self.order)
  751. y1 = []
  752. for j in y0:
  753. y1.append((Poly.new(j, self.x) * other).rep)
  754. return HolonomicFunction(ann_self, self.x, self.x0, y1)
  755. if self.annihilator.parent.base != other.annihilator.parent.base:
  756. a, b = self.unify(other)
  757. return a * b
  758. ann_other = other.annihilator
  759. list_self = []
  760. list_other = []
  761. a = ann_self.order
  762. b = ann_other.order
  763. R = ann_self.parent.base
  764. K = R.get_field()
  765. for j in ann_self.listofpoly:
  766. list_self.append(K.new(j.rep))
  767. for j in ann_other.listofpoly:
  768. list_other.append(K.new(j.rep))
  769. # will be used to reduce the degree
  770. self_red = [-list_self[i] / list_self[a] for i in range(a)]
  771. other_red = [-list_other[i] / list_other[b] for i in range(b)]
  772. # coeff_mull[i][j] is the coefficient of Dx^i(f).Dx^j(g)
  773. coeff_mul = [[K.zero for i in range(b + 1)] for j in range(a + 1)]
  774. coeff_mul[0][0] = K.one
  775. # making the ansatz
  776. lin_sys_elements = [[coeff_mul[i][j] for i in range(a) for j in range(b)]]
  777. lin_sys = DomainMatrix(lin_sys_elements, (1, a*b), K).transpose()
  778. homo_sys = DomainMatrix.zeros((a*b, 1), K)
  779. sol = _find_nonzero_solution(lin_sys, homo_sys)
  780. # until a non trivial solution is found
  781. while sol.is_zero_matrix:
  782. # updating the coefficients Dx^i(f).Dx^j(g) for next degree
  783. for i in range(a - 1, -1, -1):
  784. for j in range(b - 1, -1, -1):
  785. coeff_mul[i][j + 1] += coeff_mul[i][j]
  786. coeff_mul[i + 1][j] += coeff_mul[i][j]
  787. if isinstance(coeff_mul[i][j], K.dtype):
  788. coeff_mul[i][j] = DMFdiff(coeff_mul[i][j])
  789. else:
  790. coeff_mul[i][j] = coeff_mul[i][j].diff(self.x)
  791. # reduce the terms to lower power using annihilators of f, g
  792. for i in range(a + 1):
  793. if not coeff_mul[i][b].is_zero:
  794. for j in range(b):
  795. coeff_mul[i][j] += other_red[j] * \
  796. coeff_mul[i][b]
  797. coeff_mul[i][b] = K.zero
  798. # not d2 + 1, as that is already covered in previous loop
  799. for j in range(b):
  800. if not coeff_mul[a][j] == 0:
  801. for i in range(a):
  802. coeff_mul[i][j] += self_red[i] * \
  803. coeff_mul[a][j]
  804. coeff_mul[a][j] = K.zero
  805. lin_sys_elements.append([coeff_mul[i][j] for i in range(a) for j in range(b)])
  806. lin_sys = DomainMatrix(lin_sys_elements, (len(lin_sys_elements), a*b), K).transpose()
  807. sol = _find_nonzero_solution(lin_sys, homo_sys)
  808. sol_ann = _normalize(sol.flat(), self.annihilator.parent, negative=False)
  809. if not (self._have_init_cond() and other._have_init_cond()):
  810. return HolonomicFunction(sol_ann, self.x)
  811. if self.is_singularics() == False and other.is_singularics() == False:
  812. # if both the conditions are at same point
  813. if self.x0 == other.x0:
  814. # try to find more initial conditions
  815. y0_self = _extend_y0(self, sol_ann.order)
  816. y0_other = _extend_y0(other, sol_ann.order)
  817. # h(x0) = f(x0) * g(x0)
  818. y0 = [y0_self[0] * y0_other[0]]
  819. # coefficient of Dx^j(f)*Dx^i(g) in Dx^i(fg)
  820. for i in range(1, min(len(y0_self), len(y0_other))):
  821. coeff = [[0 for i in range(i + 1)] for j in range(i + 1)]
  822. for j in range(i + 1):
  823. for k in range(i + 1):
  824. if j + k == i:
  825. coeff[j][k] = binomial(i, j)
  826. sol = 0
  827. for j in range(i + 1):
  828. for k in range(i + 1):
  829. sol += coeff[j][k]* y0_self[j] * y0_other[k]
  830. y0.append(sol)
  831. return HolonomicFunction(sol_ann, self.x, self.x0, y0)
  832. # if the points are different, consider one
  833. else:
  834. selfat0 = self.annihilator.is_singular(0)
  835. otherat0 = other.annihilator.is_singular(0)
  836. if self.x0 == 0 and not selfat0 and not otherat0:
  837. return self * other.change_ics(0)
  838. elif other.x0 == 0 and not selfat0 and not otherat0:
  839. return self.change_ics(0) * other
  840. else:
  841. selfatx0 = self.annihilator.is_singular(self.x0)
  842. otheratx0 = other.annihilator.is_singular(self.x0)
  843. if not selfatx0 and not otheratx0:
  844. return self * other.change_ics(self.x0)
  845. else:
  846. return self.change_ics(other.x0) * other
  847. if self.x0 != other.x0:
  848. return HolonomicFunction(sol_ann, self.x)
  849. # if the functions have singular_ics
  850. y1 = None
  851. y2 = None
  852. if self.is_singularics() == False and other.is_singularics() == True:
  853. _y0 = [j / factorial(i) for i, j in enumerate(self.y0)]
  854. y1 = {S.Zero: _y0}
  855. y2 = other.y0
  856. elif self.is_singularics() == True and other.is_singularics() == False:
  857. _y0 = [j / factorial(i) for i, j in enumerate(other.y0)]
  858. y1 = self.y0
  859. y2 = {S.Zero: _y0}
  860. elif self.is_singularics() == True and other.is_singularics() == True:
  861. y1 = self.y0
  862. y2 = other.y0
  863. y0 = {}
  864. # multiply every possible pair of the series terms
  865. for i in y1:
  866. for j in y2:
  867. k = min(len(y1[i]), len(y2[j]))
  868. c = []
  869. for a in range(k):
  870. s = S.Zero
  871. for b in range(a + 1):
  872. s += y1[i][b] * y2[j][a - b]
  873. c.append(s)
  874. if not i + j in y0:
  875. y0[i + j] = c
  876. else:
  877. y0[i + j] = [a + b for a, b in zip(c, y0[i + j])]
  878. return HolonomicFunction(sol_ann, self.x, self.x0, y0)
  879. __rmul__ = __mul__
  880. def __sub__(self, other):
  881. return self + other * -1
  882. def __rsub__(self, other):
  883. return self * -1 + other
  884. def __neg__(self):
  885. return -1 * self
  886. def __truediv__(self, other):
  887. return self * (S.One / other)
  888. def __pow__(self, n):
  889. if self.annihilator.order <= 1:
  890. ann = self.annihilator
  891. parent = ann.parent
  892. if self.y0 is None:
  893. y0 = None
  894. else:
  895. y0 = [list(self.y0)[0] ** n]
  896. p0 = ann.listofpoly[0]
  897. p1 = ann.listofpoly[1]
  898. p0 = (Poly.new(p0, self.x) * n).rep
  899. sol = [parent.base.to_sympy(i) for i in [p0, p1]]
  900. dd = DifferentialOperator(sol, parent)
  901. return HolonomicFunction(dd, self.x, self.x0, y0)
  902. if n < 0:
  903. raise NotHolonomicError("Negative Power on a Holonomic Function")
  904. if n == 0:
  905. Dx = self.annihilator.parent.derivative_operator
  906. return HolonomicFunction(Dx, self.x, S.Zero, [S.One])
  907. if n == 1:
  908. return self
  909. else:
  910. if n % 2 == 1:
  911. powreduce = self**(n - 1)
  912. return powreduce * self
  913. elif n % 2 == 0:
  914. powreduce = self**(n / 2)
  915. return powreduce * powreduce
  916. def degree(self):
  917. """
  918. Returns the highest power of `x` in the annihilator.
  919. """
  920. sol = [i.degree() for i in self.annihilator.listofpoly]
  921. return max(sol)
  922. def composition(self, expr, *args, **kwargs):
  923. """
  924. Returns function after composition of a holonomic
  925. function with an algebraic function. The method cannot compute
  926. initial conditions for the result by itself, so they can be also be
  927. provided.
  928. Examples
  929. ========
  930. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  931. >>> from sympy import QQ
  932. >>> from sympy import symbols
  933. >>> x = symbols('x')
  934. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  935. >>> HolonomicFunction(Dx - 1, x).composition(x**2, 0, [1]) # e^(x**2)
  936. HolonomicFunction((-2*x) + (1)*Dx, x, 0, [1])
  937. >>> HolonomicFunction(Dx**2 + 1, x).composition(x**2 - 1, 1, [1, 0])
  938. HolonomicFunction((4*x**3) + (-1)*Dx + (x)*Dx**2, x, 1, [1, 0])
  939. See Also
  940. ========
  941. from_hyper
  942. """
  943. R = self.annihilator.parent
  944. a = self.annihilator.order
  945. diff = expr.diff(self.x)
  946. listofpoly = self.annihilator.listofpoly
  947. for i, j in enumerate(listofpoly):
  948. if isinstance(j, self.annihilator.parent.base.dtype):
  949. listofpoly[i] = self.annihilator.parent.base.to_sympy(j)
  950. r = listofpoly[a].subs({self.x:expr})
  951. subs = [-listofpoly[i].subs({self.x:expr}) / r for i in range (a)]
  952. coeffs = [S.Zero for i in range(a)] # coeffs[i] == coeff of (D^i f)(a) in D^k (f(a))
  953. coeffs[0] = S.One
  954. system = [coeffs]
  955. homogeneous = Matrix([[S.Zero for i in range(a)]]).transpose()
  956. while True:
  957. coeffs_next = [p.diff(self.x) for p in coeffs]
  958. for i in range(a - 1):
  959. coeffs_next[i + 1] += (coeffs[i] * diff)
  960. for i in range(a):
  961. coeffs_next[i] += (coeffs[-1] * subs[i] * diff)
  962. coeffs = coeffs_next
  963. # check for linear relations
  964. system.append(coeffs)
  965. sol, taus = (Matrix(system).transpose()
  966. ).gauss_jordan_solve(homogeneous)
  967. if sol.is_zero_matrix is not True:
  968. break
  969. tau = list(taus)[0]
  970. sol = sol.subs(tau, 1)
  971. sol = _normalize(sol[0:], R, negative=False)
  972. # if initial conditions are given for the resulting function
  973. if args:
  974. return HolonomicFunction(sol, self.x, args[0], args[1])
  975. return HolonomicFunction(sol, self.x)
  976. def to_sequence(self, lb=True):
  977. r"""
  978. Finds recurrence relation for the coefficients in the series expansion
  979. of the function about :math:`x_0`, where :math:`x_0` is the point at
  980. which the initial condition is stored.
  981. Explanation
  982. ===========
  983. If the point :math:`x_0` is ordinary, solution of the form :math:`[(R, n_0)]`
  984. is returned. Where :math:`R` is the recurrence relation and :math:`n_0` is the
  985. smallest ``n`` for which the recurrence holds true.
  986. If the point :math:`x_0` is regular singular, a list of solutions in
  987. the format :math:`(R, p, n_0)` is returned, i.e. `[(R, p, n_0), ... ]`.
  988. Each tuple in this vector represents a recurrence relation :math:`R`
  989. associated with a root of the indicial equation ``p``. Conditions of
  990. a different format can also be provided in this case, see the
  991. docstring of HolonomicFunction class.
  992. If it's not possible to numerically compute a initial condition,
  993. it is returned as a symbol :math:`C_j`, denoting the coefficient of
  994. :math:`(x - x_0)^j` in the power series about :math:`x_0`.
  995. Examples
  996. ========
  997. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  998. >>> from sympy import QQ
  999. >>> from sympy import symbols, S
  1000. >>> x = symbols('x')
  1001. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  1002. >>> HolonomicFunction(Dx - 1, x, 0, [1]).to_sequence()
  1003. [(HolonomicSequence((-1) + (n + 1)Sn, n), u(0) = 1, 0)]
  1004. >>> HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]).to_sequence()
  1005. [(HolonomicSequence((n**2) + (n**2 + n)Sn, n), u(0) = 0, u(1) = 1, u(2) = -1/2, 2)]
  1006. >>> HolonomicFunction(-S(1)/2 + x*Dx, x, 0, {S(1)/2: [1]}).to_sequence()
  1007. [(HolonomicSequence((n), n), u(0) = 1, 1/2, 1)]
  1008. See Also
  1009. ========
  1010. HolonomicFunction.series
  1011. References
  1012. ==========
  1013. .. [1] https://hal.inria.fr/inria-00070025/document
  1014. .. [2] https://www3.risc.jku.at/publications/download/risc_2244/DIPLFORM.pdf
  1015. """
  1016. if self.x0 != 0:
  1017. return self.shift_x(self.x0).to_sequence()
  1018. # check whether a power series exists if the point is singular
  1019. if self.annihilator.is_singular(self.x0):
  1020. return self._frobenius(lb=lb)
  1021. dict1 = {}
  1022. n = Symbol('n', integer=True)
  1023. dom = self.annihilator.parent.base.dom
  1024. R, _ = RecurrenceOperators(dom.old_poly_ring(n), 'Sn')
  1025. # substituting each term of the form `x^k Dx^j` in the
  1026. # annihilator, according to the formula below:
  1027. # x^k Dx^j = Sum(rf(n + 1 - k, j) * a(n + j - k) * x^n, (n, k, oo))
  1028. # for explanation see [2].
  1029. for i, j in enumerate(self.annihilator.listofpoly):
  1030. listofdmp = j.all_coeffs()
  1031. degree = len(listofdmp) - 1
  1032. for k in range(degree + 1):
  1033. coeff = listofdmp[degree - k]
  1034. if coeff == 0:
  1035. continue
  1036. if (i - k, k) in dict1:
  1037. dict1[(i - k, k)] += (dom.to_sympy(coeff) * rf(n - k + 1, i))
  1038. else:
  1039. dict1[(i - k, k)] = (dom.to_sympy(coeff) * rf(n - k + 1, i))
  1040. sol = []
  1041. keylist = [i[0] for i in dict1]
  1042. lower = min(keylist)
  1043. upper = max(keylist)
  1044. degree = self.degree()
  1045. # the recurrence relation holds for all values of
  1046. # n greater than smallest_n, i.e. n >= smallest_n
  1047. smallest_n = lower + degree
  1048. dummys = {}
  1049. eqs = []
  1050. unknowns = []
  1051. # an appropriate shift of the recurrence
  1052. for j in range(lower, upper + 1):
  1053. if j in keylist:
  1054. temp = S.Zero
  1055. for k in dict1.keys():
  1056. if k[0] == j:
  1057. temp += dict1[k].subs(n, n - lower)
  1058. sol.append(temp)
  1059. else:
  1060. sol.append(S.Zero)
  1061. # the recurrence relation
  1062. sol = RecurrenceOperator(sol, R)
  1063. # computing the initial conditions for recurrence
  1064. order = sol.order
  1065. all_roots = roots(R.base.to_sympy(sol.listofpoly[-1]), n, filter='Z')
  1066. all_roots = all_roots.keys()
  1067. if all_roots:
  1068. max_root = max(all_roots) + 1
  1069. smallest_n = max(max_root, smallest_n)
  1070. order += smallest_n
  1071. y0 = _extend_y0(self, order)
  1072. u0 = []
  1073. # u(n) = y^n(0)/factorial(n)
  1074. for i, j in enumerate(y0):
  1075. u0.append(j / factorial(i))
  1076. # if sufficient conditions can't be computed then
  1077. # try to use the series method i.e.
  1078. # equate the coefficients of x^k in the equation formed by
  1079. # substituting the series in differential equation, to zero.
  1080. if len(u0) < order:
  1081. for i in range(degree):
  1082. eq = S.Zero
  1083. for j in dict1:
  1084. if i + j[0] < 0:
  1085. dummys[i + j[0]] = S.Zero
  1086. elif i + j[0] < len(u0):
  1087. dummys[i + j[0]] = u0[i + j[0]]
  1088. elif not i + j[0] in dummys:
  1089. dummys[i + j[0]] = Symbol('C_%s' %(i + j[0]))
  1090. unknowns.append(dummys[i + j[0]])
  1091. if j[1] <= i:
  1092. eq += dict1[j].subs(n, i) * dummys[i + j[0]]
  1093. eqs.append(eq)
  1094. # solve the system of equations formed
  1095. soleqs = solve(eqs, *unknowns)
  1096. if isinstance(soleqs, dict):
  1097. for i in range(len(u0), order):
  1098. if i not in dummys:
  1099. dummys[i] = Symbol('C_%s' %i)
  1100. if dummys[i] in soleqs:
  1101. u0.append(soleqs[dummys[i]])
  1102. else:
  1103. u0.append(dummys[i])
  1104. if lb:
  1105. return [(HolonomicSequence(sol, u0), smallest_n)]
  1106. return [HolonomicSequence(sol, u0)]
  1107. for i in range(len(u0), order):
  1108. if i not in dummys:
  1109. dummys[i] = Symbol('C_%s' %i)
  1110. s = False
  1111. for j in soleqs:
  1112. if dummys[i] in j:
  1113. u0.append(j[dummys[i]])
  1114. s = True
  1115. if not s:
  1116. u0.append(dummys[i])
  1117. if lb:
  1118. return [(HolonomicSequence(sol, u0), smallest_n)]
  1119. return [HolonomicSequence(sol, u0)]
  1120. def _frobenius(self, lb=True):
  1121. # compute the roots of indicial equation
  1122. indicialroots = self._indicial()
  1123. reals = []
  1124. compl = []
  1125. for i in ordered(indicialroots.keys()):
  1126. if i.is_real:
  1127. reals.extend([i] * indicialroots[i])
  1128. else:
  1129. a, b = i.as_real_imag()
  1130. compl.extend([(i, a, b)] * indicialroots[i])
  1131. # sort the roots for a fixed ordering of solution
  1132. compl.sort(key=lambda x : x[1])
  1133. compl.sort(key=lambda x : x[2])
  1134. reals.sort()
  1135. # grouping the roots, roots differ by an integer are put in the same group.
  1136. grp = []
  1137. for i in reals:
  1138. intdiff = False
  1139. if len(grp) == 0:
  1140. grp.append([i])
  1141. continue
  1142. for j in grp:
  1143. if int(j[0] - i) == j[0] - i:
  1144. j.append(i)
  1145. intdiff = True
  1146. break
  1147. if not intdiff:
  1148. grp.append([i])
  1149. # True if none of the roots differ by an integer i.e.
  1150. # each element in group have only one member
  1151. independent = True if all(len(i) == 1 for i in grp) else False
  1152. allpos = all(i >= 0 for i in reals)
  1153. allint = all(int(i) == i for i in reals)
  1154. # if initial conditions are provided
  1155. # then use them.
  1156. if self.is_singularics() == True:
  1157. rootstoconsider = []
  1158. for i in ordered(self.y0.keys()):
  1159. for j in ordered(indicialroots.keys()):
  1160. if equal_valued(j, i):
  1161. rootstoconsider.append(i)
  1162. elif allpos and allint:
  1163. rootstoconsider = [min(reals)]
  1164. elif independent:
  1165. rootstoconsider = [i[0] for i in grp] + [j[0] for j in compl]
  1166. elif not allint:
  1167. rootstoconsider = []
  1168. for i in reals:
  1169. if not int(i) == i:
  1170. rootstoconsider.append(i)
  1171. elif not allpos:
  1172. if not self._have_init_cond() or S(self.y0[0]).is_finite == False:
  1173. rootstoconsider = [min(reals)]
  1174. else:
  1175. posroots = []
  1176. for i in reals:
  1177. if i >= 0:
  1178. posroots.append(i)
  1179. rootstoconsider = [min(posroots)]
  1180. n = Symbol('n', integer=True)
  1181. dom = self.annihilator.parent.base.dom
  1182. R, _ = RecurrenceOperators(dom.old_poly_ring(n), 'Sn')
  1183. finalsol = []
  1184. char = ord('C')
  1185. for p in rootstoconsider:
  1186. dict1 = {}
  1187. for i, j in enumerate(self.annihilator.listofpoly):
  1188. listofdmp = j.all_coeffs()
  1189. degree = len(listofdmp) - 1
  1190. for k in range(degree + 1):
  1191. coeff = listofdmp[degree - k]
  1192. if coeff == 0:
  1193. continue
  1194. if (i - k, k - i) in dict1:
  1195. dict1[(i - k, k - i)] += (dom.to_sympy(coeff) * rf(n - k + 1 + p, i))
  1196. else:
  1197. dict1[(i - k, k - i)] = (dom.to_sympy(coeff) * rf(n - k + 1 + p, i))
  1198. sol = []
  1199. keylist = [i[0] for i in dict1]
  1200. lower = min(keylist)
  1201. upper = max(keylist)
  1202. degree = max([i[1] for i in dict1])
  1203. degree2 = min([i[1] for i in dict1])
  1204. smallest_n = lower + degree
  1205. dummys = {}
  1206. eqs = []
  1207. unknowns = []
  1208. for j in range(lower, upper + 1):
  1209. if j in keylist:
  1210. temp = S.Zero
  1211. for k in dict1.keys():
  1212. if k[0] == j:
  1213. temp += dict1[k].subs(n, n - lower)
  1214. sol.append(temp)
  1215. else:
  1216. sol.append(S.Zero)
  1217. # the recurrence relation
  1218. sol = RecurrenceOperator(sol, R)
  1219. # computing the initial conditions for recurrence
  1220. order = sol.order
  1221. all_roots = roots(R.base.to_sympy(sol.listofpoly[-1]), n, filter='Z')
  1222. all_roots = all_roots.keys()
  1223. if all_roots:
  1224. max_root = max(all_roots) + 1
  1225. smallest_n = max(max_root, smallest_n)
  1226. order += smallest_n
  1227. u0 = []
  1228. if self.is_singularics() == True:
  1229. u0 = self.y0[p]
  1230. elif self.is_singularics() == False and p >= 0 and int(p) == p and len(rootstoconsider) == 1:
  1231. y0 = _extend_y0(self, order + int(p))
  1232. # u(n) = y^n(0)/factorial(n)
  1233. if len(y0) > int(p):
  1234. for i in range(int(p), len(y0)):
  1235. u0.append(y0[i] / factorial(i))
  1236. if len(u0) < order:
  1237. for i in range(degree2, degree):
  1238. eq = S.Zero
  1239. for j in dict1:
  1240. if i + j[0] < 0:
  1241. dummys[i + j[0]] = S.Zero
  1242. elif i + j[0] < len(u0):
  1243. dummys[i + j[0]] = u0[i + j[0]]
  1244. elif not i + j[0] in dummys:
  1245. letter = chr(char) + '_%s' %(i + j[0])
  1246. dummys[i + j[0]] = Symbol(letter)
  1247. unknowns.append(dummys[i + j[0]])
  1248. if j[1] <= i:
  1249. eq += dict1[j].subs(n, i) * dummys[i + j[0]]
  1250. eqs.append(eq)
  1251. # solve the system of equations formed
  1252. soleqs = solve(eqs, *unknowns)
  1253. if isinstance(soleqs, dict):
  1254. for i in range(len(u0), order):
  1255. if i not in dummys:
  1256. letter = chr(char) + '_%s' %i
  1257. dummys[i] = Symbol(letter)
  1258. if dummys[i] in soleqs:
  1259. u0.append(soleqs[dummys[i]])
  1260. else:
  1261. u0.append(dummys[i])
  1262. if lb:
  1263. finalsol.append((HolonomicSequence(sol, u0), p, smallest_n))
  1264. continue
  1265. else:
  1266. finalsol.append((HolonomicSequence(sol, u0), p))
  1267. continue
  1268. for i in range(len(u0), order):
  1269. if i not in dummys:
  1270. letter = chr(char) + '_%s' %i
  1271. dummys[i] = Symbol(letter)
  1272. s = False
  1273. for j in soleqs:
  1274. if dummys[i] in j:
  1275. u0.append(j[dummys[i]])
  1276. s = True
  1277. if not s:
  1278. u0.append(dummys[i])
  1279. if lb:
  1280. finalsol.append((HolonomicSequence(sol, u0), p, smallest_n))
  1281. else:
  1282. finalsol.append((HolonomicSequence(sol, u0), p))
  1283. char += 1
  1284. return finalsol
  1285. def series(self, n=6, coefficient=False, order=True, _recur=None):
  1286. r"""
  1287. Finds the power series expansion of given holonomic function about :math:`x_0`.
  1288. Explanation
  1289. ===========
  1290. A list of series might be returned if :math:`x_0` is a regular point with
  1291. multiple roots of the indicial equation.
  1292. Examples
  1293. ========
  1294. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  1295. >>> from sympy import QQ
  1296. >>> from sympy import symbols
  1297. >>> x = symbols('x')
  1298. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  1299. >>> HolonomicFunction(Dx - 1, x, 0, [1]).series() # e^x
  1300. 1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120 + O(x**6)
  1301. >>> HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).series(n=8) # sin(x)
  1302. x - x**3/6 + x**5/120 - x**7/5040 + O(x**8)
  1303. See Also
  1304. ========
  1305. HolonomicFunction.to_sequence
  1306. """
  1307. if _recur is None:
  1308. recurrence = self.to_sequence()
  1309. else:
  1310. recurrence = _recur
  1311. if isinstance(recurrence, tuple) and len(recurrence) == 2:
  1312. recurrence = recurrence[0]
  1313. constantpower = 0
  1314. elif isinstance(recurrence, tuple) and len(recurrence) == 3:
  1315. constantpower = recurrence[1]
  1316. recurrence = recurrence[0]
  1317. elif len(recurrence) == 1 and len(recurrence[0]) == 2:
  1318. recurrence = recurrence[0][0]
  1319. constantpower = 0
  1320. elif len(recurrence) == 1 and len(recurrence[0]) == 3:
  1321. constantpower = recurrence[0][1]
  1322. recurrence = recurrence[0][0]
  1323. else:
  1324. sol = []
  1325. for i in recurrence:
  1326. sol.append(self.series(_recur=i))
  1327. return sol
  1328. n = n - int(constantpower)
  1329. l = len(recurrence.u0) - 1
  1330. k = recurrence.recurrence.order
  1331. x = self.x
  1332. x0 = self.x0
  1333. seq_dmp = recurrence.recurrence.listofpoly
  1334. R = recurrence.recurrence.parent.base
  1335. K = R.get_field()
  1336. seq = []
  1337. for i, j in enumerate(seq_dmp):
  1338. seq.append(K.new(j.rep))
  1339. sub = [-seq[i] / seq[k] for i in range(k)]
  1340. sol = list(recurrence.u0)
  1341. if l + 1 >= n:
  1342. pass
  1343. else:
  1344. # use the initial conditions to find the next term
  1345. for i in range(l + 1 - k, n - k):
  1346. coeff = S.Zero
  1347. for j in range(k):
  1348. if i + j >= 0:
  1349. coeff += DMFsubs(sub[j], i) * sol[i + j]
  1350. sol.append(coeff)
  1351. if coefficient:
  1352. return sol
  1353. ser = S.Zero
  1354. for i, j in enumerate(sol):
  1355. ser += x**(i + constantpower) * j
  1356. if order:
  1357. ser += Order(x**(n + int(constantpower)), x)
  1358. if x0 != 0:
  1359. return ser.subs(x, x - x0)
  1360. return ser
  1361. def _indicial(self):
  1362. """
  1363. Computes roots of the Indicial equation.
  1364. """
  1365. if self.x0 != 0:
  1366. return self.shift_x(self.x0)._indicial()
  1367. list_coeff = self.annihilator.listofpoly
  1368. R = self.annihilator.parent.base
  1369. x = self.x
  1370. s = R.zero
  1371. y = R.one
  1372. def _pole_degree(poly):
  1373. root_all = roots(R.to_sympy(poly), x, filter='Z')
  1374. if 0 in root_all.keys():
  1375. return root_all[0]
  1376. else:
  1377. return 0
  1378. degree = [j.degree() for j in list_coeff]
  1379. degree = max(degree)
  1380. inf = 10 * (max(1, degree) + max(1, self.annihilator.order))
  1381. deg = lambda q: inf if q.is_zero else _pole_degree(q)
  1382. b = deg(list_coeff[0])
  1383. for j in range(1, len(list_coeff)):
  1384. b = min(b, deg(list_coeff[j]) - j)
  1385. for i, j in enumerate(list_coeff):
  1386. listofdmp = j.all_coeffs()
  1387. degree = len(listofdmp) - 1
  1388. if - i - b <= 0 and degree - i - b >= 0:
  1389. s = s + listofdmp[degree - i - b] * y
  1390. y *= x - i
  1391. return roots(R.to_sympy(s), x)
  1392. def evalf(self, points, method='RK4', h=0.05, derivatives=False):
  1393. r"""
  1394. Finds numerical value of a holonomic function using numerical methods.
  1395. (RK4 by default). A set of points (real or complex) must be provided
  1396. which will be the path for the numerical integration.
  1397. Explanation
  1398. ===========
  1399. The path should be given as a list :math:`[x_1, x_2, \dots x_n]`. The numerical
  1400. values will be computed at each point in this order
  1401. :math:`x_1 \rightarrow x_2 \rightarrow x_3 \dots \rightarrow x_n`.
  1402. Returns values of the function at :math:`x_1, x_2, \dots x_n` in a list.
  1403. Examples
  1404. ========
  1405. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  1406. >>> from sympy import QQ
  1407. >>> from sympy import symbols
  1408. >>> x = symbols('x')
  1409. >>> R, Dx = DifferentialOperators(QQ.old_poly_ring(x),'Dx')
  1410. A straight line on the real axis from (0 to 1)
  1411. >>> r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
  1412. Runge-Kutta 4th order on e^x from 0.1 to 1.
  1413. Exact solution at 1 is 2.71828182845905
  1414. >>> HolonomicFunction(Dx - 1, x, 0, [1]).evalf(r)
  1415. [1.10517083333333, 1.22140257085069, 1.34985849706254, 1.49182424008069,
  1416. 1.64872063859684, 1.82211796209193, 2.01375162659678, 2.22553956329232,
  1417. 2.45960141378007, 2.71827974413517]
  1418. Euler's method for the same
  1419. >>> HolonomicFunction(Dx - 1, x, 0, [1]).evalf(r, method='Euler')
  1420. [1.1, 1.21, 1.331, 1.4641, 1.61051, 1.771561, 1.9487171, 2.14358881,
  1421. 2.357947691, 2.5937424601]
  1422. One can also observe that the value obtained using Runge-Kutta 4th order
  1423. is much more accurate than Euler's method.
  1424. """
  1425. from sympy.holonomic.numerical import _evalf
  1426. lp = False
  1427. # if a point `b` is given instead of a mesh
  1428. if not hasattr(points, "__iter__"):
  1429. lp = True
  1430. b = S(points)
  1431. if self.x0 == b:
  1432. return _evalf(self, [b], method=method, derivatives=derivatives)[-1]
  1433. if not b.is_Number:
  1434. raise NotImplementedError
  1435. a = self.x0
  1436. if a > b:
  1437. h = -h
  1438. n = int((b - a) / h)
  1439. points = [a + h]
  1440. for i in range(n - 1):
  1441. points.append(points[-1] + h)
  1442. for i in roots(self.annihilator.parent.base.to_sympy(self.annihilator.listofpoly[-1]), self.x):
  1443. if i == self.x0 or i in points:
  1444. raise SingularityError(self, i)
  1445. if lp:
  1446. return _evalf(self, points, method=method, derivatives=derivatives)[-1]
  1447. return _evalf(self, points, method=method, derivatives=derivatives)
  1448. def change_x(self, z):
  1449. """
  1450. Changes only the variable of Holonomic Function, for internal
  1451. purposes. For composition use HolonomicFunction.composition()
  1452. """
  1453. dom = self.annihilator.parent.base.dom
  1454. R = dom.old_poly_ring(z)
  1455. parent, _ = DifferentialOperators(R, 'Dx')
  1456. sol = []
  1457. for j in self.annihilator.listofpoly:
  1458. sol.append(R(j.rep))
  1459. sol = DifferentialOperator(sol, parent)
  1460. return HolonomicFunction(sol, z, self.x0, self.y0)
  1461. def shift_x(self, a):
  1462. """
  1463. Substitute `x + a` for `x`.
  1464. """
  1465. x = self.x
  1466. listaftershift = self.annihilator.listofpoly
  1467. base = self.annihilator.parent.base
  1468. sol = [base.from_sympy(base.to_sympy(i).subs(x, x + a)) for i in listaftershift]
  1469. sol = DifferentialOperator(sol, self.annihilator.parent)
  1470. x0 = self.x0 - a
  1471. if not self._have_init_cond():
  1472. return HolonomicFunction(sol, x)
  1473. return HolonomicFunction(sol, x, x0, self.y0)
  1474. def to_hyper(self, as_list=False, _recur=None):
  1475. r"""
  1476. Returns a hypergeometric function (or linear combination of them)
  1477. representing the given holonomic function.
  1478. Explanation
  1479. ===========
  1480. Returns an answer of the form:
  1481. `a_1 \cdot x^{b_1} \cdot{hyper()} + a_2 \cdot x^{b_2} \cdot{hyper()} \dots`
  1482. This is very useful as one can now use ``hyperexpand`` to find the
  1483. symbolic expressions/functions.
  1484. Examples
  1485. ========
  1486. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  1487. >>> from sympy import ZZ
  1488. >>> from sympy import symbols
  1489. >>> x = symbols('x')
  1490. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x),'Dx')
  1491. >>> # sin(x)
  1492. >>> HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).to_hyper()
  1493. x*hyper((), (3/2,), -x**2/4)
  1494. >>> # exp(x)
  1495. >>> HolonomicFunction(Dx - 1, x, 0, [1]).to_hyper()
  1496. hyper((), (), x)
  1497. See Also
  1498. ========
  1499. from_hyper, from_meijerg
  1500. """
  1501. if _recur is None:
  1502. recurrence = self.to_sequence()
  1503. else:
  1504. recurrence = _recur
  1505. if isinstance(recurrence, tuple) and len(recurrence) == 2:
  1506. smallest_n = recurrence[1]
  1507. recurrence = recurrence[0]
  1508. constantpower = 0
  1509. elif isinstance(recurrence, tuple) and len(recurrence) == 3:
  1510. smallest_n = recurrence[2]
  1511. constantpower = recurrence[1]
  1512. recurrence = recurrence[0]
  1513. elif len(recurrence) == 1 and len(recurrence[0]) == 2:
  1514. smallest_n = recurrence[0][1]
  1515. recurrence = recurrence[0][0]
  1516. constantpower = 0
  1517. elif len(recurrence) == 1 and len(recurrence[0]) == 3:
  1518. smallest_n = recurrence[0][2]
  1519. constantpower = recurrence[0][1]
  1520. recurrence = recurrence[0][0]
  1521. else:
  1522. sol = self.to_hyper(as_list=as_list, _recur=recurrence[0])
  1523. for i in recurrence[1:]:
  1524. sol += self.to_hyper(as_list=as_list, _recur=i)
  1525. return sol
  1526. u0 = recurrence.u0
  1527. r = recurrence.recurrence
  1528. x = self.x
  1529. x0 = self.x0
  1530. # order of the recurrence relation
  1531. m = r.order
  1532. # when no recurrence exists, and the power series have finite terms
  1533. if m == 0:
  1534. nonzeroterms = roots(r.parent.base.to_sympy(r.listofpoly[0]), recurrence.n, filter='R')
  1535. sol = S.Zero
  1536. for j, i in enumerate(nonzeroterms):
  1537. if i < 0 or int(i) != i:
  1538. continue
  1539. i = int(i)
  1540. if i < len(u0):
  1541. if isinstance(u0[i], (PolyElement, FracElement)):
  1542. u0[i] = u0[i].as_expr()
  1543. sol += u0[i] * x**i
  1544. else:
  1545. sol += Symbol('C_%s' %j) * x**i
  1546. if isinstance(sol, (PolyElement, FracElement)):
  1547. sol = sol.as_expr() * x**constantpower
  1548. else:
  1549. sol = sol * x**constantpower
  1550. if as_list:
  1551. if x0 != 0:
  1552. return [(sol.subs(x, x - x0), )]
  1553. return [(sol, )]
  1554. if x0 != 0:
  1555. return sol.subs(x, x - x0)
  1556. return sol
  1557. if smallest_n + m > len(u0):
  1558. raise NotImplementedError("Can't compute sufficient Initial Conditions")
  1559. # check if the recurrence represents a hypergeometric series
  1560. is_hyper = True
  1561. for i in range(1, len(r.listofpoly)-1):
  1562. if r.listofpoly[i] != r.parent.base.zero:
  1563. is_hyper = False
  1564. break
  1565. if not is_hyper:
  1566. raise NotHyperSeriesError(self, self.x0)
  1567. a = r.listofpoly[0]
  1568. b = r.listofpoly[-1]
  1569. # the constant multiple of argument of hypergeometric function
  1570. if isinstance(a.rep[0], (PolyElement, FracElement)):
  1571. c = - (S(a.rep[0].as_expr()) * m**(a.degree())) / (S(b.rep[0].as_expr()) * m**(b.degree()))
  1572. else:
  1573. c = - (S(a.rep[0]) * m**(a.degree())) / (S(b.rep[0]) * m**(b.degree()))
  1574. sol = 0
  1575. arg1 = roots(r.parent.base.to_sympy(a), recurrence.n)
  1576. arg2 = roots(r.parent.base.to_sympy(b), recurrence.n)
  1577. # iterate through the initial conditions to find
  1578. # the hypergeometric representation of the given
  1579. # function.
  1580. # The answer will be a linear combination
  1581. # of different hypergeometric series which satisfies
  1582. # the recurrence.
  1583. if as_list:
  1584. listofsol = []
  1585. for i in range(smallest_n + m):
  1586. # if the recurrence relation doesn't hold for `n = i`,
  1587. # then a Hypergeometric representation doesn't exist.
  1588. # add the algebraic term a * x**i to the solution,
  1589. # where a is u0[i]
  1590. if i < smallest_n:
  1591. if as_list:
  1592. listofsol.append(((S(u0[i]) * x**(i+constantpower)).subs(x, x-x0), ))
  1593. else:
  1594. sol += S(u0[i]) * x**i
  1595. continue
  1596. # if the coefficient u0[i] is zero, then the
  1597. # independent hypergeomtric series starting with
  1598. # x**i is not a part of the answer.
  1599. if S(u0[i]) == 0:
  1600. continue
  1601. ap = []
  1602. bq = []
  1603. # substitute m * n + i for n
  1604. for k in ordered(arg1.keys()):
  1605. ap.extend([nsimplify((i - k) / m)] * arg1[k])
  1606. for k in ordered(arg2.keys()):
  1607. bq.extend([nsimplify((i - k) / m)] * arg2[k])
  1608. # convention of (k + 1) in the denominator
  1609. if 1 in bq:
  1610. bq.remove(1)
  1611. else:
  1612. ap.append(1)
  1613. if as_list:
  1614. listofsol.append(((S(u0[i])*x**(i+constantpower)).subs(x, x-x0), (hyper(ap, bq, c*x**m)).subs(x, x-x0)))
  1615. else:
  1616. sol += S(u0[i]) * hyper(ap, bq, c * x**m) * x**i
  1617. if as_list:
  1618. return listofsol
  1619. sol = sol * x**constantpower
  1620. if x0 != 0:
  1621. return sol.subs(x, x - x0)
  1622. return sol
  1623. def to_expr(self):
  1624. """
  1625. Converts a Holonomic Function back to elementary functions.
  1626. Examples
  1627. ========
  1628. >>> from sympy.holonomic.holonomic import HolonomicFunction, DifferentialOperators
  1629. >>> from sympy import ZZ
  1630. >>> from sympy import symbols, S
  1631. >>> x = symbols('x')
  1632. >>> R, Dx = DifferentialOperators(ZZ.old_poly_ring(x),'Dx')
  1633. >>> HolonomicFunction(x**2*Dx**2 + x*Dx + (x**2 - 1), x, 0, [0, S(1)/2]).to_expr()
  1634. besselj(1, x)
  1635. >>> HolonomicFunction((1 + x)*Dx**3 + Dx**2, x, 0, [1, 1, 1]).to_expr()
  1636. x*log(x + 1) + log(x + 1) + 1
  1637. """
  1638. return hyperexpand(self.to_hyper()).simplify()
  1639. def change_ics(self, b, lenics=None):
  1640. """
  1641. Changes the point `x0` to ``b`` for initial conditions.
  1642. Examples
  1643. ========
  1644. >>> from sympy.holonomic import expr_to_holonomic
  1645. >>> from sympy import symbols, sin, exp
  1646. >>> x = symbols('x')
  1647. >>> expr_to_holonomic(sin(x)).change_ics(1)
  1648. HolonomicFunction((1) + (1)*Dx**2, x, 1, [sin(1), cos(1)])
  1649. >>> expr_to_holonomic(exp(x)).change_ics(2)
  1650. HolonomicFunction((-1) + (1)*Dx, x, 2, [exp(2)])
  1651. """
  1652. symbolic = True
  1653. if lenics is None and len(self.y0) > self.annihilator.order:
  1654. lenics = len(self.y0)
  1655. dom = self.annihilator.parent.base.domain
  1656. try:
  1657. sol = expr_to_holonomic(self.to_expr(), x=self.x, x0=b, lenics=lenics, domain=dom)
  1658. except (NotPowerSeriesError, NotHyperSeriesError):
  1659. symbolic = False
  1660. if symbolic and sol.x0 == b:
  1661. return sol
  1662. y0 = self.evalf(b, derivatives=True)
  1663. return HolonomicFunction(self.annihilator, self.x, b, y0)
  1664. def to_meijerg(self):
  1665. """
  1666. Returns a linear combination of Meijer G-functions.
  1667. Examples
  1668. ========
  1669. >>> from sympy.holonomic import expr_to_holonomic
  1670. >>> from sympy import sin, cos, hyperexpand, log, symbols
  1671. >>> x = symbols('x')
  1672. >>> hyperexpand(expr_to_holonomic(cos(x) + sin(x)).to_meijerg())
  1673. sin(x) + cos(x)
  1674. >>> hyperexpand(expr_to_holonomic(log(x)).to_meijerg()).simplify()
  1675. log(x)
  1676. See Also
  1677. ========
  1678. to_hyper
  1679. """
  1680. # convert to hypergeometric first
  1681. rep = self.to_hyper(as_list=True)
  1682. sol = S.Zero
  1683. for i in rep:
  1684. if len(i) == 1:
  1685. sol += i[0]
  1686. elif len(i) == 2:
  1687. sol += i[0] * _hyper_to_meijerg(i[1])
  1688. return sol
  1689. def from_hyper(func, x0=0, evalf=False):
  1690. r"""
  1691. Converts a hypergeometric function to holonomic.
  1692. ``func`` is the Hypergeometric Function and ``x0`` is the point at
  1693. which initial conditions are required.
  1694. Examples
  1695. ========
  1696. >>> from sympy.holonomic.holonomic import from_hyper
  1697. >>> from sympy import symbols, hyper, S
  1698. >>> x = symbols('x')
  1699. >>> from_hyper(hyper([], [S(3)/2], x**2/4))
  1700. HolonomicFunction((-x) + (2)*Dx + (x)*Dx**2, x, 1, [sinh(1), -sinh(1) + cosh(1)])
  1701. """
  1702. a = func.ap
  1703. b = func.bq
  1704. z = func.args[2]
  1705. x = z.atoms(Symbol).pop()
  1706. R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
  1707. # generalized hypergeometric differential equation
  1708. xDx = x*Dx
  1709. r1 = 1
  1710. for ai in a: # XXX gives sympify error if Mul is used with list of all factors
  1711. r1 *= xDx + ai
  1712. xDx_1 = xDx - 1
  1713. # r2 = Mul(*([Dx] + [xDx_1 + bi for bi in b])) # XXX gives sympify error
  1714. r2 = Dx
  1715. for bi in b:
  1716. r2 *= xDx_1 + bi
  1717. sol = r1 - r2
  1718. simp = hyperexpand(func)
  1719. if simp in (Infinity, NegativeInfinity):
  1720. return HolonomicFunction(sol, x).composition(z)
  1721. def _find_conditions(simp, x, x0, order, evalf=False):
  1722. y0 = []
  1723. for i in range(order):
  1724. if evalf:
  1725. val = simp.subs(x, x0).evalf()
  1726. else:
  1727. val = simp.subs(x, x0)
  1728. # return None if it is Infinite or NaN
  1729. if val.is_finite is False or isinstance(val, NaN):
  1730. return None
  1731. y0.append(val)
  1732. simp = simp.diff(x)
  1733. return y0
  1734. # if the function is known symbolically
  1735. if not isinstance(simp, hyper):
  1736. y0 = _find_conditions(simp, x, x0, sol.order)
  1737. while not y0:
  1738. # if values don't exist at 0, then try to find initial
  1739. # conditions at 1. If it doesn't exist at 1 too then
  1740. # try 2 and so on.
  1741. x0 += 1
  1742. y0 = _find_conditions(simp, x, x0, sol.order)
  1743. return HolonomicFunction(sol, x).composition(z, x0, y0)
  1744. if isinstance(simp, hyper):
  1745. x0 = 1
  1746. # use evalf if the function can't be simplified
  1747. y0 = _find_conditions(simp, x, x0, sol.order, evalf)
  1748. while not y0:
  1749. x0 += 1
  1750. y0 = _find_conditions(simp, x, x0, sol.order, evalf)
  1751. return HolonomicFunction(sol, x).composition(z, x0, y0)
  1752. return HolonomicFunction(sol, x).composition(z)
  1753. def from_meijerg(func, x0=0, evalf=False, initcond=True, domain=QQ):
  1754. """
  1755. Converts a Meijer G-function to Holonomic.
  1756. ``func`` is the G-Function and ``x0`` is the point at
  1757. which initial conditions are required.
  1758. Examples
  1759. ========
  1760. >>> from sympy.holonomic.holonomic import from_meijerg
  1761. >>> from sympy import symbols, meijerg, S
  1762. >>> x = symbols('x')
  1763. >>> from_meijerg(meijerg(([], []), ([S(1)/2], [0]), x**2/4))
  1764. HolonomicFunction((1) + (1)*Dx**2, x, 0, [0, 1/sqrt(pi)])
  1765. """
  1766. a = func.ap
  1767. b = func.bq
  1768. n = len(func.an)
  1769. m = len(func.bm)
  1770. p = len(a)
  1771. z = func.args[2]
  1772. x = z.atoms(Symbol).pop()
  1773. R, Dx = DifferentialOperators(domain.old_poly_ring(x), 'Dx')
  1774. # compute the differential equation satisfied by the
  1775. # Meijer G-function.
  1776. xDx = x*Dx
  1777. xDx1 = xDx + 1
  1778. r1 = x*(-1)**(m + n - p)
  1779. for ai in a: # XXX gives sympify error if args given in list
  1780. r1 *= xDx1 - ai
  1781. # r2 = Mul(*[xDx - bi for bi in b]) # gives sympify error
  1782. r2 = 1
  1783. for bi in b:
  1784. r2 *= xDx - bi
  1785. sol = r1 - r2
  1786. if not initcond:
  1787. return HolonomicFunction(sol, x).composition(z)
  1788. simp = hyperexpand(func)
  1789. if simp in (Infinity, NegativeInfinity):
  1790. return HolonomicFunction(sol, x).composition(z)
  1791. def _find_conditions(simp, x, x0, order, evalf=False):
  1792. y0 = []
  1793. for i in range(order):
  1794. if evalf:
  1795. val = simp.subs(x, x0).evalf()
  1796. else:
  1797. val = simp.subs(x, x0)
  1798. if val.is_finite is False or isinstance(val, NaN):
  1799. return None
  1800. y0.append(val)
  1801. simp = simp.diff(x)
  1802. return y0
  1803. # computing initial conditions
  1804. if not isinstance(simp, meijerg):
  1805. y0 = _find_conditions(simp, x, x0, sol.order)
  1806. while not y0:
  1807. x0 += 1
  1808. y0 = _find_conditions(simp, x, x0, sol.order)
  1809. return HolonomicFunction(sol, x).composition(z, x0, y0)
  1810. if isinstance(simp, meijerg):
  1811. x0 = 1
  1812. y0 = _find_conditions(simp, x, x0, sol.order, evalf)
  1813. while not y0:
  1814. x0 += 1
  1815. y0 = _find_conditions(simp, x, x0, sol.order, evalf)
  1816. return HolonomicFunction(sol, x).composition(z, x0, y0)
  1817. return HolonomicFunction(sol, x).composition(z)
  1818. x_1 = Dummy('x_1')
  1819. _lookup_table = None
  1820. domain_for_table = None
  1821. from sympy.integrals.meijerint import _mytype
  1822. def expr_to_holonomic(func, x=None, x0=0, y0=None, lenics=None, domain=None, initcond=True):
  1823. """
  1824. Converts a function or an expression to a holonomic function.
  1825. Parameters
  1826. ==========
  1827. func:
  1828. The expression to be converted.
  1829. x:
  1830. variable for the function.
  1831. x0:
  1832. point at which initial condition must be computed.
  1833. y0:
  1834. One can optionally provide initial condition if the method
  1835. is not able to do it automatically.
  1836. lenics:
  1837. Number of terms in the initial condition. By default it is
  1838. equal to the order of the annihilator.
  1839. domain:
  1840. Ground domain for the polynomials in ``x`` appearing as coefficients
  1841. in the annihilator.
  1842. initcond:
  1843. Set it false if you do not want the initial conditions to be computed.
  1844. Examples
  1845. ========
  1846. >>> from sympy.holonomic.holonomic import expr_to_holonomic
  1847. >>> from sympy import sin, exp, symbols
  1848. >>> x = symbols('x')
  1849. >>> expr_to_holonomic(sin(x))
  1850. HolonomicFunction((1) + (1)*Dx**2, x, 0, [0, 1])
  1851. >>> expr_to_holonomic(exp(x))
  1852. HolonomicFunction((-1) + (1)*Dx, x, 0, [1])
  1853. See Also
  1854. ========
  1855. sympy.integrals.meijerint._rewrite1, _convert_poly_rat_alg, _create_table
  1856. """
  1857. func = sympify(func)
  1858. syms = func.free_symbols
  1859. if not x:
  1860. if len(syms) == 1:
  1861. x= syms.pop()
  1862. else:
  1863. raise ValueError("Specify the variable for the function")
  1864. elif x in syms:
  1865. syms.remove(x)
  1866. extra_syms = list(syms)
  1867. if domain is None:
  1868. if func.has(Float):
  1869. domain = RR
  1870. else:
  1871. domain = QQ
  1872. if len(extra_syms) != 0:
  1873. domain = domain[extra_syms].get_field()
  1874. # try to convert if the function is polynomial or rational
  1875. solpoly = _convert_poly_rat_alg(func, x, x0=x0, y0=y0, lenics=lenics, domain=domain, initcond=initcond)
  1876. if solpoly:
  1877. return solpoly
  1878. # create the lookup table
  1879. global _lookup_table, domain_for_table
  1880. if not _lookup_table:
  1881. domain_for_table = domain
  1882. _lookup_table = {}
  1883. _create_table(_lookup_table, domain=domain)
  1884. elif domain != domain_for_table:
  1885. domain_for_table = domain
  1886. _lookup_table = {}
  1887. _create_table(_lookup_table, domain=domain)
  1888. # use the table directly to convert to Holonomic
  1889. if func.is_Function:
  1890. f = func.subs(x, x_1)
  1891. t = _mytype(f, x_1)
  1892. if t in _lookup_table:
  1893. l = _lookup_table[t]
  1894. sol = l[0][1].change_x(x)
  1895. else:
  1896. sol = _convert_meijerint(func, x, initcond=False, domain=domain)
  1897. if not sol:
  1898. raise NotImplementedError
  1899. if y0:
  1900. sol.y0 = y0
  1901. if y0 or not initcond:
  1902. sol.x0 = x0
  1903. return sol
  1904. if not lenics:
  1905. lenics = sol.annihilator.order
  1906. _y0 = _find_conditions(func, x, x0, lenics)
  1907. while not _y0:
  1908. x0 += 1
  1909. _y0 = _find_conditions(func, x, x0, lenics)
  1910. return HolonomicFunction(sol.annihilator, x, x0, _y0)
  1911. if y0 or not initcond:
  1912. sol = sol.composition(func.args[0])
  1913. if y0:
  1914. sol.y0 = y0
  1915. sol.x0 = x0
  1916. return sol
  1917. if not lenics:
  1918. lenics = sol.annihilator.order
  1919. _y0 = _find_conditions(func, x, x0, lenics)
  1920. while not _y0:
  1921. x0 += 1
  1922. _y0 = _find_conditions(func, x, x0, lenics)
  1923. return sol.composition(func.args[0], x0, _y0)
  1924. # iterate through the expression recursively
  1925. args = func.args
  1926. f = func.func
  1927. sol = expr_to_holonomic(args[0], x=x, initcond=False, domain=domain)
  1928. if f is Add:
  1929. for i in range(1, len(args)):
  1930. sol += expr_to_holonomic(args[i], x=x, initcond=False, domain=domain)
  1931. elif f is Mul:
  1932. for i in range(1, len(args)):
  1933. sol *= expr_to_holonomic(args[i], x=x, initcond=False, domain=domain)
  1934. elif f is Pow:
  1935. sol = sol**args[1]
  1936. sol.x0 = x0
  1937. if not sol:
  1938. raise NotImplementedError
  1939. if y0:
  1940. sol.y0 = y0
  1941. if y0 or not initcond:
  1942. return sol
  1943. if sol.y0:
  1944. return sol
  1945. if not lenics:
  1946. lenics = sol.annihilator.order
  1947. if sol.annihilator.is_singular(x0):
  1948. r = sol._indicial()
  1949. l = list(r)
  1950. if len(r) == 1 and r[l[0]] == S.One:
  1951. r = l[0]
  1952. g = func / (x - x0)**r
  1953. singular_ics = _find_conditions(g, x, x0, lenics)
  1954. singular_ics = [j / factorial(i) for i, j in enumerate(singular_ics)]
  1955. y0 = {r:singular_ics}
  1956. return HolonomicFunction(sol.annihilator, x, x0, y0)
  1957. _y0 = _find_conditions(func, x, x0, lenics)
  1958. while not _y0:
  1959. x0 += 1
  1960. _y0 = _find_conditions(func, x, x0, lenics)
  1961. return HolonomicFunction(sol.annihilator, x, x0, _y0)
  1962. ## Some helper functions ##
  1963. def _normalize(list_of, parent, negative=True):
  1964. """
  1965. Normalize a given annihilator
  1966. """
  1967. num = []
  1968. denom = []
  1969. base = parent.base
  1970. K = base.get_field()
  1971. lcm_denom = base.from_sympy(S.One)
  1972. list_of_coeff = []
  1973. # convert polynomials to the elements of associated
  1974. # fraction field
  1975. for i, j in enumerate(list_of):
  1976. if isinstance(j, base.dtype):
  1977. list_of_coeff.append(K.new(j.rep))
  1978. elif not isinstance(j, K.dtype):
  1979. list_of_coeff.append(K.from_sympy(sympify(j)))
  1980. else:
  1981. list_of_coeff.append(j)
  1982. # corresponding numerators of the sequence of polynomials
  1983. num.append(list_of_coeff[i].numer())
  1984. # corresponding denominators
  1985. denom.append(list_of_coeff[i].denom())
  1986. # lcm of denominators in the coefficients
  1987. for i in denom:
  1988. lcm_denom = i.lcm(lcm_denom)
  1989. if negative:
  1990. lcm_denom = -lcm_denom
  1991. lcm_denom = K.new(lcm_denom.rep)
  1992. # multiply the coefficients with lcm
  1993. for i, j in enumerate(list_of_coeff):
  1994. list_of_coeff[i] = j * lcm_denom
  1995. gcd_numer = base((list_of_coeff[-1].numer() / list_of_coeff[-1].denom()).rep)
  1996. # gcd of numerators in the coefficients
  1997. for i in num:
  1998. gcd_numer = i.gcd(gcd_numer)
  1999. gcd_numer = K.new(gcd_numer.rep)
  2000. # divide all the coefficients by the gcd
  2001. for i, j in enumerate(list_of_coeff):
  2002. frac_ans = j / gcd_numer
  2003. list_of_coeff[i] = base((frac_ans.numer() / frac_ans.denom()).rep)
  2004. return DifferentialOperator(list_of_coeff, parent)
  2005. def _derivate_diff_eq(listofpoly):
  2006. """
  2007. Let a differential equation a0(x)y(x) + a1(x)y'(x) + ... = 0
  2008. where a0, a1,... are polynomials or rational functions. The function
  2009. returns b0, b1, b2... such that the differential equation
  2010. b0(x)y(x) + b1(x)y'(x) +... = 0 is formed after differentiating the
  2011. former equation.
  2012. """
  2013. sol = []
  2014. a = len(listofpoly) - 1
  2015. sol.append(DMFdiff(listofpoly[0]))
  2016. for i, j in enumerate(listofpoly[1:]):
  2017. sol.append(DMFdiff(j) + listofpoly[i])
  2018. sol.append(listofpoly[a])
  2019. return sol
  2020. def _hyper_to_meijerg(func):
  2021. """
  2022. Converts a `hyper` to meijerg.
  2023. """
  2024. ap = func.ap
  2025. bq = func.bq
  2026. ispoly = any(i <= 0 and int(i) == i for i in ap)
  2027. if ispoly:
  2028. return hyperexpand(func)
  2029. z = func.args[2]
  2030. # parameters of the `meijerg` function.
  2031. an = (1 - i for i in ap)
  2032. anp = ()
  2033. bm = (S.Zero, )
  2034. bmq = (1 - i for i in bq)
  2035. k = S.One
  2036. for i in bq:
  2037. k = k * gamma(i)
  2038. for i in ap:
  2039. k = k / gamma(i)
  2040. return k * meijerg(an, anp, bm, bmq, -z)
  2041. def _add_lists(list1, list2):
  2042. """Takes polynomial sequences of two annihilators a and b and returns
  2043. the list of polynomials of sum of a and b.
  2044. """
  2045. if len(list1) <= len(list2):
  2046. sol = [a + b for a, b in zip(list1, list2)] + list2[len(list1):]
  2047. else:
  2048. sol = [a + b for a, b in zip(list1, list2)] + list1[len(list2):]
  2049. return sol
  2050. def _extend_y0(Holonomic, n):
  2051. """
  2052. Tries to find more initial conditions by substituting the initial
  2053. value point in the differential equation.
  2054. """
  2055. if Holonomic.annihilator.is_singular(Holonomic.x0) or Holonomic.is_singularics() == True:
  2056. return Holonomic.y0
  2057. annihilator = Holonomic.annihilator
  2058. a = annihilator.order
  2059. listofpoly = []
  2060. y0 = Holonomic.y0
  2061. R = annihilator.parent.base
  2062. K = R.get_field()
  2063. for i, j in enumerate(annihilator.listofpoly):
  2064. if isinstance(j, annihilator.parent.base.dtype):
  2065. listofpoly.append(K.new(j.rep))
  2066. if len(y0) < a or n <= len(y0):
  2067. return y0
  2068. else:
  2069. list_red = [-listofpoly[i] / listofpoly[a]
  2070. for i in range(a)]
  2071. if len(y0) > a:
  2072. y1 = [y0[i] for i in range(a)]
  2073. else:
  2074. y1 = list(y0)
  2075. for i in range(n - a):
  2076. sol = 0
  2077. for a, b in zip(y1, list_red):
  2078. r = DMFsubs(b, Holonomic.x0)
  2079. if not getattr(r, 'is_finite', True):
  2080. return y0
  2081. if isinstance(r, (PolyElement, FracElement)):
  2082. r = r.as_expr()
  2083. sol += a * r
  2084. y1.append(sol)
  2085. list_red = _derivate_diff_eq(list_red)
  2086. return y0 + y1[len(y0):]
  2087. def DMFdiff(frac):
  2088. # differentiate a DMF object represented as p/q
  2089. if not isinstance(frac, DMF):
  2090. return frac.diff()
  2091. K = frac.ring
  2092. p = K.numer(frac)
  2093. q = K.denom(frac)
  2094. sol_num = - p * q.diff() + q * p.diff()
  2095. sol_denom = q**2
  2096. return K((sol_num.rep, sol_denom.rep))
  2097. def DMFsubs(frac, x0, mpm=False):
  2098. # substitute the point x0 in DMF object of the form p/q
  2099. if not isinstance(frac, DMF):
  2100. return frac
  2101. p = frac.num
  2102. q = frac.den
  2103. sol_p = S.Zero
  2104. sol_q = S.Zero
  2105. if mpm:
  2106. from mpmath import mp
  2107. for i, j in enumerate(reversed(p)):
  2108. if mpm:
  2109. j = sympify(j)._to_mpmath(mp.prec)
  2110. sol_p += j * x0**i
  2111. for i, j in enumerate(reversed(q)):
  2112. if mpm:
  2113. j = sympify(j)._to_mpmath(mp.prec)
  2114. sol_q += j * x0**i
  2115. if isinstance(sol_p, (PolyElement, FracElement)):
  2116. sol_p = sol_p.as_expr()
  2117. if isinstance(sol_q, (PolyElement, FracElement)):
  2118. sol_q = sol_q.as_expr()
  2119. return sol_p / sol_q
  2120. def _convert_poly_rat_alg(func, x, x0=0, y0=None, lenics=None, domain=QQ, initcond=True):
  2121. """
  2122. Converts polynomials, rationals and algebraic functions to holonomic.
  2123. """
  2124. ispoly = func.is_polynomial()
  2125. if not ispoly:
  2126. israt = func.is_rational_function()
  2127. else:
  2128. israt = True
  2129. if not (ispoly or israt):
  2130. basepoly, ratexp = func.as_base_exp()
  2131. if basepoly.is_polynomial() and ratexp.is_Number:
  2132. if isinstance(ratexp, Float):
  2133. ratexp = nsimplify(ratexp)
  2134. m, n = ratexp.p, ratexp.q
  2135. is_alg = True
  2136. else:
  2137. is_alg = False
  2138. else:
  2139. is_alg = True
  2140. if not (ispoly or israt or is_alg):
  2141. return None
  2142. R = domain.old_poly_ring(x)
  2143. _, Dx = DifferentialOperators(R, 'Dx')
  2144. # if the function is constant
  2145. if not func.has(x):
  2146. return HolonomicFunction(Dx, x, 0, [func])
  2147. if ispoly:
  2148. # differential equation satisfied by polynomial
  2149. sol = func * Dx - func.diff(x)
  2150. sol = _normalize(sol.listofpoly, sol.parent, negative=False)
  2151. is_singular = sol.is_singular(x0)
  2152. # try to compute the conditions for singular points
  2153. if y0 is None and x0 == 0 and is_singular:
  2154. rep = R.from_sympy(func).rep
  2155. for i, j in enumerate(reversed(rep)):
  2156. if j == 0:
  2157. continue
  2158. else:
  2159. coeff = list(reversed(rep))[i:]
  2160. indicial = i
  2161. break
  2162. for i, j in enumerate(coeff):
  2163. if isinstance(j, (PolyElement, FracElement)):
  2164. coeff[i] = j.as_expr()
  2165. y0 = {indicial: S(coeff)}
  2166. elif israt:
  2167. p, q = func.as_numer_denom()
  2168. # differential equation satisfied by rational
  2169. sol = p * q * Dx + p * q.diff(x) - q * p.diff(x)
  2170. sol = _normalize(sol.listofpoly, sol.parent, negative=False)
  2171. elif is_alg:
  2172. sol = n * (x / m) * Dx - 1
  2173. sol = HolonomicFunction(sol, x).composition(basepoly).annihilator
  2174. is_singular = sol.is_singular(x0)
  2175. # try to compute the conditions for singular points
  2176. if y0 is None and x0 == 0 and is_singular and \
  2177. (lenics is None or lenics <= 1):
  2178. rep = R.from_sympy(basepoly).rep
  2179. for i, j in enumerate(reversed(rep)):
  2180. if j == 0:
  2181. continue
  2182. if isinstance(j, (PolyElement, FracElement)):
  2183. j = j.as_expr()
  2184. coeff = S(j)**ratexp
  2185. indicial = S(i) * ratexp
  2186. break
  2187. if isinstance(coeff, (PolyElement, FracElement)):
  2188. coeff = coeff.as_expr()
  2189. y0 = {indicial: S([coeff])}
  2190. if y0 or not initcond:
  2191. return HolonomicFunction(sol, x, x0, y0)
  2192. if not lenics:
  2193. lenics = sol.order
  2194. if sol.is_singular(x0):
  2195. r = HolonomicFunction(sol, x, x0)._indicial()
  2196. l = list(r)
  2197. if len(r) == 1 and r[l[0]] == S.One:
  2198. r = l[0]
  2199. g = func / (x - x0)**r
  2200. singular_ics = _find_conditions(g, x, x0, lenics)
  2201. singular_ics = [j / factorial(i) for i, j in enumerate(singular_ics)]
  2202. y0 = {r:singular_ics}
  2203. return HolonomicFunction(sol, x, x0, y0)
  2204. y0 = _find_conditions(func, x, x0, lenics)
  2205. while not y0:
  2206. x0 += 1
  2207. y0 = _find_conditions(func, x, x0, lenics)
  2208. return HolonomicFunction(sol, x, x0, y0)
  2209. def _convert_meijerint(func, x, initcond=True, domain=QQ):
  2210. args = meijerint._rewrite1(func, x)
  2211. if args:
  2212. fac, po, g, _ = args
  2213. else:
  2214. return None
  2215. # lists for sum of meijerg functions
  2216. fac_list = [fac * i[0] for i in g]
  2217. t = po.as_base_exp()
  2218. s = t[1] if t[0] == x else S.Zero
  2219. po_list = [s + i[1] for i in g]
  2220. G_list = [i[2] for i in g]
  2221. # finds meijerg representation of x**s * meijerg(a1 ... ap, b1 ... bq, z)
  2222. def _shift(func, s):
  2223. z = func.args[-1]
  2224. if z.has(I):
  2225. z = z.subs(exp_polar, exp)
  2226. d = z.collect(x, evaluate=False)
  2227. b = list(d)[0]
  2228. a = d[b]
  2229. t = b.as_base_exp()
  2230. b = t[1] if t[0] == x else S.Zero
  2231. r = s / b
  2232. an = (i + r for i in func.args[0][0])
  2233. ap = (i + r for i in func.args[0][1])
  2234. bm = (i + r for i in func.args[1][0])
  2235. bq = (i + r for i in func.args[1][1])
  2236. return a**-r, meijerg((an, ap), (bm, bq), z)
  2237. coeff, m = _shift(G_list[0], po_list[0])
  2238. sol = fac_list[0] * coeff * from_meijerg(m, initcond=initcond, domain=domain)
  2239. # add all the meijerg functions after converting to holonomic
  2240. for i in range(1, len(G_list)):
  2241. coeff, m = _shift(G_list[i], po_list[i])
  2242. sol += fac_list[i] * coeff * from_meijerg(m, initcond=initcond, domain=domain)
  2243. return sol
  2244. def _create_table(table, domain=QQ):
  2245. """
  2246. Creates the look-up table. For a similar implementation
  2247. see meijerint._create_lookup_table.
  2248. """
  2249. def add(formula, annihilator, arg, x0=0, y0=()):
  2250. """
  2251. Adds a formula in the dictionary
  2252. """
  2253. table.setdefault(_mytype(formula, x_1), []).append((formula,
  2254. HolonomicFunction(annihilator, arg, x0, y0)))
  2255. R = domain.old_poly_ring(x_1)
  2256. _, Dx = DifferentialOperators(R, 'Dx')
  2257. # add some basic functions
  2258. add(sin(x_1), Dx**2 + 1, x_1, 0, [0, 1])
  2259. add(cos(x_1), Dx**2 + 1, x_1, 0, [1, 0])
  2260. add(exp(x_1), Dx - 1, x_1, 0, 1)
  2261. add(log(x_1), Dx + x_1*Dx**2, x_1, 1, [0, 1])
  2262. add(erf(x_1), 2*x_1*Dx + Dx**2, x_1, 0, [0, 2/sqrt(pi)])
  2263. add(erfc(x_1), 2*x_1*Dx + Dx**2, x_1, 0, [1, -2/sqrt(pi)])
  2264. add(erfi(x_1), -2*x_1*Dx + Dx**2, x_1, 0, [0, 2/sqrt(pi)])
  2265. add(sinh(x_1), Dx**2 - 1, x_1, 0, [0, 1])
  2266. add(cosh(x_1), Dx**2 - 1, x_1, 0, [1, 0])
  2267. add(sinc(x_1), x_1 + 2*Dx + x_1*Dx**2, x_1)
  2268. add(Si(x_1), x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1)
  2269. add(Ci(x_1), x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1)
  2270. add(Shi(x_1), -x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1)
  2271. def _find_conditions(func, x, x0, order):
  2272. y0 = []
  2273. for i in range(order):
  2274. val = func.subs(x, x0)
  2275. if isinstance(val, NaN):
  2276. val = limit(func, x, x0)
  2277. if val.is_finite is False or isinstance(val, NaN):
  2278. return None
  2279. y0.append(val)
  2280. func = func.diff(x)
  2281. return y0