single.py 107 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979
  1. #
  2. # This is the module for ODE solver classes for single ODEs.
  3. #
  4. from __future__ import annotations
  5. from typing import ClassVar, Iterator
  6. from .riccati import match_riccati, solve_riccati
  7. from sympy.core import Add, S, Pow, Rational
  8. from sympy.core.cache import cached_property
  9. from sympy.core.exprtools import factor_terms
  10. from sympy.core.expr import Expr
  11. from sympy.core.function import AppliedUndef, Derivative, diff, Function, expand, Subs, _mexpand
  12. from sympy.core.numbers import zoo
  13. from sympy.core.relational import Equality, Eq
  14. from sympy.core.symbol import Symbol, Dummy, Wild
  15. from sympy.core.mul import Mul
  16. from sympy.functions import exp, tan, log, sqrt, besselj, bessely, cbrt, airyai, airybi
  17. from sympy.integrals import Integral
  18. from sympy.polys import Poly
  19. from sympy.polys.polytools import cancel, factor, degree
  20. from sympy.simplify import collect, simplify, separatevars, logcombine, posify # type: ignore
  21. from sympy.simplify.radsimp import fraction
  22. from sympy.utilities import numbered_symbols
  23. from sympy.solvers.solvers import solve
  24. from sympy.solvers.deutils import ode_order, _preprocess
  25. from sympy.polys.matrices.linsolve import _lin_eq2dict
  26. from sympy.polys.solvers import PolyNonlinearError
  27. from .hypergeometric import equivalence_hypergeometric, match_2nd_2F1_hypergeometric, \
  28. get_sol_2F1_hypergeometric, match_2nd_hypergeometric
  29. from .nonhomogeneous import _get_euler_characteristic_eq_sols, _get_const_characteristic_eq_sols, \
  30. _solve_undetermined_coefficients, _solve_variation_of_parameters, _test_term, _undetermined_coefficients_match, \
  31. _get_simplified_sol
  32. from .lie_group import _ode_lie_group
  33. class ODEMatchError(NotImplementedError):
  34. """Raised if a SingleODESolver is asked to solve an ODE it does not match"""
  35. pass
  36. class SingleODEProblem:
  37. """Represents an ordinary differential equation (ODE)
  38. This class is used internally in the by dsolve and related
  39. functions/classes so that properties of an ODE can be computed
  40. efficiently.
  41. Examples
  42. ========
  43. This class is used internally by dsolve. To instantiate an instance
  44. directly first define an ODE problem:
  45. >>> from sympy import Function, Symbol
  46. >>> x = Symbol('x')
  47. >>> f = Function('f')
  48. >>> eq = f(x).diff(x, 2)
  49. Now you can create a SingleODEProblem instance and query its properties:
  50. >>> from sympy.solvers.ode.single import SingleODEProblem
  51. >>> problem = SingleODEProblem(f(x).diff(x), f(x), x)
  52. >>> problem.eq
  53. Derivative(f(x), x)
  54. >>> problem.func
  55. f(x)
  56. >>> problem.sym
  57. x
  58. """
  59. # Instance attributes:
  60. eq = None # type: Expr
  61. func = None # type: AppliedUndef
  62. sym = None # type: Symbol
  63. _order = None # type: int
  64. _eq_expanded = None # type: Expr
  65. _eq_preprocessed = None # type: Expr
  66. _eq_high_order_free = None
  67. def __init__(self, eq, func, sym, prep=True, **kwargs):
  68. assert isinstance(eq, Expr)
  69. assert isinstance(func, AppliedUndef)
  70. assert isinstance(sym, Symbol)
  71. assert isinstance(prep, bool)
  72. self.eq = eq
  73. self.func = func
  74. self.sym = sym
  75. self.prep = prep
  76. self.params = kwargs
  77. @cached_property
  78. def order(self) -> int:
  79. return ode_order(self.eq, self.func)
  80. @cached_property
  81. def eq_preprocessed(self) -> Expr:
  82. return self._get_eq_preprocessed()
  83. @cached_property
  84. def eq_high_order_free(self) -> Expr:
  85. a = Wild('a', exclude=[self.func])
  86. c1 = Wild('c1', exclude=[self.sym])
  87. # Precondition to try remove f(x) from highest order derivative
  88. reduced_eq = None
  89. if self.eq.is_Add:
  90. deriv_coef = self.eq.coeff(self.func.diff(self.sym, self.order))
  91. if deriv_coef not in (1, 0):
  92. r = deriv_coef.match(a*self.func**c1)
  93. if r and r[c1]:
  94. den = self.func**r[c1]
  95. reduced_eq = Add(*[arg/den for arg in self.eq.args])
  96. if not reduced_eq:
  97. reduced_eq = expand(self.eq)
  98. return reduced_eq
  99. @cached_property
  100. def eq_expanded(self) -> Expr:
  101. return expand(self.eq_preprocessed)
  102. def _get_eq_preprocessed(self) -> Expr:
  103. if self.prep:
  104. process_eq, process_func = _preprocess(self.eq, self.func)
  105. if process_func != self.func:
  106. raise ValueError
  107. else:
  108. process_eq = self.eq
  109. return process_eq
  110. def get_numbered_constants(self, num=1, start=1, prefix='C') -> list[Symbol]:
  111. """
  112. Returns a list of constants that do not occur
  113. in eq already.
  114. """
  115. ncs = self.iter_numbered_constants(start, prefix)
  116. Cs = [next(ncs) for i in range(num)]
  117. return Cs
  118. def iter_numbered_constants(self, start=1, prefix='C') -> Iterator[Symbol]:
  119. """
  120. Returns an iterator of constants that do not occur
  121. in eq already.
  122. """
  123. atom_set = self.eq.free_symbols
  124. func_set = self.eq.atoms(Function)
  125. if func_set:
  126. atom_set |= {Symbol(str(f.func)) for f in func_set}
  127. return numbered_symbols(start=start, prefix=prefix, exclude=atom_set)
  128. @cached_property
  129. def is_autonomous(self):
  130. u = Dummy('u')
  131. x = self.sym
  132. syms = self.eq.subs(self.func, u).free_symbols
  133. return x not in syms
  134. def get_linear_coefficients(self, eq, func, order):
  135. r"""
  136. Matches a differential equation to the linear form:
  137. .. math:: a_n(x) y^{(n)} + \cdots + a_1(x)y' + a_0(x) y + B(x) = 0
  138. Returns a dict of order:coeff terms, where order is the order of the
  139. derivative on each term, and coeff is the coefficient of that derivative.
  140. The key ``-1`` holds the function `B(x)`. Returns ``None`` if the ODE is
  141. not linear. This function assumes that ``func`` has already been checked
  142. to be good.
  143. Examples
  144. ========
  145. >>> from sympy import Function, cos, sin
  146. >>> from sympy.abc import x
  147. >>> from sympy.solvers.ode.single import SingleODEProblem
  148. >>> f = Function('f')
  149. >>> eq = f(x).diff(x, 3) + 2*f(x).diff(x) + \
  150. ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - \
  151. ... sin(x)
  152. >>> obj = SingleODEProblem(eq, f(x), x)
  153. >>> obj.get_linear_coefficients(eq, f(x), 3)
  154. {-1: x - sin(x), 0: -1, 1: cos(x) + 2, 2: x, 3: 1}
  155. >>> eq = f(x).diff(x, 3) + 2*f(x).diff(x) + \
  156. ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - \
  157. ... sin(f(x))
  158. >>> obj = SingleODEProblem(eq, f(x), x)
  159. >>> obj.get_linear_coefficients(eq, f(x), 3) == None
  160. True
  161. """
  162. f = func.func
  163. x = func.args[0]
  164. symset = {Derivative(f(x), x, i) for i in range(order+1)}
  165. try:
  166. rhs, lhs_terms = _lin_eq2dict(eq, symset)
  167. except PolyNonlinearError:
  168. return None
  169. if rhs.has(func) or any(c.has(func) for c in lhs_terms.values()):
  170. return None
  171. terms = {i: lhs_terms.get(f(x).diff(x, i), S.Zero) for i in range(order+1)}
  172. terms[-1] = rhs
  173. return terms
  174. # TODO: Add methods that can be used by many ODE solvers:
  175. # order
  176. # is_linear()
  177. # get_linear_coefficients()
  178. # eq_prepared (the ODE in prepared form)
  179. class SingleODESolver:
  180. """
  181. Base class for Single ODE solvers.
  182. Subclasses should implement the _matches and _get_general_solution
  183. methods. This class is not intended to be instantiated directly but its
  184. subclasses are as part of dsolve.
  185. Examples
  186. ========
  187. You can use a subclass of SingleODEProblem to solve a particular type of
  188. ODE. We first define a particular ODE problem:
  189. >>> from sympy import Function, Symbol
  190. >>> x = Symbol('x')
  191. >>> f = Function('f')
  192. >>> eq = f(x).diff(x, 2)
  193. Now we solve this problem using the NthAlgebraic solver which is a
  194. subclass of SingleODESolver:
  195. >>> from sympy.solvers.ode.single import NthAlgebraic, SingleODEProblem
  196. >>> problem = SingleODEProblem(eq, f(x), x)
  197. >>> solver = NthAlgebraic(problem)
  198. >>> solver.get_general_solution()
  199. [Eq(f(x), _C*x + _C)]
  200. The normal way to solve an ODE is to use dsolve (which would use
  201. NthAlgebraic and other solvers internally). When using dsolve a number of
  202. other things are done such as evaluating integrals, simplifying the
  203. solution and renumbering the constants:
  204. >>> from sympy import dsolve
  205. >>> dsolve(eq, hint='nth_algebraic')
  206. Eq(f(x), C1 + C2*x)
  207. """
  208. # Subclasses should store the hint name (the argument to dsolve) in this
  209. # attribute
  210. hint: ClassVar[str]
  211. # Subclasses should define this to indicate if they support an _Integral
  212. # hint.
  213. has_integral: ClassVar[bool]
  214. # The ODE to be solved
  215. ode_problem = None # type: SingleODEProblem
  216. # Cache whether or not the equation has matched the method
  217. _matched: bool | None = None
  218. # Subclasses should store in this attribute the list of order(s) of ODE
  219. # that subclass can solve or leave it to None if not specific to any order
  220. order: list | None = None
  221. def __init__(self, ode_problem):
  222. self.ode_problem = ode_problem
  223. def matches(self) -> bool:
  224. if self.order is not None and self.ode_problem.order not in self.order:
  225. self._matched = False
  226. return self._matched
  227. if self._matched is None:
  228. self._matched = self._matches()
  229. return self._matched
  230. def get_general_solution(self, *, simplify: bool = True) -> list[Equality]:
  231. if not self.matches():
  232. msg = "%s solver cannot solve:\n%s"
  233. raise ODEMatchError(msg % (self.hint, self.ode_problem.eq))
  234. return self._get_general_solution(simplify_flag=simplify)
  235. def _matches(self) -> bool:
  236. msg = "Subclasses of SingleODESolver should implement matches."
  237. raise NotImplementedError(msg)
  238. def _get_general_solution(self, *, simplify_flag: bool = True) -> list[Equality]:
  239. msg = "Subclasses of SingleODESolver should implement get_general_solution."
  240. raise NotImplementedError(msg)
  241. class SinglePatternODESolver(SingleODESolver):
  242. '''Superclass for ODE solvers based on pattern matching'''
  243. def wilds(self):
  244. prob = self.ode_problem
  245. f = prob.func.func
  246. x = prob.sym
  247. order = prob.order
  248. return self._wilds(f, x, order)
  249. def wilds_match(self):
  250. match = self._wilds_match
  251. return [match.get(w, S.Zero) for w in self.wilds()]
  252. def _matches(self):
  253. eq = self.ode_problem.eq_expanded
  254. f = self.ode_problem.func.func
  255. x = self.ode_problem.sym
  256. order = self.ode_problem.order
  257. df = f(x).diff(x, order)
  258. if order not in [1, 2]:
  259. return False
  260. pattern = self._equation(f(x), x, order)
  261. if not pattern.coeff(df).has(Wild):
  262. eq = expand(eq / eq.coeff(df))
  263. eq = eq.collect([f(x).diff(x), f(x)], func = cancel)
  264. self._wilds_match = match = eq.match(pattern)
  265. if match is not None:
  266. return self._verify(f(x))
  267. return False
  268. def _verify(self, fx) -> bool:
  269. return True
  270. def _wilds(self, f, x, order):
  271. msg = "Subclasses of SingleODESolver should implement _wilds"
  272. raise NotImplementedError(msg)
  273. def _equation(self, fx, x, order):
  274. msg = "Subclasses of SingleODESolver should implement _equation"
  275. raise NotImplementedError(msg)
  276. class NthAlgebraic(SingleODESolver):
  277. r"""
  278. Solves an `n`\th order ordinary differential equation using algebra and
  279. integrals.
  280. There is no general form for the kind of equation that this can solve. The
  281. the equation is solved algebraically treating differentiation as an
  282. invertible algebraic function.
  283. Examples
  284. ========
  285. >>> from sympy import Function, dsolve, Eq
  286. >>> from sympy.abc import x
  287. >>> f = Function('f')
  288. >>> eq = Eq(f(x) * (f(x).diff(x)**2 - 1), 0)
  289. >>> dsolve(eq, f(x), hint='nth_algebraic')
  290. [Eq(f(x), 0), Eq(f(x), C1 - x), Eq(f(x), C1 + x)]
  291. Note that this solver can return algebraic solutions that do not have any
  292. integration constants (f(x) = 0 in the above example).
  293. """
  294. hint = 'nth_algebraic'
  295. has_integral = True # nth_algebraic_Integral hint
  296. def _matches(self):
  297. r"""
  298. Matches any differential equation that nth_algebraic can solve. Uses
  299. `sympy.solve` but teaches it how to integrate derivatives.
  300. This involves calling `sympy.solve` and does most of the work of finding a
  301. solution (apart from evaluating the integrals).
  302. """
  303. eq = self.ode_problem.eq
  304. func = self.ode_problem.func
  305. var = self.ode_problem.sym
  306. # Derivative that solve can handle:
  307. diffx = self._get_diffx(var)
  308. # Replace derivatives wrt the independent variable with diffx
  309. def replace(eq, var):
  310. def expand_diffx(*args):
  311. differand, diffs = args[0], args[1:]
  312. toreplace = differand
  313. for v, n in diffs:
  314. for _ in range(n):
  315. if v == var:
  316. toreplace = diffx(toreplace)
  317. else:
  318. toreplace = Derivative(toreplace, v)
  319. return toreplace
  320. return eq.replace(Derivative, expand_diffx)
  321. # Restore derivatives in solution afterwards
  322. def unreplace(eq, var):
  323. return eq.replace(diffx, lambda e: Derivative(e, var))
  324. subs_eqn = replace(eq, var)
  325. try:
  326. # turn off simplification to protect Integrals that have
  327. # _t instead of fx in them and would otherwise factor
  328. # as t_*Integral(1, x)
  329. solns = solve(subs_eqn, func, simplify=False)
  330. except NotImplementedError:
  331. solns = []
  332. solns = [simplify(unreplace(soln, var)) for soln in solns]
  333. solns = [Equality(func, soln) for soln in solns]
  334. self.solutions = solns
  335. return len(solns) != 0
  336. def _get_general_solution(self, *, simplify_flag: bool = True):
  337. return self.solutions
  338. # This needs to produce an invertible function but the inverse depends
  339. # which variable we are integrating with respect to. Since the class can
  340. # be stored in cached results we need to ensure that we always get the
  341. # same class back for each particular integration variable so we store these
  342. # classes in a global dict:
  343. _diffx_stored: dict[Symbol, type[Function]] = {}
  344. @staticmethod
  345. def _get_diffx(var):
  346. diffcls = NthAlgebraic._diffx_stored.get(var, None)
  347. if diffcls is None:
  348. # A class that behaves like Derivative wrt var but is "invertible".
  349. class diffx(Function):
  350. def inverse(self):
  351. # don't use integrate here because fx has been replaced by _t
  352. # in the equation; integrals will not be correct while solve
  353. # is at work.
  354. return lambda expr: Integral(expr, var) + Dummy('C')
  355. diffcls = NthAlgebraic._diffx_stored.setdefault(var, diffx)
  356. return diffcls
  357. class FirstExact(SinglePatternODESolver):
  358. r"""
  359. Solves 1st order exact ordinary differential equations.
  360. A 1st order differential equation is called exact if it is the total
  361. differential of a function. That is, the differential equation
  362. .. math:: P(x, y) \,\partial{}x + Q(x, y) \,\partial{}y = 0
  363. is exact if there is some function `F(x, y)` such that `P(x, y) =
  364. \partial{}F/\partial{}x` and `Q(x, y) = \partial{}F/\partial{}y`. It can
  365. be shown that a necessary and sufficient condition for a first order ODE
  366. to be exact is that `\partial{}P/\partial{}y = \partial{}Q/\partial{}x`.
  367. Then, the solution will be as given below::
  368. >>> from sympy import Function, Eq, Integral, symbols, pprint
  369. >>> x, y, t, x0, y0, C1= symbols('x,y,t,x0,y0,C1')
  370. >>> P, Q, F= map(Function, ['P', 'Q', 'F'])
  371. >>> pprint(Eq(Eq(F(x, y), Integral(P(t, y), (t, x0, x)) +
  372. ... Integral(Q(x0, t), (t, y0, y))), C1))
  373. x y
  374. / /
  375. | |
  376. F(x, y) = | P(t, y) dt + | Q(x0, t) dt = C1
  377. | |
  378. / /
  379. x0 y0
  380. Where the first partials of `P` and `Q` exist and are continuous in a
  381. simply connected region.
  382. A note: SymPy currently has no way to represent inert substitution on an
  383. expression, so the hint ``1st_exact_Integral`` will return an integral
  384. with `dy`. This is supposed to represent the function that you are
  385. solving for.
  386. Examples
  387. ========
  388. >>> from sympy import Function, dsolve, cos, sin
  389. >>> from sympy.abc import x
  390. >>> f = Function('f')
  391. >>> dsolve(cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x),
  392. ... f(x), hint='1st_exact')
  393. Eq(x*cos(f(x)) + f(x)**3/3, C1)
  394. References
  395. ==========
  396. - https://en.wikipedia.org/wiki/Exact_differential_equation
  397. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  398. Dover 1963, pp. 73
  399. # indirect doctest
  400. """
  401. hint = "1st_exact"
  402. has_integral = True
  403. order = [1]
  404. def _wilds(self, f, x, order):
  405. P = Wild('P', exclude=[f(x).diff(x)])
  406. Q = Wild('Q', exclude=[f(x).diff(x)])
  407. return P, Q
  408. def _equation(self, fx, x, order):
  409. P, Q = self.wilds()
  410. return P + Q*fx.diff(x)
  411. def _verify(self, fx) -> bool:
  412. P, Q = self.wilds()
  413. x = self.ode_problem.sym
  414. y = Dummy('y')
  415. m, n = self.wilds_match()
  416. m = m.subs(fx, y)
  417. n = n.subs(fx, y)
  418. numerator = cancel(m.diff(y) - n.diff(x))
  419. if numerator.is_zero:
  420. # Is exact
  421. return True
  422. else:
  423. # The following few conditions try to convert a non-exact
  424. # differential equation into an exact one.
  425. # References:
  426. # 1. Differential equations with applications
  427. # and historical notes - George E. Simmons
  428. # 2. https://math.okstate.edu/people/binegar/2233-S99/2233-l12.pdf
  429. factor_n = cancel(numerator/n)
  430. factor_m = cancel(-numerator/m)
  431. if y not in factor_n.free_symbols:
  432. # If (dP/dy - dQ/dx) / Q = f(x)
  433. # then exp(integral(f(x))*equation becomes exact
  434. factor = factor_n
  435. integration_variable = x
  436. elif x not in factor_m.free_symbols:
  437. # If (dP/dy - dQ/dx) / -P = f(y)
  438. # then exp(integral(f(y))*equation becomes exact
  439. factor = factor_m
  440. integration_variable = y
  441. else:
  442. # Couldn't convert to exact
  443. return False
  444. factor = exp(Integral(factor, integration_variable))
  445. m *= factor
  446. n *= factor
  447. self._wilds_match[P] = m.subs(y, fx)
  448. self._wilds_match[Q] = n.subs(y, fx)
  449. return True
  450. def _get_general_solution(self, *, simplify_flag: bool = True):
  451. m, n = self.wilds_match()
  452. fx = self.ode_problem.func
  453. x = self.ode_problem.sym
  454. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  455. y = Dummy('y')
  456. m = m.subs(fx, y)
  457. n = n.subs(fx, y)
  458. gen_sol = Eq(Subs(Integral(m, x)
  459. + Integral(n - Integral(m, x).diff(y), y), y, fx), C1)
  460. return [gen_sol]
  461. class FirstLinear(SinglePatternODESolver):
  462. r"""
  463. Solves 1st order linear differential equations.
  464. These are differential equations of the form
  465. .. math:: dy/dx + P(x) y = Q(x)\text{.}
  466. These kinds of differential equations can be solved in a general way. The
  467. integrating factor `e^{\int P(x) \,dx}` will turn the equation into a
  468. separable equation. The general solution is::
  469. >>> from sympy import Function, dsolve, Eq, pprint, diff, sin
  470. >>> from sympy.abc import x
  471. >>> f, P, Q = map(Function, ['f', 'P', 'Q'])
  472. >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x))
  473. >>> pprint(genform)
  474. d
  475. P(x)*f(x) + --(f(x)) = Q(x)
  476. dx
  477. >>> pprint(dsolve(genform, f(x), hint='1st_linear_Integral'))
  478. / / \
  479. | | |
  480. | | / | /
  481. | | | | |
  482. | | | P(x) dx | - | P(x) dx
  483. | | | | |
  484. | | / | /
  485. f(x) = |C1 + | Q(x)*e dx|*e
  486. | | |
  487. \ / /
  488. Examples
  489. ========
  490. >>> f = Function('f')
  491. >>> pprint(dsolve(Eq(x*diff(f(x), x) - f(x), x**2*sin(x)),
  492. ... f(x), '1st_linear'))
  493. f(x) = x*(C1 - cos(x))
  494. References
  495. ==========
  496. - https://en.wikipedia.org/wiki/Linear_differential_equation#First-order_equation_with_variable_coefficients
  497. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  498. Dover 1963, pp. 92
  499. # indirect doctest
  500. """
  501. hint = '1st_linear'
  502. has_integral = True
  503. order = [1]
  504. def _wilds(self, f, x, order):
  505. P = Wild('P', exclude=[f(x)])
  506. Q = Wild('Q', exclude=[f(x), f(x).diff(x)])
  507. return P, Q
  508. def _equation(self, fx, x, order):
  509. P, Q = self.wilds()
  510. return fx.diff(x) + P*fx - Q
  511. def _get_general_solution(self, *, simplify_flag: bool = True):
  512. P, Q = self.wilds_match()
  513. fx = self.ode_problem.func
  514. x = self.ode_problem.sym
  515. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  516. gensol = Eq(fx, ((C1 + Integral(Q*exp(Integral(P, x)), x))
  517. * exp(-Integral(P, x))))
  518. return [gensol]
  519. class AlmostLinear(SinglePatternODESolver):
  520. r"""
  521. Solves an almost-linear differential equation.
  522. The general form of an almost linear differential equation is
  523. .. math:: a(x) g'(f(x)) f'(x) + b(x) g(f(x)) + c(x)
  524. Here `f(x)` is the function to be solved for (the dependent variable).
  525. The substitution `g(f(x)) = u(x)` leads to a linear differential equation
  526. for `u(x)` of the form `a(x) u' + b(x) u + c(x) = 0`. This can be solved
  527. for `u(x)` by the `first_linear` hint and then `f(x)` is found by solving
  528. `g(f(x)) = u(x)`.
  529. See Also
  530. ========
  531. :obj:`sympy.solvers.ode.single.FirstLinear`
  532. Examples
  533. ========
  534. >>> from sympy import dsolve, Function, pprint, sin, cos
  535. >>> from sympy.abc import x
  536. >>> f = Function('f')
  537. >>> d = f(x).diff(x)
  538. >>> eq = x*d + x*f(x) + 1
  539. >>> dsolve(eq, f(x), hint='almost_linear')
  540. Eq(f(x), (C1 - Ei(x))*exp(-x))
  541. >>> pprint(dsolve(eq, f(x), hint='almost_linear'))
  542. -x
  543. f(x) = (C1 - Ei(x))*e
  544. >>> example = cos(f(x))*f(x).diff(x) + sin(f(x)) + 1
  545. >>> pprint(example)
  546. d
  547. sin(f(x)) + cos(f(x))*--(f(x)) + 1
  548. dx
  549. >>> pprint(dsolve(example, f(x), hint='almost_linear'))
  550. / -x \ / -x \
  551. [f(x) = pi - asin\C1*e - 1/, f(x) = asin\C1*e - 1/]
  552. References
  553. ==========
  554. - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications
  555. of the ACM, Volume 14, Number 8, August 1971, pp. 558
  556. """
  557. hint = "almost_linear"
  558. has_integral = True
  559. order = [1]
  560. def _wilds(self, f, x, order):
  561. P = Wild('P', exclude=[f(x).diff(x)])
  562. Q = Wild('Q', exclude=[f(x).diff(x)])
  563. return P, Q
  564. def _equation(self, fx, x, order):
  565. P, Q = self.wilds()
  566. return P*fx.diff(x) + Q
  567. def _verify(self, fx):
  568. a, b = self.wilds_match()
  569. c, b = b.as_independent(fx) if b.is_Add else (S.Zero, b)
  570. # a, b and c are the function a(x), b(x) and c(x) respectively.
  571. # c(x) is obtained by separating out b as terms with and without fx i.e, l(y)
  572. # The following conditions checks if the given equation is an almost-linear differential equation using the fact that
  573. # a(x)*(l(y))' / l(y)' is independent of l(y)
  574. if b.diff(fx) != 0 and not simplify(b.diff(fx)/a).has(fx):
  575. self.ly = factor_terms(b).as_independent(fx, as_Add=False)[1] # Gives the term containing fx i.e., l(y)
  576. self.ax = a / self.ly.diff(fx)
  577. self.cx = -c # cx is taken as -c(x) to simplify expression in the solution integral
  578. self.bx = factor_terms(b) / self.ly
  579. return True
  580. return False
  581. def _get_general_solution(self, *, simplify_flag: bool = True):
  582. x = self.ode_problem.sym
  583. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  584. gensol = Eq(self.ly, ((C1 + Integral((self.cx/self.ax)*exp(Integral(self.bx/self.ax, x)), x))
  585. * exp(-Integral(self.bx/self.ax, x))))
  586. return [gensol]
  587. class Bernoulli(SinglePatternODESolver):
  588. r"""
  589. Solves Bernoulli differential equations.
  590. These are equations of the form
  591. .. math:: dy/dx + P(x) y = Q(x) y^n\text{, }n \ne 1`\text{.}
  592. The substitution `w = 1/y^{1-n}` will transform an equation of this form
  593. into one that is linear (see the docstring of
  594. :obj:`~sympy.solvers.ode.single.FirstLinear`). The general solution is::
  595. >>> from sympy import Function, dsolve, Eq, pprint
  596. >>> from sympy.abc import x, n
  597. >>> f, P, Q = map(Function, ['f', 'P', 'Q'])
  598. >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)**n)
  599. >>> pprint(genform)
  600. d n
  601. P(x)*f(x) + --(f(x)) = Q(x)*f (x)
  602. dx
  603. >>> pprint(dsolve(genform, f(x), hint='Bernoulli_Integral'), num_columns=110)
  604. -1
  605. -----
  606. n - 1
  607. // / / \ \
  608. || | | | |
  609. || | / | / | / |
  610. || | | | | | | |
  611. || | -(n - 1)* | P(x) dx | -(n - 1)* | P(x) dx | (n - 1)* | P(x) dx|
  612. || | | | | | | |
  613. || | / | / | / |
  614. f(x) = ||C1 - n* | Q(x)*e dx + | Q(x)*e dx|*e |
  615. || | | | |
  616. \\ / / / /
  617. Note that the equation is separable when `n = 1` (see the docstring of
  618. :obj:`~sympy.solvers.ode.single.Separable`).
  619. >>> pprint(dsolve(Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)), f(x),
  620. ... hint='separable_Integral'))
  621. f(x)
  622. /
  623. | /
  624. | 1 |
  625. | - dy = C1 + | (-P(x) + Q(x)) dx
  626. | y |
  627. | /
  628. /
  629. Examples
  630. ========
  631. >>> from sympy import Function, dsolve, Eq, pprint, log
  632. >>> from sympy.abc import x
  633. >>> f = Function('f')
  634. >>> pprint(dsolve(Eq(x*f(x).diff(x) + f(x), log(x)*f(x)**2),
  635. ... f(x), hint='Bernoulli'))
  636. 1
  637. f(x) = -----------------
  638. C1*x + log(x) + 1
  639. References
  640. ==========
  641. - https://en.wikipedia.org/wiki/Bernoulli_differential_equation
  642. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  643. Dover 1963, pp. 95
  644. # indirect doctest
  645. """
  646. hint = "Bernoulli"
  647. has_integral = True
  648. order = [1]
  649. def _wilds(self, f, x, order):
  650. P = Wild('P', exclude=[f(x)])
  651. Q = Wild('Q', exclude=[f(x)])
  652. n = Wild('n', exclude=[x, f(x), f(x).diff(x)])
  653. return P, Q, n
  654. def _equation(self, fx, x, order):
  655. P, Q, n = self.wilds()
  656. return fx.diff(x) + P*fx - Q*fx**n
  657. def _get_general_solution(self, *, simplify_flag: bool = True):
  658. P, Q, n = self.wilds_match()
  659. fx = self.ode_problem.func
  660. x = self.ode_problem.sym
  661. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  662. if n==1:
  663. gensol = Eq(log(fx), (
  664. C1 + Integral((-P + Q), x)
  665. ))
  666. else:
  667. gensol = Eq(fx**(1-n), (
  668. (C1 - (n - 1) * Integral(Q*exp(-n*Integral(P, x))
  669. * exp(Integral(P, x)), x)
  670. ) * exp(-(1 - n)*Integral(P, x)))
  671. )
  672. return [gensol]
  673. class Factorable(SingleODESolver):
  674. r"""
  675. Solves equations having a solvable factor.
  676. This function is used to solve the equation having factors. Factors may be of type algebraic or ode. It
  677. will try to solve each factor independently. Factors will be solved by calling dsolve. We will return the
  678. list of solutions.
  679. Examples
  680. ========
  681. >>> from sympy import Function, dsolve, pprint
  682. >>> from sympy.abc import x
  683. >>> f = Function('f')
  684. >>> eq = (f(x)**2-4)*(f(x).diff(x)+f(x))
  685. >>> pprint(dsolve(eq, f(x)))
  686. -x
  687. [f(x) = 2, f(x) = -2, f(x) = C1*e ]
  688. """
  689. hint = "factorable"
  690. has_integral = False
  691. def _matches(self):
  692. eq_orig = self.ode_problem.eq
  693. f = self.ode_problem.func.func
  694. x = self.ode_problem.sym
  695. df = f(x).diff(x)
  696. self.eqs = []
  697. eq = eq_orig.collect(f(x), func = cancel)
  698. eq = fraction(factor(eq))[0]
  699. factors = Mul.make_args(factor(eq))
  700. roots = [fac.as_base_exp() for fac in factors if len(fac.args)!=0]
  701. if len(roots)>1 or roots[0][1]>1:
  702. for base, expo in roots:
  703. if base.has(f(x)):
  704. self.eqs.append(base)
  705. if len(self.eqs)>0:
  706. return True
  707. roots = solve(eq, df)
  708. if len(roots)>0:
  709. self.eqs = [(df - root) for root in roots]
  710. # Avoid infinite recursion
  711. matches = self.eqs != [eq_orig]
  712. return matches
  713. for i in factors:
  714. if i.has(f(x)):
  715. self.eqs.append(i)
  716. return len(self.eqs)>0 and len(factors)>1
  717. def _get_general_solution(self, *, simplify_flag: bool = True):
  718. func = self.ode_problem.func.func
  719. x = self.ode_problem.sym
  720. eqns = self.eqs
  721. sols = []
  722. for eq in eqns:
  723. try:
  724. sol = dsolve(eq, func(x))
  725. except NotImplementedError:
  726. continue
  727. else:
  728. if isinstance(sol, list):
  729. sols.extend(sol)
  730. else:
  731. sols.append(sol)
  732. if sols == []:
  733. raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by"
  734. + " the factorable group method")
  735. return sols
  736. class RiccatiSpecial(SinglePatternODESolver):
  737. r"""
  738. The general Riccati equation has the form
  739. .. math:: dy/dx = f(x) y^2 + g(x) y + h(x)\text{.}
  740. While it does not have a general solution [1], the "special" form, `dy/dx
  741. = a y^2 - b x^c`, does have solutions in many cases [2]. This routine
  742. returns a solution for `a(dy/dx) = b y^2 + c y/x + d/x^2` that is obtained
  743. by using a suitable change of variables to reduce it to the special form
  744. and is valid when neither `a` nor `b` are zero and either `c` or `d` is
  745. zero.
  746. >>> from sympy.abc import x, a, b, c, d
  747. >>> from sympy import dsolve, checkodesol, pprint, Function
  748. >>> f = Function('f')
  749. >>> y = f(x)
  750. >>> genform = a*y.diff(x) - (b*y**2 + c*y/x + d/x**2)
  751. >>> sol = dsolve(genform, y, hint="Riccati_special_minus2")
  752. >>> pprint(sol, wrap_line=False)
  753. / / __________________ \\
  754. | __________________ | / 2 ||
  755. | / 2 | \/ 4*b*d - (a + c) *log(x)||
  756. -|a + c - \/ 4*b*d - (a + c) *tan|C1 + ----------------------------||
  757. \ \ 2*a //
  758. f(x) = ------------------------------------------------------------------------
  759. 2*b*x
  760. >>> checkodesol(genform, sol, order=1)[0]
  761. True
  762. References
  763. ==========
  764. - https://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Riccati
  765. - https://eqworld.ipmnet.ru/en/solutions/ode/ode0106.pdf -
  766. https://eqworld.ipmnet.ru/en/solutions/ode/ode0123.pdf
  767. """
  768. hint = "Riccati_special_minus2"
  769. has_integral = False
  770. order = [1]
  771. def _wilds(self, f, x, order):
  772. a = Wild('a', exclude=[x, f(x), f(x).diff(x), 0])
  773. b = Wild('b', exclude=[x, f(x), f(x).diff(x), 0])
  774. c = Wild('c', exclude=[x, f(x), f(x).diff(x)])
  775. d = Wild('d', exclude=[x, f(x), f(x).diff(x)])
  776. return a, b, c, d
  777. def _equation(self, fx, x, order):
  778. a, b, c, d = self.wilds()
  779. return a*fx.diff(x) + b*fx**2 + c*fx/x + d/x**2
  780. def _get_general_solution(self, *, simplify_flag: bool = True):
  781. a, b, c, d = self.wilds_match()
  782. fx = self.ode_problem.func
  783. x = self.ode_problem.sym
  784. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  785. mu = sqrt(4*d*b - (a - c)**2)
  786. gensol = Eq(fx, (a - c - mu*tan(mu/(2*a)*log(x) + C1))/(2*b*x))
  787. return [gensol]
  788. class RationalRiccati(SinglePatternODESolver):
  789. r"""
  790. Gives general solutions to the first order Riccati differential
  791. equations that have atleast one rational particular solution.
  792. .. math :: y' = b_0(x) + b_1(x) y + b_2(x) y^2
  793. where `b_0`, `b_1` and `b_2` are rational functions of `x`
  794. with `b_2 \ne 0` (`b_2 = 0` would make it a Bernoulli equation).
  795. Examples
  796. ========
  797. >>> from sympy import Symbol, Function, dsolve, checkodesol
  798. >>> f = Function('f')
  799. >>> x = Symbol('x')
  800. >>> eq = -x**4*f(x)**2 + x**3*f(x).diff(x) + x**2*f(x) + 20
  801. >>> sol = dsolve(eq, hint="1st_rational_riccati")
  802. >>> sol
  803. Eq(f(x), (4*C1 - 5*x**9 - 4)/(x**2*(C1 + x**9 - 1)))
  804. >>> checkodesol(eq, sol)
  805. (True, 0)
  806. References
  807. ==========
  808. - Riccati ODE: https://en.wikipedia.org/wiki/Riccati_equation
  809. - N. Thieu Vo - Rational and Algebraic Solutions of First-Order Algebraic ODEs:
  810. Algorithm 11, pp. 78 - https://www3.risc.jku.at/publications/download/risc_5387/PhDThesisThieu.pdf
  811. """
  812. has_integral = False
  813. hint = "1st_rational_riccati"
  814. order = [1]
  815. def _wilds(self, f, x, order):
  816. b0 = Wild('b0', exclude=[f(x), f(x).diff(x)])
  817. b1 = Wild('b1', exclude=[f(x), f(x).diff(x)])
  818. b2 = Wild('b2', exclude=[f(x), f(x).diff(x)])
  819. return (b0, b1, b2)
  820. def _equation(self, fx, x, order):
  821. b0, b1, b2 = self.wilds()
  822. return fx.diff(x) - b0 - b1*fx - b2*fx**2
  823. def _matches(self):
  824. eq = self.ode_problem.eq_expanded
  825. f = self.ode_problem.func.func
  826. x = self.ode_problem.sym
  827. order = self.ode_problem.order
  828. if order != 1:
  829. return False
  830. match, funcs = match_riccati(eq, f, x)
  831. if not match:
  832. return False
  833. _b0, _b1, _b2 = funcs
  834. b0, b1, b2 = self.wilds()
  835. self._wilds_match = match = {b0: _b0, b1: _b1, b2: _b2}
  836. return True
  837. def _get_general_solution(self, *, simplify_flag: bool = True):
  838. # Match the equation
  839. b0, b1, b2 = self.wilds_match()
  840. fx = self.ode_problem.func
  841. x = self.ode_problem.sym
  842. return solve_riccati(fx, x, b0, b1, b2, gensol=True)
  843. class SecondNonlinearAutonomousConserved(SinglePatternODESolver):
  844. r"""
  845. Gives solution for the autonomous second order nonlinear
  846. differential equation of the form
  847. .. math :: f''(x) = g(f(x))
  848. The solution for this differential equation can be computed
  849. by multiplying by `f'(x)` and integrating on both sides,
  850. converting it into a first order differential equation.
  851. Examples
  852. ========
  853. >>> from sympy import Function, symbols, dsolve
  854. >>> f, g = symbols('f g', cls=Function)
  855. >>> x = symbols('x')
  856. >>> eq = f(x).diff(x, 2) - g(f(x))
  857. >>> dsolve(eq, simplify=False)
  858. [Eq(Integral(1/sqrt(C1 + 2*Integral(g(_u), _u)), (_u, f(x))), C2 + x),
  859. Eq(Integral(1/sqrt(C1 + 2*Integral(g(_u), _u)), (_u, f(x))), C2 - x)]
  860. >>> from sympy import exp, log
  861. >>> eq = f(x).diff(x, 2) - exp(f(x)) + log(f(x))
  862. >>> dsolve(eq, simplify=False)
  863. [Eq(Integral(1/sqrt(-2*_u*log(_u) + 2*_u + C1 + 2*exp(_u)), (_u, f(x))), C2 + x),
  864. Eq(Integral(1/sqrt(-2*_u*log(_u) + 2*_u + C1 + 2*exp(_u)), (_u, f(x))), C2 - x)]
  865. References
  866. ==========
  867. - https://eqworld.ipmnet.ru/en/solutions/ode/ode0301.pdf
  868. """
  869. hint = "2nd_nonlinear_autonomous_conserved"
  870. has_integral = True
  871. order = [2]
  872. def _wilds(self, f, x, order):
  873. fy = Wild('fy', exclude=[0, f(x).diff(x), f(x).diff(x, 2)])
  874. return (fy, )
  875. def _equation(self, fx, x, order):
  876. fy = self.wilds()[0]
  877. return fx.diff(x, 2) + fy
  878. def _verify(self, fx):
  879. return self.ode_problem.is_autonomous
  880. def _get_general_solution(self, *, simplify_flag: bool = True):
  881. g = self.wilds_match()[0]
  882. fx = self.ode_problem.func
  883. x = self.ode_problem.sym
  884. u = Dummy('u')
  885. g = g.subs(fx, u)
  886. C1, C2 = self.ode_problem.get_numbered_constants(num=2)
  887. inside = -2*Integral(g, u) + C1
  888. lhs = Integral(1/sqrt(inside), (u, fx))
  889. return [Eq(lhs, C2 + x), Eq(lhs, C2 - x)]
  890. class Liouville(SinglePatternODESolver):
  891. r"""
  892. Solves 2nd order Liouville differential equations.
  893. The general form of a Liouville ODE is
  894. .. math:: \frac{d^2 y}{dx^2} + g(y) \left(\!
  895. \frac{dy}{dx}\!\right)^2 + h(x)
  896. \frac{dy}{dx}\text{.}
  897. The general solution is:
  898. >>> from sympy import Function, dsolve, Eq, pprint, diff
  899. >>> from sympy.abc import x
  900. >>> f, g, h = map(Function, ['f', 'g', 'h'])
  901. >>> genform = Eq(diff(f(x),x,x) + g(f(x))*diff(f(x),x)**2 +
  902. ... h(x)*diff(f(x),x), 0)
  903. >>> pprint(genform)
  904. 2 2
  905. /d \ d d
  906. g(f(x))*|--(f(x))| + h(x)*--(f(x)) + ---(f(x)) = 0
  907. \dx / dx 2
  908. dx
  909. >>> pprint(dsolve(genform, f(x), hint='Liouville_Integral'))
  910. f(x)
  911. / /
  912. | |
  913. | / | /
  914. | | | |
  915. | - | h(x) dx | | g(y) dy
  916. | | | |
  917. | / | /
  918. C1 + C2* | e dx + | e dy = 0
  919. | |
  920. / /
  921. Examples
  922. ========
  923. >>> from sympy import Function, dsolve, Eq, pprint
  924. >>> from sympy.abc import x
  925. >>> f = Function('f')
  926. >>> pprint(dsolve(diff(f(x), x, x) + diff(f(x), x)**2/f(x) +
  927. ... diff(f(x), x)/x, f(x), hint='Liouville'))
  928. ________________ ________________
  929. [f(x) = -\/ C1 + C2*log(x) , f(x) = \/ C1 + C2*log(x) ]
  930. References
  931. ==========
  932. - Goldstein and Braun, "Advanced Methods for the Solution of Differential
  933. Equations", pp. 98
  934. - https://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Liouville
  935. # indirect doctest
  936. """
  937. hint = "Liouville"
  938. has_integral = True
  939. order = [2]
  940. def _wilds(self, f, x, order):
  941. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  942. e = Wild('e', exclude=[f(x).diff(x)])
  943. k = Wild('k', exclude=[f(x).diff(x)])
  944. return d, e, k
  945. def _equation(self, fx, x, order):
  946. # Liouville ODE in the form
  947. # f(x).diff(x, 2) + g(f(x))*(f(x).diff(x))**2 + h(x)*f(x).diff(x)
  948. # See Goldstein and Braun, "Advanced Methods for the Solution of
  949. # Differential Equations", pg. 98
  950. d, e, k = self.wilds()
  951. return d*fx.diff(x, 2) + e*fx.diff(x)**2 + k*fx.diff(x)
  952. def _verify(self, fx):
  953. d, e, k = self.wilds_match()
  954. self.y = Dummy('y')
  955. x = self.ode_problem.sym
  956. self.g = simplify(e/d).subs(fx, self.y)
  957. self.h = simplify(k/d).subs(fx, self.y)
  958. if self.y in self.h.free_symbols or x in self.g.free_symbols:
  959. return False
  960. return True
  961. def _get_general_solution(self, *, simplify_flag: bool = True):
  962. d, e, k = self.wilds_match()
  963. fx = self.ode_problem.func
  964. x = self.ode_problem.sym
  965. C1, C2 = self.ode_problem.get_numbered_constants(num=2)
  966. int = Integral(exp(Integral(self.g, self.y)), (self.y, None, fx))
  967. gen_sol = Eq(int + C1*Integral(exp(-Integral(self.h, x)), x) + C2, 0)
  968. return [gen_sol]
  969. class Separable(SinglePatternODESolver):
  970. r"""
  971. Solves separable 1st order differential equations.
  972. This is any differential equation that can be written as `P(y)
  973. \tfrac{dy}{dx} = Q(x)`. The solution can then just be found by
  974. rearranging terms and integrating: `\int P(y) \,dy = \int Q(x) \,dx`.
  975. This hint uses :py:meth:`sympy.simplify.simplify.separatevars` as its back
  976. end, so if a separable equation is not caught by this solver, it is most
  977. likely the fault of that function.
  978. :py:meth:`~sympy.simplify.simplify.separatevars` is
  979. smart enough to do most expansion and factoring necessary to convert a
  980. separable equation `F(x, y)` into the proper form `P(x)\cdot{}Q(y)`. The
  981. general solution is::
  982. >>> from sympy import Function, dsolve, Eq, pprint
  983. >>> from sympy.abc import x
  984. >>> a, b, c, d, f = map(Function, ['a', 'b', 'c', 'd', 'f'])
  985. >>> genform = Eq(a(x)*b(f(x))*f(x).diff(x), c(x)*d(f(x)))
  986. >>> pprint(genform)
  987. d
  988. a(x)*b(f(x))*--(f(x)) = c(x)*d(f(x))
  989. dx
  990. >>> pprint(dsolve(genform, f(x), hint='separable_Integral'))
  991. f(x)
  992. / /
  993. | |
  994. | b(y) | c(x)
  995. | ---- dy = C1 + | ---- dx
  996. | d(y) | a(x)
  997. | |
  998. / /
  999. Examples
  1000. ========
  1001. >>> from sympy import Function, dsolve, Eq
  1002. >>> from sympy.abc import x
  1003. >>> f = Function('f')
  1004. >>> pprint(dsolve(Eq(f(x)*f(x).diff(x) + x, 3*x*f(x)**2), f(x),
  1005. ... hint='separable', simplify=False))
  1006. / 2 \ 2
  1007. log\3*f (x) - 1/ x
  1008. ---------------- = C1 + --
  1009. 6 2
  1010. References
  1011. ==========
  1012. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1013. Dover 1963, pp. 52
  1014. # indirect doctest
  1015. """
  1016. hint = "separable"
  1017. has_integral = True
  1018. order = [1]
  1019. def _wilds(self, f, x, order):
  1020. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1021. e = Wild('e', exclude=[f(x).diff(x)])
  1022. return d, e
  1023. def _equation(self, fx, x, order):
  1024. d, e = self.wilds()
  1025. return d + e*fx.diff(x)
  1026. def _verify(self, fx):
  1027. d, e = self.wilds_match()
  1028. self.y = Dummy('y')
  1029. x = self.ode_problem.sym
  1030. d = separatevars(d.subs(fx, self.y))
  1031. e = separatevars(e.subs(fx, self.y))
  1032. # m1[coeff]*m1[x]*m1[y] + m2[coeff]*m2[x]*m2[y]*y'
  1033. self.m1 = separatevars(d, dict=True, symbols=(x, self.y))
  1034. self.m2 = separatevars(e, dict=True, symbols=(x, self.y))
  1035. if self.m1 and self.m2:
  1036. return True
  1037. return False
  1038. def _get_match_object(self):
  1039. fx = self.ode_problem.func
  1040. x = self.ode_problem.sym
  1041. return self.m1, self.m2, x, fx
  1042. def _get_general_solution(self, *, simplify_flag: bool = True):
  1043. m1, m2, x, fx = self._get_match_object()
  1044. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1045. int = Integral(m2['coeff']*m2[self.y]/m1[self.y],
  1046. (self.y, None, fx))
  1047. gen_sol = Eq(int, Integral(-m1['coeff']*m1[x]/
  1048. m2[x], x) + C1)
  1049. return [gen_sol]
  1050. class SeparableReduced(Separable):
  1051. r"""
  1052. Solves a differential equation that can be reduced to the separable form.
  1053. The general form of this equation is
  1054. .. math:: y' + (y/x) H(x^n y) = 0\text{}.
  1055. This can be solved by substituting `u(y) = x^n y`. The equation then
  1056. reduces to the separable form `\frac{u'}{u (\mathrm{power} - H(u))} -
  1057. \frac{1}{x} = 0`.
  1058. The general solution is:
  1059. >>> from sympy import Function, dsolve, pprint
  1060. >>> from sympy.abc import x, n
  1061. >>> f, g = map(Function, ['f', 'g'])
  1062. >>> genform = f(x).diff(x) + (f(x)/x)*g(x**n*f(x))
  1063. >>> pprint(genform)
  1064. / n \
  1065. d f(x)*g\x *f(x)/
  1066. --(f(x)) + ---------------
  1067. dx x
  1068. >>> pprint(dsolve(genform, hint='separable_reduced'))
  1069. n
  1070. x *f(x)
  1071. /
  1072. |
  1073. | 1
  1074. | ------------ dy = C1 + log(x)
  1075. | y*(n - g(y))
  1076. |
  1077. /
  1078. See Also
  1079. ========
  1080. :obj:`sympy.solvers.ode.single.Separable`
  1081. Examples
  1082. ========
  1083. >>> from sympy import dsolve, Function, pprint
  1084. >>> from sympy.abc import x
  1085. >>> f = Function('f')
  1086. >>> d = f(x).diff(x)
  1087. >>> eq = (x - x**2*f(x))*d - f(x)
  1088. >>> dsolve(eq, hint='separable_reduced')
  1089. [Eq(f(x), (1 - sqrt(C1*x**2 + 1))/x), Eq(f(x), (sqrt(C1*x**2 + 1) + 1)/x)]
  1090. >>> pprint(dsolve(eq, hint='separable_reduced'))
  1091. ___________ ___________
  1092. / 2 / 2
  1093. 1 - \/ C1*x + 1 \/ C1*x + 1 + 1
  1094. [f(x) = ------------------, f(x) = ------------------]
  1095. x x
  1096. References
  1097. ==========
  1098. - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications
  1099. of the ACM, Volume 14, Number 8, August 1971, pp. 558
  1100. """
  1101. hint = "separable_reduced"
  1102. has_integral = True
  1103. order = [1]
  1104. def _degree(self, expr, x):
  1105. # Made this function to calculate the degree of
  1106. # x in an expression. If expr will be of form
  1107. # x**p*y, (wheare p can be variables/rationals) then it
  1108. # will return p.
  1109. for val in expr:
  1110. if val.has(x):
  1111. if isinstance(val, Pow) and val.as_base_exp()[0] == x:
  1112. return (val.as_base_exp()[1])
  1113. elif val == x:
  1114. return (val.as_base_exp()[1])
  1115. else:
  1116. return self._degree(val.args, x)
  1117. return 0
  1118. def _powers(self, expr):
  1119. # this function will return all the different relative power of x w.r.t f(x).
  1120. # expr = x**p * f(x)**q then it will return {p/q}.
  1121. pows = set()
  1122. fx = self.ode_problem.func
  1123. x = self.ode_problem.sym
  1124. self.y = Dummy('y')
  1125. if isinstance(expr, Add):
  1126. exprs = expr.atoms(Add)
  1127. elif isinstance(expr, Mul):
  1128. exprs = expr.atoms(Mul)
  1129. elif isinstance(expr, Pow):
  1130. exprs = expr.atoms(Pow)
  1131. else:
  1132. exprs = {expr}
  1133. for arg in exprs:
  1134. if arg.has(x):
  1135. _, u = arg.as_independent(x, fx)
  1136. pow = self._degree((u.subs(fx, self.y), ), x)/self._degree((u.subs(fx, self.y), ), self.y)
  1137. pows.add(pow)
  1138. return pows
  1139. def _verify(self, fx):
  1140. num, den = self.wilds_match()
  1141. x = self.ode_problem.sym
  1142. factor = simplify(x/fx*num/den)
  1143. # Try representing factor in terms of x^n*y
  1144. # where n is lowest power of x in factor;
  1145. # first remove terms like sqrt(2)*3 from factor.atoms(Mul)
  1146. num, dem = factor.as_numer_denom()
  1147. num = expand(num)
  1148. dem = expand(dem)
  1149. pows = self._powers(num)
  1150. pows.update(self._powers(dem))
  1151. pows = list(pows)
  1152. if(len(pows)==1) and pows[0]!=zoo:
  1153. self.t = Dummy('t')
  1154. self.r2 = {'t': self.t}
  1155. num = num.subs(x**pows[0]*fx, self.t)
  1156. dem = dem.subs(x**pows[0]*fx, self.t)
  1157. test = num/dem
  1158. free = test.free_symbols
  1159. if len(free) == 1 and free.pop() == self.t:
  1160. self.r2.update({'power' : pows[0], 'u' : test})
  1161. return True
  1162. return False
  1163. return False
  1164. def _get_match_object(self):
  1165. fx = self.ode_problem.func
  1166. x = self.ode_problem.sym
  1167. u = self.r2['u'].subs(self.r2['t'], self.y)
  1168. ycoeff = 1/(self.y*(self.r2['power'] - u))
  1169. m1 = {self.y: 1, x: -1/x, 'coeff': 1}
  1170. m2 = {self.y: ycoeff, x: 1, 'coeff': 1}
  1171. return m1, m2, x, x**self.r2['power']*fx
  1172. class HomogeneousCoeffSubsDepDivIndep(SinglePatternODESolver):
  1173. r"""
  1174. Solves a 1st order differential equation with homogeneous coefficients
  1175. using the substitution `u_1 = \frac{\text{<dependent
  1176. variable>}}{\text{<independent variable>}}`.
  1177. This is a differential equation
  1178. .. math:: P(x, y) + Q(x, y) dy/dx = 0
  1179. such that `P` and `Q` are homogeneous and of the same order. A function
  1180. `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = t^n F(x, y)`.
  1181. Equivalently, `F(x, y)` can be rewritten as `G(y/x)` or `H(x/y)`. See
  1182. also the docstring of :py:meth:`~sympy.solvers.ode.homogeneous_order`.
  1183. If the coefficients `P` and `Q` in the differential equation above are
  1184. homogeneous functions of the same order, then it can be shown that the
  1185. substitution `y = u_1 x` (i.e. `u_1 = y/x`) will turn the differential
  1186. equation into an equation separable in the variables `x` and `u`. If
  1187. `h(u_1)` is the function that results from making the substitution `u_1 =
  1188. f(x)/x` on `P(x, f(x))` and `g(u_2)` is the function that results from the
  1189. substitution on `Q(x, f(x))` in the differential equation `P(x, f(x)) +
  1190. Q(x, f(x)) f'(x) = 0`, then the general solution is::
  1191. >>> from sympy import Function, dsolve, pprint
  1192. >>> from sympy.abc import x
  1193. >>> f, g, h = map(Function, ['f', 'g', 'h'])
  1194. >>> genform = g(f(x)/x) + h(f(x)/x)*f(x).diff(x)
  1195. >>> pprint(genform)
  1196. /f(x)\ /f(x)\ d
  1197. g|----| + h|----|*--(f(x))
  1198. \ x / \ x / dx
  1199. >>> pprint(dsolve(genform, f(x),
  1200. ... hint='1st_homogeneous_coeff_subs_dep_div_indep_Integral'))
  1201. f(x)
  1202. ----
  1203. x
  1204. /
  1205. |
  1206. | -h(u1)
  1207. log(x) = C1 + | ---------------- d(u1)
  1208. | u1*h(u1) + g(u1)
  1209. |
  1210. /
  1211. Where `u_1 h(u_1) + g(u_1) \ne 0` and `x \ne 0`.
  1212. See also the docstrings of
  1213. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffBest` and
  1214. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`.
  1215. Examples
  1216. ========
  1217. >>> from sympy import Function, dsolve
  1218. >>> from sympy.abc import x
  1219. >>> f = Function('f')
  1220. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1221. ... hint='1st_homogeneous_coeff_subs_dep_div_indep', simplify=False))
  1222. / 3 \
  1223. |3*f(x) f (x)|
  1224. log|------ + -----|
  1225. | x 3 |
  1226. \ x /
  1227. log(x) = log(C1) - -------------------
  1228. 3
  1229. References
  1230. ==========
  1231. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1232. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1233. Dover 1963, pp. 59
  1234. # indirect doctest
  1235. """
  1236. hint = "1st_homogeneous_coeff_subs_dep_div_indep"
  1237. has_integral = True
  1238. order = [1]
  1239. def _wilds(self, f, x, order):
  1240. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1241. e = Wild('e', exclude=[f(x).diff(x)])
  1242. return d, e
  1243. def _equation(self, fx, x, order):
  1244. d, e = self.wilds()
  1245. return d + e*fx.diff(x)
  1246. def _verify(self, fx):
  1247. self.d, self.e = self.wilds_match()
  1248. self.y = Dummy('y')
  1249. x = self.ode_problem.sym
  1250. self.d = separatevars(self.d.subs(fx, self.y))
  1251. self.e = separatevars(self.e.subs(fx, self.y))
  1252. ordera = homogeneous_order(self.d, x, self.y)
  1253. orderb = homogeneous_order(self.e, x, self.y)
  1254. if ordera == orderb and ordera is not None:
  1255. self.u = Dummy('u')
  1256. if simplify((self.d + self.u*self.e).subs({x: 1, self.y: self.u})) != 0:
  1257. return True
  1258. return False
  1259. return False
  1260. def _get_match_object(self):
  1261. fx = self.ode_problem.func
  1262. x = self.ode_problem.sym
  1263. self.u1 = Dummy('u1')
  1264. xarg = 0
  1265. yarg = 0
  1266. return [self.d, self.e, fx, x, self.u, self.u1, self.y, xarg, yarg]
  1267. def _get_general_solution(self, *, simplify_flag: bool = True):
  1268. d, e, fx, x, u, u1, y, xarg, yarg = self._get_match_object()
  1269. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1270. int = Integral(
  1271. (-e/(d + u1*e)).subs({x: 1, y: u1}),
  1272. (u1, None, fx/x))
  1273. sol = logcombine(Eq(log(x), int + log(C1)), force=True)
  1274. gen_sol = sol.subs(fx, u).subs(((u, u - yarg), (x, x - xarg), (u, fx)))
  1275. return [gen_sol]
  1276. class HomogeneousCoeffSubsIndepDivDep(SinglePatternODESolver):
  1277. r"""
  1278. Solves a 1st order differential equation with homogeneous coefficients
  1279. using the substitution `u_2 = \frac{\text{<independent
  1280. variable>}}{\text{<dependent variable>}}`.
  1281. This is a differential equation
  1282. .. math:: P(x, y) + Q(x, y) dy/dx = 0
  1283. such that `P` and `Q` are homogeneous and of the same order. A function
  1284. `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = t^n F(x, y)`.
  1285. Equivalently, `F(x, y)` can be rewritten as `G(y/x)` or `H(x/y)`. See
  1286. also the docstring of :py:meth:`~sympy.solvers.ode.homogeneous_order`.
  1287. If the coefficients `P` and `Q` in the differential equation above are
  1288. homogeneous functions of the same order, then it can be shown that the
  1289. substitution `x = u_2 y` (i.e. `u_2 = x/y`) will turn the differential
  1290. equation into an equation separable in the variables `y` and `u_2`. If
  1291. `h(u_2)` is the function that results from making the substitution `u_2 =
  1292. x/f(x)` on `P(x, f(x))` and `g(u_2)` is the function that results from the
  1293. substitution on `Q(x, f(x))` in the differential equation `P(x, f(x)) +
  1294. Q(x, f(x)) f'(x) = 0`, then the general solution is:
  1295. >>> from sympy import Function, dsolve, pprint
  1296. >>> from sympy.abc import x
  1297. >>> f, g, h = map(Function, ['f', 'g', 'h'])
  1298. >>> genform = g(x/f(x)) + h(x/f(x))*f(x).diff(x)
  1299. >>> pprint(genform)
  1300. / x \ / x \ d
  1301. g|----| + h|----|*--(f(x))
  1302. \f(x)/ \f(x)/ dx
  1303. >>> pprint(dsolve(genform, f(x),
  1304. ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral'))
  1305. x
  1306. ----
  1307. f(x)
  1308. /
  1309. |
  1310. | -g(u1)
  1311. | ---------------- d(u1)
  1312. | u1*g(u1) + h(u1)
  1313. |
  1314. /
  1315. <BLANKLINE>
  1316. f(x) = C1*e
  1317. Where `u_1 g(u_1) + h(u_1) \ne 0` and `f(x) \ne 0`.
  1318. See also the docstrings of
  1319. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffBest` and
  1320. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`.
  1321. Examples
  1322. ========
  1323. >>> from sympy import Function, pprint, dsolve
  1324. >>> from sympy.abc import x
  1325. >>> f = Function('f')
  1326. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1327. ... hint='1st_homogeneous_coeff_subs_indep_div_dep',
  1328. ... simplify=False))
  1329. / 2 \
  1330. | 3*x |
  1331. log|----- + 1|
  1332. | 2 |
  1333. \f (x) /
  1334. log(f(x)) = log(C1) - --------------
  1335. 3
  1336. References
  1337. ==========
  1338. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1339. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1340. Dover 1963, pp. 59
  1341. # indirect doctest
  1342. """
  1343. hint = "1st_homogeneous_coeff_subs_indep_div_dep"
  1344. has_integral = True
  1345. order = [1]
  1346. def _wilds(self, f, x, order):
  1347. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1348. e = Wild('e', exclude=[f(x).diff(x)])
  1349. return d, e
  1350. def _equation(self, fx, x, order):
  1351. d, e = self.wilds()
  1352. return d + e*fx.diff(x)
  1353. def _verify(self, fx):
  1354. self.d, self.e = self.wilds_match()
  1355. self.y = Dummy('y')
  1356. x = self.ode_problem.sym
  1357. self.d = separatevars(self.d.subs(fx, self.y))
  1358. self.e = separatevars(self.e.subs(fx, self.y))
  1359. ordera = homogeneous_order(self.d, x, self.y)
  1360. orderb = homogeneous_order(self.e, x, self.y)
  1361. if ordera == orderb and ordera is not None:
  1362. self.u = Dummy('u')
  1363. if simplify((self.e + self.u*self.d).subs({x: self.u, self.y: 1})) != 0:
  1364. return True
  1365. return False
  1366. return False
  1367. def _get_match_object(self):
  1368. fx = self.ode_problem.func
  1369. x = self.ode_problem.sym
  1370. self.u1 = Dummy('u1')
  1371. xarg = 0
  1372. yarg = 0
  1373. return [self.d, self.e, fx, x, self.u, self.u1, self.y, xarg, yarg]
  1374. def _get_general_solution(self, *, simplify_flag: bool = True):
  1375. d, e, fx, x, u, u1, y, xarg, yarg = self._get_match_object()
  1376. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1377. int = Integral(simplify((-d/(e + u1*d)).subs({x: u1, y: 1})), (u1, None, x/fx)) # type: ignore
  1378. sol = logcombine(Eq(log(fx), int + log(C1)), force=True)
  1379. gen_sol = sol.subs(fx, u).subs(((u, u - yarg), (x, x - xarg), (u, fx)))
  1380. return [gen_sol]
  1381. class HomogeneousCoeffBest(HomogeneousCoeffSubsIndepDivDep, HomogeneousCoeffSubsDepDivIndep):
  1382. r"""
  1383. Returns the best solution to an ODE from the two hints
  1384. ``1st_homogeneous_coeff_subs_dep_div_indep`` and
  1385. ``1st_homogeneous_coeff_subs_indep_div_dep``.
  1386. This is as determined by :py:meth:`~sympy.solvers.ode.ode.ode_sol_simplicity`.
  1387. See the
  1388. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`
  1389. and
  1390. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`
  1391. docstrings for more information on these hints. Note that there is no
  1392. ``ode_1st_homogeneous_coeff_best_Integral`` hint.
  1393. Examples
  1394. ========
  1395. >>> from sympy import Function, dsolve, pprint
  1396. >>> from sympy.abc import x
  1397. >>> f = Function('f')
  1398. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1399. ... hint='1st_homogeneous_coeff_best', simplify=False))
  1400. / 2 \
  1401. | 3*x |
  1402. log|----- + 1|
  1403. | 2 |
  1404. \f (x) /
  1405. log(f(x)) = log(C1) - --------------
  1406. 3
  1407. References
  1408. ==========
  1409. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1410. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1411. Dover 1963, pp. 59
  1412. # indirect doctest
  1413. """
  1414. hint = "1st_homogeneous_coeff_best"
  1415. has_integral = False
  1416. order = [1]
  1417. def _verify(self, fx):
  1418. if HomogeneousCoeffSubsIndepDivDep._verify(self, fx) and HomogeneousCoeffSubsDepDivIndep._verify(self, fx):
  1419. return True
  1420. return False
  1421. def _get_general_solution(self, *, simplify_flag: bool = True):
  1422. # There are two substitutions that solve the equation, u1=y/x and u2=x/y
  1423. # # They produce different integrals, so try them both and see which
  1424. # # one is easier
  1425. sol1 = HomogeneousCoeffSubsIndepDivDep._get_general_solution(self)
  1426. sol2 = HomogeneousCoeffSubsDepDivIndep._get_general_solution(self)
  1427. fx = self.ode_problem.func
  1428. if simplify_flag:
  1429. sol1 = odesimp(self.ode_problem.eq, *sol1, fx, "1st_homogeneous_coeff_subs_indep_div_dep")
  1430. sol2 = odesimp(self.ode_problem.eq, *sol2, fx, "1st_homogeneous_coeff_subs_dep_div_indep")
  1431. return min([sol1, sol2], key=lambda x: ode_sol_simplicity(x, fx, trysolving=not simplify))
  1432. class LinearCoefficients(HomogeneousCoeffBest):
  1433. r"""
  1434. Solves a differential equation with linear coefficients.
  1435. The general form of a differential equation with linear coefficients is
  1436. .. math:: y' + F\left(\!\frac{a_1 x + b_1 y + c_1}{a_2 x + b_2 y +
  1437. c_2}\!\right) = 0\text{,}
  1438. where `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are constants and `a_1 b_2
  1439. - a_2 b_1 \ne 0`.
  1440. This can be solved by substituting:
  1441. .. math:: x = x' + \frac{b_2 c_1 - b_1 c_2}{a_2 b_1 - a_1 b_2}
  1442. y = y' + \frac{a_1 c_2 - a_2 c_1}{a_2 b_1 - a_1
  1443. b_2}\text{.}
  1444. This substitution reduces the equation to a homogeneous differential
  1445. equation.
  1446. See Also
  1447. ========
  1448. :obj:`sympy.solvers.ode.single.HomogeneousCoeffBest`
  1449. :obj:`sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`
  1450. :obj:`sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`
  1451. Examples
  1452. ========
  1453. >>> from sympy import dsolve, Function, pprint
  1454. >>> from sympy.abc import x
  1455. >>> f = Function('f')
  1456. >>> df = f(x).diff(x)
  1457. >>> eq = (x + f(x) + 1)*df + (f(x) - 6*x + 1)
  1458. >>> dsolve(eq, hint='linear_coefficients')
  1459. [Eq(f(x), -x - sqrt(C1 + 7*x**2) - 1), Eq(f(x), -x + sqrt(C1 + 7*x**2) - 1)]
  1460. >>> pprint(dsolve(eq, hint='linear_coefficients'))
  1461. ___________ ___________
  1462. / 2 / 2
  1463. [f(x) = -x - \/ C1 + 7*x - 1, f(x) = -x + \/ C1 + 7*x - 1]
  1464. References
  1465. ==========
  1466. - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications
  1467. of the ACM, Volume 14, Number 8, August 1971, pp. 558
  1468. """
  1469. hint = "linear_coefficients"
  1470. has_integral = True
  1471. order = [1]
  1472. def _wilds(self, f, x, order):
  1473. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1474. e = Wild('e', exclude=[f(x).diff(x)])
  1475. return d, e
  1476. def _equation(self, fx, x, order):
  1477. d, e = self.wilds()
  1478. return d + e*fx.diff(x)
  1479. def _verify(self, fx):
  1480. self.d, self.e = self.wilds_match()
  1481. a, b = self.wilds()
  1482. F = self.d/self.e
  1483. x = self.ode_problem.sym
  1484. params = self._linear_coeff_match(F, fx)
  1485. if params:
  1486. self.xarg, self.yarg = params
  1487. u = Dummy('u')
  1488. t = Dummy('t')
  1489. self.y = Dummy('y')
  1490. # Dummy substitution for df and f(x).
  1491. dummy_eq = self.ode_problem.eq.subs(((fx.diff(x), t), (fx, u)))
  1492. reps = ((x, x + self.xarg), (u, u + self.yarg), (t, fx.diff(x)), (u, fx))
  1493. dummy_eq = simplify(dummy_eq.subs(reps))
  1494. # get the re-cast values for e and d
  1495. r2 = collect(expand(dummy_eq), [fx.diff(x), fx]).match(a*fx.diff(x) + b)
  1496. if r2:
  1497. self.d, self.e = r2[b], r2[a]
  1498. orderd = homogeneous_order(self.d, x, fx)
  1499. ordere = homogeneous_order(self.e, x, fx)
  1500. if orderd == ordere and orderd is not None:
  1501. self.d = self.d.subs(fx, self.y)
  1502. self.e = self.e.subs(fx, self.y)
  1503. return True
  1504. return False
  1505. return False
  1506. def _linear_coeff_match(self, expr, func):
  1507. r"""
  1508. Helper function to match hint ``linear_coefficients``.
  1509. Matches the expression to the form `(a_1 x + b_1 f(x) + c_1)/(a_2 x + b_2
  1510. f(x) + c_2)` where the following conditions hold:
  1511. 1. `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are Rationals;
  1512. 2. `c_1` or `c_2` are not equal to zero;
  1513. 3. `a_2 b_1 - a_1 b_2` is not equal to zero.
  1514. Return ``xarg``, ``yarg`` where
  1515. 1. ``xarg`` = `(b_2 c_1 - b_1 c_2)/(a_2 b_1 - a_1 b_2)`
  1516. 2. ``yarg`` = `(a_1 c_2 - a_2 c_1)/(a_2 b_1 - a_1 b_2)`
  1517. Examples
  1518. ========
  1519. >>> from sympy import Function, sin
  1520. >>> from sympy.abc import x
  1521. >>> from sympy.solvers.ode.single import LinearCoefficients
  1522. >>> f = Function('f')
  1523. >>> eq = (-25*f(x) - 8*x + 62)/(4*f(x) + 11*x - 11)
  1524. >>> obj = LinearCoefficients(eq)
  1525. >>> obj._linear_coeff_match(eq, f(x))
  1526. (1/9, 22/9)
  1527. >>> eq = sin((-5*f(x) - 8*x + 6)/(4*f(x) + x - 1))
  1528. >>> obj = LinearCoefficients(eq)
  1529. >>> obj._linear_coeff_match(eq, f(x))
  1530. (19/27, 2/27)
  1531. >>> eq = sin(f(x)/x)
  1532. >>> obj = LinearCoefficients(eq)
  1533. >>> obj._linear_coeff_match(eq, f(x))
  1534. """
  1535. f = func.func
  1536. x = func.args[0]
  1537. def abc(eq):
  1538. r'''
  1539. Internal function of _linear_coeff_match
  1540. that returns Rationals a, b, c
  1541. if eq is a*x + b*f(x) + c, else None.
  1542. '''
  1543. eq = _mexpand(eq)
  1544. c = eq.as_independent(x, f(x), as_Add=True)[0]
  1545. if not c.is_Rational:
  1546. return
  1547. a = eq.coeff(x)
  1548. if not a.is_Rational:
  1549. return
  1550. b = eq.coeff(f(x))
  1551. if not b.is_Rational:
  1552. return
  1553. if eq == a*x + b*f(x) + c:
  1554. return a, b, c
  1555. def match(arg):
  1556. r'''
  1557. Internal function of _linear_coeff_match that returns Rationals a1,
  1558. b1, c1, a2, b2, c2 and a2*b1 - a1*b2 of the expression (a1*x + b1*f(x)
  1559. + c1)/(a2*x + b2*f(x) + c2) if one of c1 or c2 and a2*b1 - a1*b2 is
  1560. non-zero, else None.
  1561. '''
  1562. n, d = arg.together().as_numer_denom()
  1563. m = abc(n)
  1564. if m is not None:
  1565. a1, b1, c1 = m
  1566. m = abc(d)
  1567. if m is not None:
  1568. a2, b2, c2 = m
  1569. d = a2*b1 - a1*b2
  1570. if (c1 or c2) and d:
  1571. return a1, b1, c1, a2, b2, c2, d
  1572. m = [fi.args[0] for fi in expr.atoms(Function) if fi.func != f and
  1573. len(fi.args) == 1 and not fi.args[0].is_Function] or {expr}
  1574. m1 = match(m.pop())
  1575. if m1 and all(match(mi) == m1 for mi in m):
  1576. a1, b1, c1, a2, b2, c2, denom = m1
  1577. return (b2*c1 - b1*c2)/denom, (a1*c2 - a2*c1)/denom
  1578. def _get_match_object(self):
  1579. fx = self.ode_problem.func
  1580. x = self.ode_problem.sym
  1581. self.u1 = Dummy('u1')
  1582. u = Dummy('u')
  1583. return [self.d, self.e, fx, x, u, self.u1, self.y, self.xarg, self.yarg]
  1584. class NthOrderReducible(SingleODESolver):
  1585. r"""
  1586. Solves ODEs that only involve derivatives of the dependent variable using
  1587. a substitution of the form `f^n(x) = g(x)`.
  1588. For example any second order ODE of the form `f''(x) = h(f'(x), x)` can be
  1589. transformed into a pair of 1st order ODEs `g'(x) = h(g(x), x)` and
  1590. `f'(x) = g(x)`. Usually the 1st order ODE for `g` is easier to solve. If
  1591. that gives an explicit solution for `g` then `f` is found simply by
  1592. integration.
  1593. Examples
  1594. ========
  1595. >>> from sympy import Function, dsolve, Eq
  1596. >>> from sympy.abc import x
  1597. >>> f = Function('f')
  1598. >>> eq = Eq(x*f(x).diff(x)**2 + f(x).diff(x, 2), 0)
  1599. >>> dsolve(eq, f(x), hint='nth_order_reducible')
  1600. ... # doctest: +NORMALIZE_WHITESPACE
  1601. Eq(f(x), C1 - sqrt(-1/C2)*log(-C2*sqrt(-1/C2) + x) + sqrt(-1/C2)*log(C2*sqrt(-1/C2) + x))
  1602. """
  1603. hint = "nth_order_reducible"
  1604. has_integral = False
  1605. def _matches(self):
  1606. # Any ODE that can be solved with a substitution and
  1607. # repeated integration e.g.:
  1608. # `d^2/dx^2(y) + x*d/dx(y) = constant
  1609. #f'(x) must be finite for this to work
  1610. eq = self.ode_problem.eq_preprocessed
  1611. func = self.ode_problem.func
  1612. x = self.ode_problem.sym
  1613. r"""
  1614. Matches any differential equation that can be rewritten with a smaller
  1615. order. Only derivatives of ``func`` alone, wrt a single variable,
  1616. are considered, and only in them should ``func`` appear.
  1617. """
  1618. # ODE only handles functions of 1 variable so this affirms that state
  1619. assert len(func.args) == 1
  1620. vc = [d.variable_count[0] for d in eq.atoms(Derivative)
  1621. if d.expr == func and len(d.variable_count) == 1]
  1622. ords = [c for v, c in vc if v == x]
  1623. if len(ords) < 2:
  1624. return False
  1625. self.smallest = min(ords)
  1626. # make sure func does not appear outside of derivatives
  1627. D = Dummy()
  1628. if eq.subs(func.diff(x, self.smallest), D).has(func):
  1629. return False
  1630. return True
  1631. def _get_general_solution(self, *, simplify_flag: bool = True):
  1632. eq = self.ode_problem.eq
  1633. f = self.ode_problem.func.func
  1634. x = self.ode_problem.sym
  1635. n = self.smallest
  1636. # get a unique function name for g
  1637. names = [a.name for a in eq.atoms(AppliedUndef)]
  1638. while True:
  1639. name = Dummy().name
  1640. if name not in names:
  1641. g = Function(name)
  1642. break
  1643. w = f(x).diff(x, n)
  1644. geq = eq.subs(w, g(x))
  1645. gsol = dsolve(geq, g(x))
  1646. if not isinstance(gsol, list):
  1647. gsol = [gsol]
  1648. # Might be multiple solutions to the reduced ODE:
  1649. fsol = []
  1650. for gsoli in gsol:
  1651. fsoli = dsolve(gsoli.subs(g(x), w), f(x)) # or do integration n times
  1652. fsol.append(fsoli)
  1653. return fsol
  1654. class SecondHypergeometric(SingleODESolver):
  1655. r"""
  1656. Solves 2nd order linear differential equations.
  1657. It computes special function solutions which can be expressed using the
  1658. 2F1, 1F1 or 0F1 hypergeometric functions.
  1659. .. math:: y'' + A(x) y' + B(x) y = 0\text{,}
  1660. where `A` and `B` are rational functions.
  1661. These kinds of differential equations have solution of non-Liouvillian form.
  1662. Given linear ODE can be obtained from 2F1 given by
  1663. .. math:: (x^2 - x) y'' + ((a + b + 1) x - c) y' + b a y = 0\text{,}
  1664. where {a, b, c} are arbitrary constants.
  1665. Notes
  1666. =====
  1667. The algorithm should find any solution of the form
  1668. .. math:: y = P(x) _pF_q(..; ..;\frac{\alpha x^k + \beta}{\gamma x^k + \delta})\text{,}
  1669. where pFq is any of 2F1, 1F1 or 0F1 and `P` is an "arbitrary function".
  1670. Currently only the 2F1 case is implemented in SymPy but the other cases are
  1671. described in the paper and could be implemented in future (contributions
  1672. welcome!).
  1673. Examples
  1674. ========
  1675. >>> from sympy import Function, dsolve, pprint
  1676. >>> from sympy.abc import x
  1677. >>> f = Function('f')
  1678. >>> eq = (x*x - x)*f(x).diff(x,2) + (5*x - 1)*f(x).diff(x) + 4*f(x)
  1679. >>> pprint(dsolve(eq, f(x), '2nd_hypergeometric'))
  1680. _
  1681. / / 4 \\ |_ /-1, -1 | \
  1682. |C1 + C2*|log(x) + -----||* | | | x|
  1683. \ \ x + 1// 2 1 \ 1 | /
  1684. f(x) = --------------------------------------------
  1685. 3
  1686. (x - 1)
  1687. References
  1688. ==========
  1689. - "Non-Liouvillian solutions for second order linear ODEs" by L. Chan, E.S. Cheb-Terrab
  1690. """
  1691. hint = "2nd_hypergeometric"
  1692. has_integral = True
  1693. def _matches(self):
  1694. eq = self.ode_problem.eq_preprocessed
  1695. func = self.ode_problem.func
  1696. r = match_2nd_hypergeometric(eq, func)
  1697. self.match_object = None
  1698. if r:
  1699. A, B = r
  1700. d = equivalence_hypergeometric(A, B, func)
  1701. if d:
  1702. if d['type'] == "2F1":
  1703. self.match_object = match_2nd_2F1_hypergeometric(d['I0'], d['k'], d['sing_point'], func)
  1704. if self.match_object is not None:
  1705. self.match_object.update({'A':A, 'B':B})
  1706. # We can extend it for 1F1 and 0F1 type also.
  1707. return self.match_object is not None
  1708. def _get_general_solution(self, *, simplify_flag: bool = True):
  1709. eq = self.ode_problem.eq
  1710. func = self.ode_problem.func
  1711. if self.match_object['type'] == "2F1":
  1712. sol = get_sol_2F1_hypergeometric(eq, func, self.match_object)
  1713. if sol is None:
  1714. raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by"
  1715. + " the hypergeometric method")
  1716. return [sol]
  1717. class NthLinearConstantCoeffHomogeneous(SingleODESolver):
  1718. r"""
  1719. Solves an `n`\th order linear homogeneous differential equation with
  1720. constant coefficients.
  1721. This is an equation of the form
  1722. .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
  1723. + a_0 f(x) = 0\text{.}
  1724. These equations can be solved in a general manner, by taking the roots of
  1725. the characteristic equation `a_n m^n + a_{n-1} m^{n-1} + \cdots + a_1 m +
  1726. a_0 = 0`. The solution will then be the sum of `C_n x^i e^{r x}` terms,
  1727. for each where `C_n` is an arbitrary constant, `r` is a root of the
  1728. characteristic equation and `i` is one of each from 0 to the multiplicity
  1729. of the root - 1 (for example, a root 3 of multiplicity 2 would create the
  1730. terms `C_1 e^{3 x} + C_2 x e^{3 x}`). The exponential is usually expanded
  1731. for complex roots using Euler's equation `e^{I x} = \cos(x) + I \sin(x)`.
  1732. Complex roots always come in conjugate pairs in polynomials with real
  1733. coefficients, so the two roots will be represented (after simplifying the
  1734. constants) as `e^{a x} \left(C_1 \cos(b x) + C_2 \sin(b x)\right)`.
  1735. If SymPy cannot find exact roots to the characteristic equation, a
  1736. :py:class:`~sympy.polys.rootoftools.ComplexRootOf` instance will be return
  1737. instead.
  1738. >>> from sympy import Function, dsolve
  1739. >>> from sympy.abc import x
  1740. >>> f = Function('f')
  1741. >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x),
  1742. ... hint='nth_linear_constant_coeff_homogeneous')
  1743. ... # doctest: +NORMALIZE_WHITESPACE
  1744. Eq(f(x), C5*exp(x*CRootOf(_x**5 + 10*_x - 2, 0))
  1745. + (C1*sin(x*im(CRootOf(_x**5 + 10*_x - 2, 1)))
  1746. + C2*cos(x*im(CRootOf(_x**5 + 10*_x - 2, 1))))*exp(x*re(CRootOf(_x**5 + 10*_x - 2, 1)))
  1747. + (C3*sin(x*im(CRootOf(_x**5 + 10*_x - 2, 3)))
  1748. + C4*cos(x*im(CRootOf(_x**5 + 10*_x - 2, 3))))*exp(x*re(CRootOf(_x**5 + 10*_x - 2, 3))))
  1749. Note that because this method does not involve integration, there is no
  1750. ``nth_linear_constant_coeff_homogeneous_Integral`` hint.
  1751. Examples
  1752. ========
  1753. >>> from sympy import Function, dsolve, pprint
  1754. >>> from sympy.abc import x
  1755. >>> f = Function('f')
  1756. >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) -
  1757. ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x),
  1758. ... hint='nth_linear_constant_coeff_homogeneous'))
  1759. x -2*x
  1760. f(x) = (C1 + C2*x)*e + (C3*sin(x) + C4*cos(x))*e
  1761. References
  1762. ==========
  1763. - https://en.wikipedia.org/wiki/Linear_differential_equation section:
  1764. Nonhomogeneous_equation_with_constant_coefficients
  1765. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1766. Dover 1963, pp. 211
  1767. # indirect doctest
  1768. """
  1769. hint = "nth_linear_constant_coeff_homogeneous"
  1770. has_integral = False
  1771. def _matches(self):
  1772. eq = self.ode_problem.eq_high_order_free
  1773. func = self.ode_problem.func
  1774. order = self.ode_problem.order
  1775. x = self.ode_problem.sym
  1776. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1777. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1778. if not self.r[-1]:
  1779. return True
  1780. else:
  1781. return False
  1782. return False
  1783. def _get_general_solution(self, *, simplify_flag: bool = True):
  1784. fx = self.ode_problem.func
  1785. order = self.ode_problem.order
  1786. roots, collectterms = _get_const_characteristic_eq_sols(self.r, fx, order)
  1787. # A generator of constants
  1788. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1789. gsol = Add(*[i*j for (i, j) in zip(constants, roots)])
  1790. gsol = Eq(fx, gsol)
  1791. if simplify_flag:
  1792. gsol = _get_simplified_sol([gsol], fx, collectterms)
  1793. return [gsol]
  1794. class NthLinearConstantCoeffVariationOfParameters(SingleODESolver):
  1795. r"""
  1796. Solves an `n`\th order linear differential equation with constant
  1797. coefficients using the method of variation of parameters.
  1798. This method works on any differential equations of the form
  1799. .. math:: f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x) + a_0
  1800. f(x) = P(x)\text{.}
  1801. This method works by assuming that the particular solution takes the form
  1802. .. math:: \sum_{x=1}^{n} c_i(x) y_i(x)\text{,}
  1803. where `y_i` is the `i`\th solution to the homogeneous equation. The
  1804. solution is then solved using Wronskian's and Cramer's Rule. The
  1805. particular solution is given by
  1806. .. math:: \sum_{x=1}^n \left( \int \frac{W_i(x)}{W(x)} \,dx
  1807. \right) y_i(x) \text{,}
  1808. where `W(x)` is the Wronskian of the fundamental system (the system of `n`
  1809. linearly independent solutions to the homogeneous equation), and `W_i(x)`
  1810. is the Wronskian of the fundamental system with the `i`\th column replaced
  1811. with `[0, 0, \cdots, 0, P(x)]`.
  1812. This method is general enough to solve any `n`\th order inhomogeneous
  1813. linear differential equation with constant coefficients, but sometimes
  1814. SymPy cannot simplify the Wronskian well enough to integrate it. If this
  1815. method hangs, try using the
  1816. ``nth_linear_constant_coeff_variation_of_parameters_Integral`` hint and
  1817. simplifying the integrals manually. Also, prefer using
  1818. ``nth_linear_constant_coeff_undetermined_coefficients`` when it
  1819. applies, because it does not use integration, making it faster and more
  1820. reliable.
  1821. Warning, using simplify=False with
  1822. 'nth_linear_constant_coeff_variation_of_parameters' in
  1823. :py:meth:`~sympy.solvers.ode.dsolve` may cause it to hang, because it will
  1824. not attempt to simplify the Wronskian before integrating. It is
  1825. recommended that you only use simplify=False with
  1826. 'nth_linear_constant_coeff_variation_of_parameters_Integral' for this
  1827. method, especially if the solution to the homogeneous equation has
  1828. trigonometric functions in it.
  1829. Examples
  1830. ========
  1831. >>> from sympy import Function, dsolve, pprint, exp, log
  1832. >>> from sympy.abc import x
  1833. >>> f = Function('f')
  1834. >>> pprint(dsolve(f(x).diff(x, 3) - 3*f(x).diff(x, 2) +
  1835. ... 3*f(x).diff(x) - f(x) - exp(x)*log(x), f(x),
  1836. ... hint='nth_linear_constant_coeff_variation_of_parameters'))
  1837. / / / x*log(x) 11*x\\\ x
  1838. f(x) = |C1 + x*|C2 + x*|C3 + -------- - ----|||*e
  1839. \ \ \ 6 36 ///
  1840. References
  1841. ==========
  1842. - https://en.wikipedia.org/wiki/Variation_of_parameters
  1843. - https://planetmath.org/VariationOfParameters
  1844. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1845. Dover 1963, pp. 233
  1846. # indirect doctest
  1847. """
  1848. hint = "nth_linear_constant_coeff_variation_of_parameters"
  1849. has_integral = True
  1850. def _matches(self):
  1851. eq = self.ode_problem.eq_high_order_free
  1852. func = self.ode_problem.func
  1853. order = self.ode_problem.order
  1854. x = self.ode_problem.sym
  1855. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1856. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1857. if self.r[-1]:
  1858. return True
  1859. else:
  1860. return False
  1861. return False
  1862. def _get_general_solution(self, *, simplify_flag: bool = True):
  1863. eq = self.ode_problem.eq_high_order_free
  1864. f = self.ode_problem.func.func
  1865. x = self.ode_problem.sym
  1866. order = self.ode_problem.order
  1867. roots, collectterms = _get_const_characteristic_eq_sols(self.r, f(x), order)
  1868. # A generator of constants
  1869. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1870. homogen_sol = Add(*[i*j for (i, j) in zip(constants, roots)])
  1871. homogen_sol = Eq(f(x), homogen_sol)
  1872. homogen_sol = _solve_variation_of_parameters(eq, f(x), roots, homogen_sol, order, self.r, simplify_flag)
  1873. if simplify_flag:
  1874. homogen_sol = _get_simplified_sol([homogen_sol], f(x), collectterms)
  1875. return [homogen_sol]
  1876. class NthLinearConstantCoeffUndeterminedCoefficients(SingleODESolver):
  1877. r"""
  1878. Solves an `n`\th order linear differential equation with constant
  1879. coefficients using the method of undetermined coefficients.
  1880. This method works on differential equations of the form
  1881. .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
  1882. + a_0 f(x) = P(x)\text{,}
  1883. where `P(x)` is a function that has a finite number of linearly
  1884. independent derivatives.
  1885. Functions that fit this requirement are finite sums functions of the form
  1886. `a x^i e^{b x} \sin(c x + d)` or `a x^i e^{b x} \cos(c x + d)`, where `i`
  1887. is a non-negative integer and `a`, `b`, `c`, and `d` are constants. For
  1888. example any polynomial in `x`, functions like `x^2 e^{2 x}`, `x \sin(x)`,
  1889. and `e^x \cos(x)` can all be used. Products of `\sin`'s and `\cos`'s have
  1890. a finite number of derivatives, because they can be expanded into `\sin(a
  1891. x)` and `\cos(b x)` terms. However, SymPy currently cannot do that
  1892. expansion, so you will need to manually rewrite the expression in terms of
  1893. the above to use this method. So, for example, you will need to manually
  1894. convert `\sin^2(x)` into `(1 + \cos(2 x))/2` to properly apply the method
  1895. of undetermined coefficients on it.
  1896. This method works by creating a trial function from the expression and all
  1897. of its linear independent derivatives and substituting them into the
  1898. original ODE. The coefficients for each term will be a system of linear
  1899. equations, which are be solved for and substituted, giving the solution.
  1900. If any of the trial functions are linearly dependent on the solution to
  1901. the homogeneous equation, they are multiplied by sufficient `x` to make
  1902. them linearly independent.
  1903. Examples
  1904. ========
  1905. >>> from sympy import Function, dsolve, pprint, exp, cos
  1906. >>> from sympy.abc import x
  1907. >>> f = Function('f')
  1908. >>> pprint(dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) -
  1909. ... 4*exp(-x)*x**2 + cos(2*x), f(x),
  1910. ... hint='nth_linear_constant_coeff_undetermined_coefficients'))
  1911. / / 3\\
  1912. | | x || -x 4*sin(2*x) 3*cos(2*x)
  1913. f(x) = |C1 + x*|C2 + --||*e - ---------- + ----------
  1914. \ \ 3 // 25 25
  1915. References
  1916. ==========
  1917. - https://en.wikipedia.org/wiki/Method_of_undetermined_coefficients
  1918. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1919. Dover 1963, pp. 221
  1920. # indirect doctest
  1921. """
  1922. hint = "nth_linear_constant_coeff_undetermined_coefficients"
  1923. has_integral = False
  1924. def _matches(self):
  1925. eq = self.ode_problem.eq_high_order_free
  1926. func = self.ode_problem.func
  1927. order = self.ode_problem.order
  1928. x = self.ode_problem.sym
  1929. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1930. does_match = False
  1931. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1932. if self.r[-1]:
  1933. eq_homogeneous = Add(eq, -self.r[-1])
  1934. undetcoeff = _undetermined_coefficients_match(self.r[-1], x, func, eq_homogeneous)
  1935. if undetcoeff['test']:
  1936. self.trialset = undetcoeff['trialset']
  1937. does_match = True
  1938. return does_match
  1939. def _get_general_solution(self, *, simplify_flag: bool = True):
  1940. eq = self.ode_problem.eq
  1941. f = self.ode_problem.func.func
  1942. x = self.ode_problem.sym
  1943. order = self.ode_problem.order
  1944. roots, collectterms = _get_const_characteristic_eq_sols(self.r, f(x), order)
  1945. # A generator of constants
  1946. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1947. homogen_sol = Add(*[i*j for (i, j) in zip(constants, roots)])
  1948. homogen_sol = Eq(f(x), homogen_sol)
  1949. self.r.update({'list': roots, 'sol': homogen_sol, 'simpliy_flag': simplify_flag})
  1950. gsol = _solve_undetermined_coefficients(eq, f(x), order, self.r, self.trialset)
  1951. if simplify_flag:
  1952. gsol = _get_simplified_sol([gsol], f(x), collectterms)
  1953. return [gsol]
  1954. class NthLinearEulerEqHomogeneous(SingleODESolver):
  1955. r"""
  1956. Solves an `n`\th order linear homogeneous variable-coefficient
  1957. Cauchy-Euler equidimensional ordinary differential equation.
  1958. This is an equation with form `0 = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  1959. \cdots`.
  1960. These equations can be solved in a general manner, by substituting
  1961. solutions of the form `f(x) = x^r`, and deriving a characteristic equation
  1962. for `r`. When there are repeated roots, we include extra terms of the
  1963. form `C_{r k} \ln^k(x) x^r`, where `C_{r k}` is an arbitrary integration
  1964. constant, `r` is a root of the characteristic equation, and `k` ranges
  1965. over the multiplicity of `r`. In the cases where the roots are complex,
  1966. solutions of the form `C_1 x^a \sin(b \log(x)) + C_2 x^a \cos(b \log(x))`
  1967. are returned, based on expansions with Euler's formula. The general
  1968. solution is the sum of the terms found. If SymPy cannot find exact roots
  1969. to the characteristic equation, a
  1970. :py:obj:`~.ComplexRootOf` instance will be returned
  1971. instead.
  1972. >>> from sympy import Function, dsolve
  1973. >>> from sympy.abc import x
  1974. >>> f = Function('f')
  1975. >>> dsolve(4*x**2*f(x).diff(x, 2) + f(x), f(x),
  1976. ... hint='nth_linear_euler_eq_homogeneous')
  1977. ... # doctest: +NORMALIZE_WHITESPACE
  1978. Eq(f(x), sqrt(x)*(C1 + C2*log(x)))
  1979. Note that because this method does not involve integration, there is no
  1980. ``nth_linear_euler_eq_homogeneous_Integral`` hint.
  1981. The following is for internal use:
  1982. - ``returns = 'sol'`` returns the solution to the ODE.
  1983. - ``returns = 'list'`` returns a list of linearly independent solutions,
  1984. corresponding to the fundamental solution set, for use with non
  1985. homogeneous solution methods like variation of parameters and
  1986. undetermined coefficients. Note that, though the solutions should be
  1987. linearly independent, this function does not explicitly check that. You
  1988. can do ``assert simplify(wronskian(sollist)) != 0`` to check for linear
  1989. independence. Also, ``assert len(sollist) == order`` will need to pass.
  1990. - ``returns = 'both'``, return a dictionary ``{'sol': <solution to ODE>,
  1991. 'list': <list of linearly independent solutions>}``.
  1992. Examples
  1993. ========
  1994. >>> from sympy import Function, dsolve, pprint
  1995. >>> from sympy.abc import x
  1996. >>> f = Function('f')
  1997. >>> eq = f(x).diff(x, 2)*x**2 - 4*f(x).diff(x)*x + 6*f(x)
  1998. >>> pprint(dsolve(eq, f(x),
  1999. ... hint='nth_linear_euler_eq_homogeneous'))
  2000. 2
  2001. f(x) = x *(C1 + C2*x)
  2002. References
  2003. ==========
  2004. - https://en.wikipedia.org/wiki/Cauchy%E2%80%93Euler_equation
  2005. - C. Bender & S. Orszag, "Advanced Mathematical Methods for Scientists and
  2006. Engineers", Springer 1999, pp. 12
  2007. # indirect doctest
  2008. """
  2009. hint = "nth_linear_euler_eq_homogeneous"
  2010. has_integral = False
  2011. def _matches(self):
  2012. eq = self.ode_problem.eq_preprocessed
  2013. f = self.ode_problem.func.func
  2014. order = self.ode_problem.order
  2015. x = self.ode_problem.sym
  2016. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2017. self.r = None
  2018. does_match = False
  2019. if order and match:
  2020. coeff = match[order]
  2021. factor = x**order / coeff
  2022. self.r = {i: factor*match[i] for i in match}
  2023. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2024. self.r if i >= 0):
  2025. if not self.r[-1]:
  2026. does_match = True
  2027. return does_match
  2028. def _get_general_solution(self, *, simplify_flag: bool = True):
  2029. fx = self.ode_problem.func
  2030. eq = self.ode_problem.eq
  2031. homogen_sol = _get_euler_characteristic_eq_sols(eq, fx, self.r)[0]
  2032. return [homogen_sol]
  2033. class NthLinearEulerEqNonhomogeneousVariationOfParameters(SingleODESolver):
  2034. r"""
  2035. Solves an `n`\th order linear non homogeneous Cauchy-Euler equidimensional
  2036. ordinary differential equation using variation of parameters.
  2037. This is an equation with form `g(x) = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  2038. \cdots`.
  2039. This method works by assuming that the particular solution takes the form
  2040. .. math:: \sum_{x=1}^{n} c_i(x) y_i(x) {a_n} {x^n} \text{, }
  2041. where `y_i` is the `i`\th solution to the homogeneous equation. The
  2042. solution is then solved using Wronskian's and Cramer's Rule. The
  2043. particular solution is given by multiplying eq given below with `a_n x^{n}`
  2044. .. math:: \sum_{x=1}^n \left( \int \frac{W_i(x)}{W(x)} \, dx
  2045. \right) y_i(x) \text{, }
  2046. where `W(x)` is the Wronskian of the fundamental system (the system of `n`
  2047. linearly independent solutions to the homogeneous equation), and `W_i(x)`
  2048. is the Wronskian of the fundamental system with the `i`\th column replaced
  2049. with `[0, 0, \cdots, 0, \frac{x^{- n}}{a_n} g{\left(x \right)}]`.
  2050. This method is general enough to solve any `n`\th order inhomogeneous
  2051. linear differential equation, but sometimes SymPy cannot simplify the
  2052. Wronskian well enough to integrate it. If this method hangs, try using the
  2053. ``nth_linear_constant_coeff_variation_of_parameters_Integral`` hint and
  2054. simplifying the integrals manually. Also, prefer using
  2055. ``nth_linear_constant_coeff_undetermined_coefficients`` when it
  2056. applies, because it does not use integration, making it faster and more
  2057. reliable.
  2058. Warning, using simplify=False with
  2059. 'nth_linear_constant_coeff_variation_of_parameters' in
  2060. :py:meth:`~sympy.solvers.ode.dsolve` may cause it to hang, because it will
  2061. not attempt to simplify the Wronskian before integrating. It is
  2062. recommended that you only use simplify=False with
  2063. 'nth_linear_constant_coeff_variation_of_parameters_Integral' for this
  2064. method, especially if the solution to the homogeneous equation has
  2065. trigonometric functions in it.
  2066. Examples
  2067. ========
  2068. >>> from sympy import Function, dsolve, Derivative
  2069. >>> from sympy.abc import x
  2070. >>> f = Function('f')
  2071. >>> eq = x**2*Derivative(f(x), x, x) - 2*x*Derivative(f(x), x) + 2*f(x) - x**4
  2072. >>> dsolve(eq, f(x),
  2073. ... hint='nth_linear_euler_eq_nonhomogeneous_variation_of_parameters').expand()
  2074. Eq(f(x), C1*x + C2*x**2 + x**4/6)
  2075. """
  2076. hint = "nth_linear_euler_eq_nonhomogeneous_variation_of_parameters"
  2077. has_integral = True
  2078. def _matches(self):
  2079. eq = self.ode_problem.eq_preprocessed
  2080. f = self.ode_problem.func.func
  2081. order = self.ode_problem.order
  2082. x = self.ode_problem.sym
  2083. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2084. self.r = None
  2085. does_match = False
  2086. if order and match:
  2087. coeff = match[order]
  2088. factor = x**order / coeff
  2089. self.r = {i: factor*match[i] for i in match}
  2090. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2091. self.r if i >= 0):
  2092. if self.r[-1]:
  2093. does_match = True
  2094. return does_match
  2095. def _get_general_solution(self, *, simplify_flag: bool = True):
  2096. eq = self.ode_problem.eq
  2097. f = self.ode_problem.func.func
  2098. x = self.ode_problem.sym
  2099. order = self.ode_problem.order
  2100. homogen_sol, roots = _get_euler_characteristic_eq_sols(eq, f(x), self.r)
  2101. self.r[-1] = self.r[-1]/self.r[order]
  2102. sol = _solve_variation_of_parameters(eq, f(x), roots, homogen_sol, order, self.r, simplify_flag)
  2103. return [Eq(f(x), homogen_sol.rhs + (sol.rhs - homogen_sol.rhs)*self.r[order])]
  2104. class NthLinearEulerEqNonhomogeneousUndeterminedCoefficients(SingleODESolver):
  2105. r"""
  2106. Solves an `n`\th order linear non homogeneous Cauchy-Euler equidimensional
  2107. ordinary differential equation using undetermined coefficients.
  2108. This is an equation with form `g(x) = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  2109. \cdots`.
  2110. These equations can be solved in a general manner, by substituting
  2111. solutions of the form `x = exp(t)`, and deriving a characteristic equation
  2112. of form `g(exp(t)) = b_0 f(t) + b_1 f'(t) + b_2 f''(t) \cdots` which can
  2113. be then solved by nth_linear_constant_coeff_undetermined_coefficients if
  2114. g(exp(t)) has finite number of linearly independent derivatives.
  2115. Functions that fit this requirement are finite sums functions of the form
  2116. `a x^i e^{b x} \sin(c x + d)` or `a x^i e^{b x} \cos(c x + d)`, where `i`
  2117. is a non-negative integer and `a`, `b`, `c`, and `d` are constants. For
  2118. example any polynomial in `x`, functions like `x^2 e^{2 x}`, `x \sin(x)`,
  2119. and `e^x \cos(x)` can all be used. Products of `\sin`'s and `\cos`'s have
  2120. a finite number of derivatives, because they can be expanded into `\sin(a
  2121. x)` and `\cos(b x)` terms. However, SymPy currently cannot do that
  2122. expansion, so you will need to manually rewrite the expression in terms of
  2123. the above to use this method. So, for example, you will need to manually
  2124. convert `\sin^2(x)` into `(1 + \cos(2 x))/2` to properly apply the method
  2125. of undetermined coefficients on it.
  2126. After replacement of x by exp(t), this method works by creating a trial function
  2127. from the expression and all of its linear independent derivatives and
  2128. substituting them into the original ODE. The coefficients for each term
  2129. will be a system of linear equations, which are be solved for and
  2130. substituted, giving the solution. If any of the trial functions are linearly
  2131. dependent on the solution to the homogeneous equation, they are multiplied
  2132. by sufficient `x` to make them linearly independent.
  2133. Examples
  2134. ========
  2135. >>> from sympy import dsolve, Function, Derivative, log
  2136. >>> from sympy.abc import x
  2137. >>> f = Function('f')
  2138. >>> eq = x**2*Derivative(f(x), x, x) - 2*x*Derivative(f(x), x) + 2*f(x) - log(x)
  2139. >>> dsolve(eq, f(x),
  2140. ... hint='nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients').expand()
  2141. Eq(f(x), C1*x + C2*x**2 + log(x)/2 + 3/4)
  2142. """
  2143. hint = "nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients"
  2144. has_integral = False
  2145. def _matches(self):
  2146. eq = self.ode_problem.eq_high_order_free
  2147. f = self.ode_problem.func.func
  2148. order = self.ode_problem.order
  2149. x = self.ode_problem.sym
  2150. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2151. self.r = None
  2152. does_match = False
  2153. if order and match:
  2154. coeff = match[order]
  2155. factor = x**order / coeff
  2156. self.r = {i: factor*match[i] for i in match}
  2157. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2158. self.r if i >= 0):
  2159. if self.r[-1]:
  2160. e, re = posify(self.r[-1].subs(x, exp(x)))
  2161. undetcoeff = _undetermined_coefficients_match(e.subs(re), x)
  2162. if undetcoeff['test']:
  2163. does_match = True
  2164. return does_match
  2165. def _get_general_solution(self, *, simplify_flag: bool = True):
  2166. f = self.ode_problem.func.func
  2167. x = self.ode_problem.sym
  2168. chareq, eq, symbol = S.Zero, S.Zero, Dummy('x')
  2169. for i in self.r.keys():
  2170. if i >= 0:
  2171. chareq += (self.r[i]*diff(x**symbol, x, i)*x**-symbol).expand()
  2172. for i in range(1, degree(Poly(chareq, symbol))+1):
  2173. eq += chareq.coeff(symbol**i)*diff(f(x), x, i)
  2174. if chareq.as_coeff_add(symbol)[0]:
  2175. eq += chareq.as_coeff_add(symbol)[0]*f(x)
  2176. e, re = posify(self.r[-1].subs(x, exp(x)))
  2177. eq += e.subs(re)
  2178. self.const_undet_instance = NthLinearConstantCoeffUndeterminedCoefficients(SingleODEProblem(eq, f(x), x))
  2179. sol = self.const_undet_instance.get_general_solution(simplify = simplify_flag)[0]
  2180. sol = sol.subs(x, log(x))
  2181. sol = sol.subs(f(log(x)), f(x)).expand()
  2182. return [sol]
  2183. class SecondLinearBessel(SingleODESolver):
  2184. r"""
  2185. Gives solution of the Bessel differential equation
  2186. .. math :: x^2 \frac{d^2y}{dx^2} + x \frac{dy}{dx} y(x) + (x^2-n^2) y(x)
  2187. if `n` is integer then the solution is of the form ``Eq(f(x), C0 besselj(n,x)
  2188. + C1 bessely(n,x))`` as both the solutions are linearly independent else if
  2189. `n` is a fraction then the solution is of the form ``Eq(f(x), C0 besselj(n,x)
  2190. + C1 besselj(-n,x))`` which can also transform into ``Eq(f(x), C0 besselj(n,x)
  2191. + C1 bessely(n,x))``.
  2192. Examples
  2193. ========
  2194. >>> from sympy.abc import x
  2195. >>> from sympy import Symbol
  2196. >>> v = Symbol('v', positive=True)
  2197. >>> from sympy import dsolve, Function
  2198. >>> f = Function('f')
  2199. >>> y = f(x)
  2200. >>> genform = x**2*y.diff(x, 2) + x*y.diff(x) + (x**2 - v**2)*y
  2201. >>> dsolve(genform)
  2202. Eq(f(x), C1*besselj(v, x) + C2*bessely(v, x))
  2203. References
  2204. ==========
  2205. https://math24.net/bessel-differential-equation.html
  2206. """
  2207. hint = "2nd_linear_bessel"
  2208. has_integral = False
  2209. def _matches(self):
  2210. eq = self.ode_problem.eq_high_order_free
  2211. f = self.ode_problem.func
  2212. order = self.ode_problem.order
  2213. x = self.ode_problem.sym
  2214. df = f.diff(x)
  2215. a = Wild('a', exclude=[f,df])
  2216. b = Wild('b', exclude=[x, f,df])
  2217. a4 = Wild('a4', exclude=[x,f,df])
  2218. b4 = Wild('b4', exclude=[x,f,df])
  2219. c4 = Wild('c4', exclude=[x,f,df])
  2220. d4 = Wild('d4', exclude=[x,f,df])
  2221. a3 = Wild('a3', exclude=[f, df, f.diff(x, 2)])
  2222. b3 = Wild('b3', exclude=[f, df, f.diff(x, 2)])
  2223. c3 = Wild('c3', exclude=[f, df, f.diff(x, 2)])
  2224. deq = a3*(f.diff(x, 2)) + b3*df + c3*f
  2225. r = collect(eq,
  2226. [f.diff(x, 2), df, f]).match(deq)
  2227. if order == 2 and r:
  2228. if not all(r[key].is_polynomial() for key in r):
  2229. n, d = eq.as_numer_denom()
  2230. eq = expand(n)
  2231. r = collect(eq,
  2232. [f.diff(x, 2), df, f]).match(deq)
  2233. if r and r[a3] != 0:
  2234. # leading coeff of f(x).diff(x, 2)
  2235. coeff = factor(r[a3]).match(a4*(x-b)**b4)
  2236. if coeff:
  2237. # if coeff[b4] = 0 means constant coefficient
  2238. if coeff[b4] == 0:
  2239. return False
  2240. point = coeff[b]
  2241. else:
  2242. return False
  2243. if point:
  2244. r[a3] = simplify(r[a3].subs(x, x+point))
  2245. r[b3] = simplify(r[b3].subs(x, x+point))
  2246. r[c3] = simplify(r[c3].subs(x, x+point))
  2247. # making a3 in the form of x**2
  2248. r[a3] = cancel(r[a3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2249. r[b3] = cancel(r[b3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2250. r[c3] = cancel(r[c3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2251. # checking if b3 is of form c*(x-b)
  2252. coeff1 = factor(r[b3]).match(a4*(x))
  2253. if coeff1 is None:
  2254. return False
  2255. # c3 maybe of very complex form so I am simply checking (a - b) form
  2256. # if yes later I will match with the standerd form of bessel in a and b
  2257. # a, b are wild variable defined above.
  2258. _coeff2 = r[c3].match(a - b)
  2259. if _coeff2 is None:
  2260. return False
  2261. # matching with standerd form for c3
  2262. coeff2 = factor(_coeff2[a]).match(c4**2*(x)**(2*a4))
  2263. if coeff2 is None:
  2264. return False
  2265. if _coeff2[b] == 0:
  2266. coeff2[d4] = 0
  2267. else:
  2268. coeff2[d4] = factor(_coeff2[b]).match(d4**2)[d4]
  2269. self.rn = {'n':coeff2[d4], 'a4':coeff2[c4], 'd4':coeff2[a4]}
  2270. self.rn['c4'] = coeff1[a4]
  2271. self.rn['b4'] = point
  2272. return True
  2273. return False
  2274. def _get_general_solution(self, *, simplify_flag: bool = True):
  2275. f = self.ode_problem.func.func
  2276. x = self.ode_problem.sym
  2277. n = self.rn['n']
  2278. a4 = self.rn['a4']
  2279. c4 = self.rn['c4']
  2280. d4 = self.rn['d4']
  2281. b4 = self.rn['b4']
  2282. n = sqrt(n**2 + Rational(1, 4)*(c4 - 1)**2)
  2283. (C1, C2) = self.ode_problem.get_numbered_constants(num=2)
  2284. return [Eq(f(x), ((x**(Rational(1-c4,2)))*(C1*besselj(n/d4,a4*x**d4/d4)
  2285. + C2*bessely(n/d4,a4*x**d4/d4))).subs(x, x-b4))]
  2286. class SecondLinearAiry(SingleODESolver):
  2287. r"""
  2288. Gives solution of the Airy differential equation
  2289. .. math :: \frac{d^2y}{dx^2} + (a + b x) y(x) = 0
  2290. in terms of Airy special functions airyai and airybi.
  2291. Examples
  2292. ========
  2293. >>> from sympy import dsolve, Function
  2294. >>> from sympy.abc import x
  2295. >>> f = Function("f")
  2296. >>> eq = f(x).diff(x, 2) - x*f(x)
  2297. >>> dsolve(eq)
  2298. Eq(f(x), C1*airyai(x) + C2*airybi(x))
  2299. """
  2300. hint = "2nd_linear_airy"
  2301. has_integral = False
  2302. def _matches(self):
  2303. eq = self.ode_problem.eq_high_order_free
  2304. f = self.ode_problem.func
  2305. order = self.ode_problem.order
  2306. x = self.ode_problem.sym
  2307. df = f.diff(x)
  2308. a4 = Wild('a4', exclude=[x,f,df])
  2309. b4 = Wild('b4', exclude=[x,f,df])
  2310. match = self.ode_problem.get_linear_coefficients(eq, f, order)
  2311. does_match = False
  2312. if order == 2 and match and match[2] != 0:
  2313. if match[1].is_zero:
  2314. self.rn = cancel(match[0]/match[2]).match(a4+b4*x)
  2315. if self.rn and self.rn[b4] != 0:
  2316. self.rn = {'b':self.rn[a4],'m':self.rn[b4]}
  2317. does_match = True
  2318. return does_match
  2319. def _get_general_solution(self, *, simplify_flag: bool = True):
  2320. f = self.ode_problem.func.func
  2321. x = self.ode_problem.sym
  2322. (C1, C2) = self.ode_problem.get_numbered_constants(num=2)
  2323. b = self.rn['b']
  2324. m = self.rn['m']
  2325. if m.is_positive:
  2326. arg = - b/cbrt(m)**2 - cbrt(m)*x
  2327. elif m.is_negative:
  2328. arg = - b/cbrt(-m)**2 + cbrt(-m)*x
  2329. else:
  2330. arg = - b/cbrt(-m)**2 + cbrt(-m)*x
  2331. return [Eq(f(x), C1*airyai(arg) + C2*airybi(arg))]
  2332. class LieGroup(SingleODESolver):
  2333. r"""
  2334. This hint implements the Lie group method of solving first order differential
  2335. equations. The aim is to convert the given differential equation from the
  2336. given coordinate system into another coordinate system where it becomes
  2337. invariant under the one-parameter Lie group of translations. The converted
  2338. ODE can be easily solved by quadrature. It makes use of the
  2339. :py:meth:`sympy.solvers.ode.infinitesimals` function which returns the
  2340. infinitesimals of the transformation.
  2341. The coordinates `r` and `s` can be found by solving the following Partial
  2342. Differential Equations.
  2343. .. math :: \xi\frac{\partial r}{\partial x} + \eta\frac{\partial r}{\partial y}
  2344. = 0
  2345. .. math :: \xi\frac{\partial s}{\partial x} + \eta\frac{\partial s}{\partial y}
  2346. = 1
  2347. The differential equation becomes separable in the new coordinate system
  2348. .. math :: \frac{ds}{dr} = \frac{\frac{\partial s}{\partial x} +
  2349. h(x, y)\frac{\partial s}{\partial y}}{
  2350. \frac{\partial r}{\partial x} + h(x, y)\frac{\partial r}{\partial y}}
  2351. After finding the solution by integration, it is then converted back to the original
  2352. coordinate system by substituting `r` and `s` in terms of `x` and `y` again.
  2353. Examples
  2354. ========
  2355. >>> from sympy import Function, dsolve, exp, pprint
  2356. >>> from sympy.abc import x
  2357. >>> f = Function('f')
  2358. >>> pprint(dsolve(f(x).diff(x) + 2*x*f(x) - x*exp(-x**2), f(x),
  2359. ... hint='lie_group'))
  2360. / 2\ 2
  2361. | x | -x
  2362. f(x) = |C1 + --|*e
  2363. \ 2 /
  2364. References
  2365. ==========
  2366. - Solving differential equations by Symmetry Groups,
  2367. John Starrett, pp. 1 - pp. 14
  2368. """
  2369. hint = "lie_group"
  2370. has_integral = False
  2371. def _has_additional_params(self):
  2372. return 'xi' in self.ode_problem.params and 'eta' in self.ode_problem.params
  2373. def _matches(self):
  2374. eq = self.ode_problem.eq
  2375. f = self.ode_problem.func.func
  2376. order = self.ode_problem.order
  2377. x = self.ode_problem.sym
  2378. df = f(x).diff(x)
  2379. y = Dummy('y')
  2380. d = Wild('d', exclude=[df, f(x).diff(x, 2)])
  2381. e = Wild('e', exclude=[df])
  2382. does_match = False
  2383. if self._has_additional_params() and order == 1:
  2384. xi = self.ode_problem.params['xi']
  2385. eta = self.ode_problem.params['eta']
  2386. self.r3 = {'xi': xi, 'eta': eta}
  2387. r = collect(eq, df, exact=True).match(d + e * df)
  2388. if r:
  2389. r['d'] = d
  2390. r['e'] = e
  2391. r['y'] = y
  2392. r[d] = r[d].subs(f(x), y)
  2393. r[e] = r[e].subs(f(x), y)
  2394. self.r3.update(r)
  2395. does_match = True
  2396. return does_match
  2397. def _get_general_solution(self, *, simplify_flag: bool = True):
  2398. eq = self.ode_problem.eq
  2399. x = self.ode_problem.sym
  2400. func = self.ode_problem.func
  2401. order = self.ode_problem.order
  2402. df = func.diff(x)
  2403. try:
  2404. eqsol = solve(eq, df)
  2405. except NotImplementedError:
  2406. eqsol = []
  2407. desols = []
  2408. for s in eqsol:
  2409. sol = _ode_lie_group(s, func, order, match=self.r3)
  2410. if sol:
  2411. desols.extend(sol)
  2412. if desols == []:
  2413. raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by"
  2414. + " the lie group method")
  2415. return desols
  2416. solver_map = {
  2417. 'factorable': Factorable,
  2418. 'nth_linear_constant_coeff_homogeneous': NthLinearConstantCoeffHomogeneous,
  2419. 'nth_linear_euler_eq_homogeneous': NthLinearEulerEqHomogeneous,
  2420. 'nth_linear_constant_coeff_undetermined_coefficients': NthLinearConstantCoeffUndeterminedCoefficients,
  2421. 'nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients': NthLinearEulerEqNonhomogeneousUndeterminedCoefficients,
  2422. 'separable': Separable,
  2423. '1st_exact': FirstExact,
  2424. '1st_linear': FirstLinear,
  2425. 'Bernoulli': Bernoulli,
  2426. 'Riccati_special_minus2': RiccatiSpecial,
  2427. '1st_rational_riccati': RationalRiccati,
  2428. '1st_homogeneous_coeff_best': HomogeneousCoeffBest,
  2429. '1st_homogeneous_coeff_subs_indep_div_dep': HomogeneousCoeffSubsIndepDivDep,
  2430. '1st_homogeneous_coeff_subs_dep_div_indep': HomogeneousCoeffSubsDepDivIndep,
  2431. 'almost_linear': AlmostLinear,
  2432. 'linear_coefficients': LinearCoefficients,
  2433. 'separable_reduced': SeparableReduced,
  2434. 'nth_linear_constant_coeff_variation_of_parameters': NthLinearConstantCoeffVariationOfParameters,
  2435. 'nth_linear_euler_eq_nonhomogeneous_variation_of_parameters': NthLinearEulerEqNonhomogeneousVariationOfParameters,
  2436. 'Liouville': Liouville,
  2437. '2nd_linear_airy': SecondLinearAiry,
  2438. '2nd_linear_bessel': SecondLinearBessel,
  2439. '2nd_hypergeometric': SecondHypergeometric,
  2440. 'nth_order_reducible': NthOrderReducible,
  2441. '2nd_nonlinear_autonomous_conserved': SecondNonlinearAutonomousConserved,
  2442. 'nth_algebraic': NthAlgebraic,
  2443. 'lie_group': LieGroup,
  2444. }
  2445. # Avoid circular import:
  2446. from .ode import dsolve, ode_sol_simplicity, odesimp, homogeneous_order