single.py 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977
  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: Expr
  61. func: AppliedUndef
  62. sym: Symbol
  63. _order: int
  64. _eq_expanded: Expr
  65. _eq_preprocessed: 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 reduced_eq is None:
  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: 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. return bool(self.m1 and self.m2)
  1036. def _get_match_object(self):
  1037. fx = self.ode_problem.func
  1038. x = self.ode_problem.sym
  1039. return self.m1, self.m2, x, fx
  1040. def _get_general_solution(self, *, simplify_flag: bool = True):
  1041. m1, m2, x, fx = self._get_match_object()
  1042. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1043. int = Integral(m2['coeff']*m2[self.y]/m1[self.y],
  1044. (self.y, None, fx))
  1045. gen_sol = Eq(int, Integral(-m1['coeff']*m1[x]/
  1046. m2[x], x) + C1)
  1047. return [gen_sol]
  1048. class SeparableReduced(Separable):
  1049. r"""
  1050. Solves a differential equation that can be reduced to the separable form.
  1051. The general form of this equation is
  1052. .. math:: y' + (y/x) H(x^n y) = 0\text{}.
  1053. This can be solved by substituting `u(y) = x^n y`. The equation then
  1054. reduces to the separable form `\frac{u'}{u (\mathrm{power} - H(u))} -
  1055. \frac{1}{x} = 0`.
  1056. The general solution is:
  1057. >>> from sympy import Function, dsolve, pprint
  1058. >>> from sympy.abc import x, n
  1059. >>> f, g = map(Function, ['f', 'g'])
  1060. >>> genform = f(x).diff(x) + (f(x)/x)*g(x**n*f(x))
  1061. >>> pprint(genform)
  1062. / n \
  1063. d f(x)*g\x *f(x)/
  1064. --(f(x)) + ---------------
  1065. dx x
  1066. >>> pprint(dsolve(genform, hint='separable_reduced'))
  1067. n
  1068. x *f(x)
  1069. /
  1070. |
  1071. | 1
  1072. | ------------ dy = C1 + log(x)
  1073. | y*(n - g(y))
  1074. |
  1075. /
  1076. See Also
  1077. ========
  1078. :obj:`sympy.solvers.ode.single.Separable`
  1079. Examples
  1080. ========
  1081. >>> from sympy import dsolve, Function, pprint
  1082. >>> from sympy.abc import x
  1083. >>> f = Function('f')
  1084. >>> d = f(x).diff(x)
  1085. >>> eq = (x - x**2*f(x))*d - f(x)
  1086. >>> dsolve(eq, hint='separable_reduced')
  1087. [Eq(f(x), (1 - sqrt(C1*x**2 + 1))/x), Eq(f(x), (sqrt(C1*x**2 + 1) + 1)/x)]
  1088. >>> pprint(dsolve(eq, hint='separable_reduced'))
  1089. ___________ ___________
  1090. / 2 / 2
  1091. 1 - \/ C1*x + 1 \/ C1*x + 1 + 1
  1092. [f(x) = ------------------, f(x) = ------------------]
  1093. x x
  1094. References
  1095. ==========
  1096. - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications
  1097. of the ACM, Volume 14, Number 8, August 1971, pp. 558
  1098. """
  1099. hint = "separable_reduced"
  1100. has_integral = True
  1101. order = [1]
  1102. def _degree(self, expr, x):
  1103. # Made this function to calculate the degree of
  1104. # x in an expression. If expr will be of form
  1105. # x**p*y, (wheare p can be variables/rationals) then it
  1106. # will return p.
  1107. for val in expr:
  1108. if val.has(x):
  1109. if isinstance(val, Pow) and val.as_base_exp()[0] == x:
  1110. return (val.as_base_exp()[1])
  1111. elif val == x:
  1112. return (val.as_base_exp()[1])
  1113. else:
  1114. return self._degree(val.args, x)
  1115. return 0
  1116. def _powers(self, expr):
  1117. # this function will return all the different relative power of x w.r.t f(x).
  1118. # expr = x**p * f(x)**q then it will return {p/q}.
  1119. pows = set()
  1120. fx = self.ode_problem.func
  1121. x = self.ode_problem.sym
  1122. self.y = Dummy('y')
  1123. if isinstance(expr, Add):
  1124. exprs = expr.atoms(Add)
  1125. elif isinstance(expr, Mul):
  1126. exprs = expr.atoms(Mul)
  1127. elif isinstance(expr, Pow):
  1128. exprs = expr.atoms(Pow)
  1129. else:
  1130. exprs = {expr}
  1131. for arg in exprs:
  1132. if arg.has(x):
  1133. _, u = arg.as_independent(x, fx)
  1134. pow = self._degree((u.subs(fx, self.y), ), x)/self._degree((u.subs(fx, self.y), ), self.y)
  1135. pows.add(pow)
  1136. return pows
  1137. def _verify(self, fx):
  1138. num, den = self.wilds_match()
  1139. x = self.ode_problem.sym
  1140. factor = simplify(x/fx*num/den)
  1141. # Try representing factor in terms of x^n*y
  1142. # where n is lowest power of x in factor;
  1143. # first remove terms like sqrt(2)*3 from factor.atoms(Mul)
  1144. num, dem = factor.as_numer_denom()
  1145. num = expand(num)
  1146. dem = expand(dem)
  1147. pows = self._powers(num)
  1148. pows.update(self._powers(dem))
  1149. pows = list(pows)
  1150. if(len(pows)==1) and pows[0]!=zoo:
  1151. self.t = Dummy('t')
  1152. self.r2 = {'t': self.t}
  1153. num = num.subs(x**pows[0]*fx, self.t)
  1154. dem = dem.subs(x**pows[0]*fx, self.t)
  1155. test = num/dem
  1156. free = test.free_symbols
  1157. if len(free) == 1 and free.pop() == self.t:
  1158. self.r2.update({'power' : pows[0], 'u' : test})
  1159. return True
  1160. return False
  1161. return False
  1162. def _get_match_object(self):
  1163. fx = self.ode_problem.func
  1164. x = self.ode_problem.sym
  1165. u = self.r2['u'].subs(self.r2['t'], self.y)
  1166. ycoeff = 1/(self.y*(self.r2['power'] - u))
  1167. m1 = {self.y: 1, x: -1/x, 'coeff': 1}
  1168. m2 = {self.y: ycoeff, x: 1, 'coeff': 1}
  1169. return m1, m2, x, x**self.r2['power']*fx
  1170. class HomogeneousCoeffSubsDepDivIndep(SinglePatternODESolver):
  1171. r"""
  1172. Solves a 1st order differential equation with homogeneous coefficients
  1173. using the substitution `u_1 = \frac{\text{<dependent
  1174. variable>}}{\text{<independent variable>}}`.
  1175. This is a differential equation
  1176. .. math:: P(x, y) + Q(x, y) dy/dx = 0
  1177. such that `P` and `Q` are homogeneous and of the same order. A function
  1178. `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = t^n F(x, y)`.
  1179. Equivalently, `F(x, y)` can be rewritten as `G(y/x)` or `H(x/y)`. See
  1180. also the docstring of :py:meth:`~sympy.solvers.ode.homogeneous_order`.
  1181. If the coefficients `P` and `Q` in the differential equation above are
  1182. homogeneous functions of the same order, then it can be shown that the
  1183. substitution `y = u_1 x` (i.e. `u_1 = y/x`) will turn the differential
  1184. equation into an equation separable in the variables `x` and `u`. If
  1185. `h(u_1)` is the function that results from making the substitution `u_1 =
  1186. f(x)/x` on `P(x, f(x))` and `g(u_2)` is the function that results from the
  1187. substitution on `Q(x, f(x))` in the differential equation `P(x, f(x)) +
  1188. Q(x, f(x)) f'(x) = 0`, then the general solution is::
  1189. >>> from sympy import Function, dsolve, pprint
  1190. >>> from sympy.abc import x
  1191. >>> f, g, h = map(Function, ['f', 'g', 'h'])
  1192. >>> genform = g(f(x)/x) + h(f(x)/x)*f(x).diff(x)
  1193. >>> pprint(genform)
  1194. /f(x)\ /f(x)\ d
  1195. g|----| + h|----|*--(f(x))
  1196. \ x / \ x / dx
  1197. >>> pprint(dsolve(genform, f(x),
  1198. ... hint='1st_homogeneous_coeff_subs_dep_div_indep_Integral'))
  1199. f(x)
  1200. ----
  1201. x
  1202. /
  1203. |
  1204. | -h(u1)
  1205. log(x) = C1 + | ---------------- d(u1)
  1206. | u1*h(u1) + g(u1)
  1207. |
  1208. /
  1209. Where `u_1 h(u_1) + g(u_1) \ne 0` and `x \ne 0`.
  1210. See also the docstrings of
  1211. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffBest` and
  1212. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`.
  1213. Examples
  1214. ========
  1215. >>> from sympy import Function, dsolve
  1216. >>> from sympy.abc import x
  1217. >>> f = Function('f')
  1218. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1219. ... hint='1st_homogeneous_coeff_subs_dep_div_indep', simplify=False))
  1220. / 3 \
  1221. |3*f(x) f (x)|
  1222. log|------ + -----|
  1223. | x 3 |
  1224. \ x /
  1225. log(x) = log(C1) - -------------------
  1226. 3
  1227. References
  1228. ==========
  1229. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1230. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1231. Dover 1963, pp. 59
  1232. # indirect doctest
  1233. """
  1234. hint = "1st_homogeneous_coeff_subs_dep_div_indep"
  1235. has_integral = True
  1236. order = [1]
  1237. def _wilds(self, f, x, order):
  1238. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1239. e = Wild('e', exclude=[f(x).diff(x)])
  1240. return d, e
  1241. def _equation(self, fx, x, order):
  1242. d, e = self.wilds()
  1243. return d + e*fx.diff(x)
  1244. def _verify(self, fx):
  1245. self.d, self.e = self.wilds_match()
  1246. self.y = Dummy('y')
  1247. x = self.ode_problem.sym
  1248. self.d = separatevars(self.d.subs(fx, self.y))
  1249. self.e = separatevars(self.e.subs(fx, self.y))
  1250. ordera = homogeneous_order(self.d, x, self.y)
  1251. orderb = homogeneous_order(self.e, x, self.y)
  1252. if ordera == orderb and ordera is not None:
  1253. self.u = Dummy('u')
  1254. if simplify((self.d + self.u*self.e).subs({x: 1, self.y: self.u})) != 0:
  1255. return True
  1256. return False
  1257. return False
  1258. def _get_match_object(self):
  1259. fx = self.ode_problem.func
  1260. x = self.ode_problem.sym
  1261. self.u1 = Dummy('u1')
  1262. xarg = 0
  1263. yarg = 0
  1264. return [self.d, self.e, fx, x, self.u, self.u1, self.y, xarg, yarg]
  1265. def _get_general_solution(self, *, simplify_flag: bool = True):
  1266. d, e, fx, x, u, u1, y, xarg, yarg = self._get_match_object()
  1267. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1268. int = Integral(
  1269. (-e/(d + u1*e)).subs({x: 1, y: u1}),
  1270. (u1, None, fx/x))
  1271. sol = logcombine(Eq(log(x), int + log(C1)), force=True)
  1272. gen_sol = sol.subs(fx, u).subs(((u, u - yarg), (x, x - xarg), (u, fx)))
  1273. return [gen_sol]
  1274. class HomogeneousCoeffSubsIndepDivDep(SinglePatternODESolver):
  1275. r"""
  1276. Solves a 1st order differential equation with homogeneous coefficients
  1277. using the substitution `u_2 = \frac{\text{<independent
  1278. variable>}}{\text{<dependent variable>}}`.
  1279. This is a differential equation
  1280. .. math:: P(x, y) + Q(x, y) dy/dx = 0
  1281. such that `P` and `Q` are homogeneous and of the same order. A function
  1282. `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = t^n F(x, y)`.
  1283. Equivalently, `F(x, y)` can be rewritten as `G(y/x)` or `H(x/y)`. See
  1284. also the docstring of :py:meth:`~sympy.solvers.ode.homogeneous_order`.
  1285. If the coefficients `P` and `Q` in the differential equation above are
  1286. homogeneous functions of the same order, then it can be shown that the
  1287. substitution `x = u_2 y` (i.e. `u_2 = x/y`) will turn the differential
  1288. equation into an equation separable in the variables `y` and `u_2`. If
  1289. `h(u_2)` is the function that results from making the substitution `u_2 =
  1290. x/f(x)` on `P(x, f(x))` and `g(u_2)` is the function that results from the
  1291. substitution on `Q(x, f(x))` in the differential equation `P(x, f(x)) +
  1292. Q(x, f(x)) f'(x) = 0`, then the general solution is:
  1293. >>> from sympy import Function, dsolve, pprint
  1294. >>> from sympy.abc import x
  1295. >>> f, g, h = map(Function, ['f', 'g', 'h'])
  1296. >>> genform = g(x/f(x)) + h(x/f(x))*f(x).diff(x)
  1297. >>> pprint(genform)
  1298. / x \ / x \ d
  1299. g|----| + h|----|*--(f(x))
  1300. \f(x)/ \f(x)/ dx
  1301. >>> pprint(dsolve(genform, f(x),
  1302. ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral'))
  1303. x
  1304. ----
  1305. f(x)
  1306. /
  1307. |
  1308. | -g(u1)
  1309. | ---------------- d(u1)
  1310. | u1*g(u1) + h(u1)
  1311. |
  1312. /
  1313. <BLANKLINE>
  1314. f(x) = C1*e
  1315. Where `u_1 g(u_1) + h(u_1) \ne 0` and `f(x) \ne 0`.
  1316. See also the docstrings of
  1317. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffBest` and
  1318. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`.
  1319. Examples
  1320. ========
  1321. >>> from sympy import Function, pprint, dsolve
  1322. >>> from sympy.abc import x
  1323. >>> f = Function('f')
  1324. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1325. ... hint='1st_homogeneous_coeff_subs_indep_div_dep',
  1326. ... simplify=False))
  1327. / 2 \
  1328. |3*x |
  1329. log|----- + 1|
  1330. | 2 |
  1331. \f (x) /
  1332. log(f(x)) = log(C1) - --------------
  1333. 3
  1334. References
  1335. ==========
  1336. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1337. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1338. Dover 1963, pp. 59
  1339. # indirect doctest
  1340. """
  1341. hint = "1st_homogeneous_coeff_subs_indep_div_dep"
  1342. has_integral = True
  1343. order = [1]
  1344. def _wilds(self, f, x, order):
  1345. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1346. e = Wild('e', exclude=[f(x).diff(x)])
  1347. return d, e
  1348. def _equation(self, fx, x, order):
  1349. d, e = self.wilds()
  1350. return d + e*fx.diff(x)
  1351. def _verify(self, fx):
  1352. self.d, self.e = self.wilds_match()
  1353. self.y = Dummy('y')
  1354. x = self.ode_problem.sym
  1355. self.d = separatevars(self.d.subs(fx, self.y))
  1356. self.e = separatevars(self.e.subs(fx, self.y))
  1357. ordera = homogeneous_order(self.d, x, self.y)
  1358. orderb = homogeneous_order(self.e, x, self.y)
  1359. if ordera == orderb and ordera is not None:
  1360. self.u = Dummy('u')
  1361. if simplify((self.e + self.u*self.d).subs({x: self.u, self.y: 1})) != 0:
  1362. return True
  1363. return False
  1364. return False
  1365. def _get_match_object(self):
  1366. fx = self.ode_problem.func
  1367. x = self.ode_problem.sym
  1368. self.u1 = Dummy('u1')
  1369. xarg = 0
  1370. yarg = 0
  1371. return [self.d, self.e, fx, x, self.u, self.u1, self.y, xarg, yarg]
  1372. def _get_general_solution(self, *, simplify_flag: bool = True):
  1373. d, e, fx, x, u, u1, y, xarg, yarg = self._get_match_object()
  1374. (C1,) = self.ode_problem.get_numbered_constants(num=1)
  1375. int = Integral(simplify((-d/(e + u1*d)).subs({x: u1, y: 1})), (u1, None, x/fx)) # type: ignore
  1376. sol = logcombine(Eq(log(fx), int + log(C1)), force=True)
  1377. gen_sol = sol.subs(fx, u).subs(((u, u - yarg), (x, x - xarg), (u, fx)))
  1378. return [gen_sol]
  1379. class HomogeneousCoeffBest(HomogeneousCoeffSubsIndepDivDep, HomogeneousCoeffSubsDepDivIndep):
  1380. r"""
  1381. Returns the best solution to an ODE from the two hints
  1382. ``1st_homogeneous_coeff_subs_dep_div_indep`` and
  1383. ``1st_homogeneous_coeff_subs_indep_div_dep``.
  1384. This is as determined by :py:meth:`~sympy.solvers.ode.ode.ode_sol_simplicity`.
  1385. See the
  1386. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`
  1387. and
  1388. :obj:`~sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`
  1389. docstrings for more information on these hints. Note that there is no
  1390. ``ode_1st_homogeneous_coeff_best_Integral`` hint.
  1391. Examples
  1392. ========
  1393. >>> from sympy import Function, dsolve, pprint
  1394. >>> from sympy.abc import x
  1395. >>> f = Function('f')
  1396. >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x),
  1397. ... hint='1st_homogeneous_coeff_best', simplify=False))
  1398. / 2 \
  1399. |3*x |
  1400. log|----- + 1|
  1401. | 2 |
  1402. \f (x) /
  1403. log(f(x)) = log(C1) - --------------
  1404. 3
  1405. References
  1406. ==========
  1407. - https://en.wikipedia.org/wiki/Homogeneous_differential_equation
  1408. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1409. Dover 1963, pp. 59
  1410. # indirect doctest
  1411. """
  1412. hint = "1st_homogeneous_coeff_best"
  1413. has_integral = False
  1414. order = [1]
  1415. def _verify(self, fx):
  1416. return HomogeneousCoeffSubsIndepDivDep._verify(self, fx) and \
  1417. HomogeneousCoeffSubsDepDivIndep._verify(self, fx)
  1418. def _get_general_solution(self, *, simplify_flag: bool = True):
  1419. # There are two substitutions that solve the equation, u1=y/x and u2=x/y
  1420. # # They produce different integrals, so try them both and see which
  1421. # # one is easier
  1422. sol1 = HomogeneousCoeffSubsIndepDivDep._get_general_solution(self)
  1423. sol2 = HomogeneousCoeffSubsDepDivIndep._get_general_solution(self)
  1424. fx = self.ode_problem.func
  1425. if simplify_flag:
  1426. sol1 = odesimp(self.ode_problem.eq, *sol1, fx, "1st_homogeneous_coeff_subs_indep_div_dep")
  1427. sol2 = odesimp(self.ode_problem.eq, *sol2, fx, "1st_homogeneous_coeff_subs_dep_div_indep")
  1428. # XXX: not simplify should be not simplify_flag. mypy correctly complains
  1429. return min([sol1, sol2], key=lambda x: ode_sol_simplicity(x, fx, trysolving=not simplify)) # type: ignore
  1430. class LinearCoefficients(HomogeneousCoeffBest):
  1431. r"""
  1432. Solves a differential equation with linear coefficients.
  1433. The general form of a differential equation with linear coefficients is
  1434. .. math:: y' + F\left(\!\frac{a_1 x + b_1 y + c_1}{a_2 x + b_2 y +
  1435. c_2}\!\right) = 0\text{,}
  1436. where `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are constants and `a_1 b_2
  1437. - a_2 b_1 \ne 0`.
  1438. This can be solved by substituting:
  1439. .. math:: x = x' + \frac{b_2 c_1 - b_1 c_2}{a_2 b_1 - a_1 b_2}
  1440. y = y' + \frac{a_1 c_2 - a_2 c_1}{a_2 b_1 - a_1
  1441. b_2}\text{.}
  1442. This substitution reduces the equation to a homogeneous differential
  1443. equation.
  1444. See Also
  1445. ========
  1446. :obj:`sympy.solvers.ode.single.HomogeneousCoeffBest`
  1447. :obj:`sympy.solvers.ode.single.HomogeneousCoeffSubsIndepDivDep`
  1448. :obj:`sympy.solvers.ode.single.HomogeneousCoeffSubsDepDivIndep`
  1449. Examples
  1450. ========
  1451. >>> from sympy import dsolve, Function, pprint
  1452. >>> from sympy.abc import x
  1453. >>> f = Function('f')
  1454. >>> df = f(x).diff(x)
  1455. >>> eq = (x + f(x) + 1)*df + (f(x) - 6*x + 1)
  1456. >>> dsolve(eq, hint='linear_coefficients')
  1457. [Eq(f(x), -x - sqrt(C1 + 7*x**2) - 1), Eq(f(x), -x + sqrt(C1 + 7*x**2) - 1)]
  1458. >>> pprint(dsolve(eq, hint='linear_coefficients'))
  1459. ___________ ___________
  1460. / 2 / 2
  1461. [f(x) = -x - \/ C1 + 7*x - 1, f(x) = -x + \/ C1 + 7*x - 1]
  1462. References
  1463. ==========
  1464. - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications
  1465. of the ACM, Volume 14, Number 8, August 1971, pp. 558
  1466. """
  1467. hint = "linear_coefficients"
  1468. has_integral = True
  1469. order = [1]
  1470. def _wilds(self, f, x, order):
  1471. d = Wild('d', exclude=[f(x).diff(x), f(x).diff(x, 2)])
  1472. e = Wild('e', exclude=[f(x).diff(x)])
  1473. return d, e
  1474. def _equation(self, fx, x, order):
  1475. d, e = self.wilds()
  1476. return d + e*fx.diff(x)
  1477. def _verify(self, fx):
  1478. self.d, self.e = self.wilds_match()
  1479. a, b = self.wilds()
  1480. F = self.d/self.e
  1481. x = self.ode_problem.sym
  1482. params = self._linear_coeff_match(F, fx)
  1483. if params:
  1484. self.xarg, self.yarg = params
  1485. u = Dummy('u')
  1486. t = Dummy('t')
  1487. self.y = Dummy('y')
  1488. # Dummy substitution for df and f(x).
  1489. dummy_eq = self.ode_problem.eq.subs(((fx.diff(x), t), (fx, u)))
  1490. reps = ((x, x + self.xarg), (u, u + self.yarg), (t, fx.diff(x)), (u, fx))
  1491. dummy_eq = simplify(dummy_eq.subs(reps))
  1492. # get the re-cast values for e and d
  1493. r2 = collect(expand(dummy_eq), [fx.diff(x), fx]).match(a*fx.diff(x) + b)
  1494. if r2:
  1495. self.d, self.e = r2[b], r2[a]
  1496. orderd = homogeneous_order(self.d, x, fx)
  1497. ordere = homogeneous_order(self.e, x, fx)
  1498. if orderd == ordere and orderd is not None:
  1499. self.d = self.d.subs(fx, self.y)
  1500. self.e = self.e.subs(fx, self.y)
  1501. return True
  1502. return False
  1503. return False
  1504. def _linear_coeff_match(self, expr, func):
  1505. r"""
  1506. Helper function to match hint ``linear_coefficients``.
  1507. Matches the expression to the form `(a_1 x + b_1 f(x) + c_1)/(a_2 x + b_2
  1508. f(x) + c_2)` where the following conditions hold:
  1509. 1. `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are Rationals;
  1510. 2. `c_1` or `c_2` are not equal to zero;
  1511. 3. `a_2 b_1 - a_1 b_2` is not equal to zero.
  1512. Return ``xarg``, ``yarg`` where
  1513. 1. ``xarg`` = `(b_2 c_1 - b_1 c_2)/(a_2 b_1 - a_1 b_2)`
  1514. 2. ``yarg`` = `(a_1 c_2 - a_2 c_1)/(a_2 b_1 - a_1 b_2)`
  1515. Examples
  1516. ========
  1517. >>> from sympy import Function, sin
  1518. >>> from sympy.abc import x
  1519. >>> from sympy.solvers.ode.single import LinearCoefficients
  1520. >>> f = Function('f')
  1521. >>> eq = (-25*f(x) - 8*x + 62)/(4*f(x) + 11*x - 11)
  1522. >>> obj = LinearCoefficients(eq)
  1523. >>> obj._linear_coeff_match(eq, f(x))
  1524. (1/9, 22/9)
  1525. >>> eq = sin((-5*f(x) - 8*x + 6)/(4*f(x) + x - 1))
  1526. >>> obj = LinearCoefficients(eq)
  1527. >>> obj._linear_coeff_match(eq, f(x))
  1528. (19/27, 2/27)
  1529. >>> eq = sin(f(x)/x)
  1530. >>> obj = LinearCoefficients(eq)
  1531. >>> obj._linear_coeff_match(eq, f(x))
  1532. """
  1533. f = func.func
  1534. x = func.args[0]
  1535. def abc(eq):
  1536. r'''
  1537. Internal function of _linear_coeff_match
  1538. that returns Rationals a, b, c
  1539. if eq is a*x + b*f(x) + c, else None.
  1540. '''
  1541. eq = _mexpand(eq)
  1542. c = eq.as_independent(x, f(x), as_Add=True)[0]
  1543. if not c.is_Rational:
  1544. return
  1545. a = eq.coeff(x)
  1546. if not a.is_Rational:
  1547. return
  1548. b = eq.coeff(f(x))
  1549. if not b.is_Rational:
  1550. return
  1551. if eq == a*x + b*f(x) + c:
  1552. return a, b, c
  1553. def match(arg):
  1554. r'''
  1555. Internal function of _linear_coeff_match that returns Rationals a1,
  1556. b1, c1, a2, b2, c2 and a2*b1 - a1*b2 of the expression (a1*x + b1*f(x)
  1557. + c1)/(a2*x + b2*f(x) + c2) if one of c1 or c2 and a2*b1 - a1*b2 is
  1558. non-zero, else None.
  1559. '''
  1560. n, d = arg.together().as_numer_denom()
  1561. m = abc(n)
  1562. if m is not None:
  1563. a1, b1, c1 = m
  1564. m = abc(d)
  1565. if m is not None:
  1566. a2, b2, c2 = m
  1567. d = a2*b1 - a1*b2
  1568. if (c1 or c2) and d:
  1569. return a1, b1, c1, a2, b2, c2, d
  1570. m = [fi.args[0] for fi in expr.atoms(Function) if fi.func != f and
  1571. len(fi.args) == 1 and not fi.args[0].is_Function] or {expr}
  1572. m1 = match(m.pop())
  1573. if m1 and all(match(mi) == m1 for mi in m):
  1574. a1, b1, c1, a2, b2, c2, denom = m1
  1575. return (b2*c1 - b1*c2)/denom, (a1*c2 - a2*c1)/denom
  1576. def _get_match_object(self):
  1577. fx = self.ode_problem.func
  1578. x = self.ode_problem.sym
  1579. self.u1 = Dummy('u1')
  1580. u = Dummy('u')
  1581. return [self.d, self.e, fx, x, u, self.u1, self.y, self.xarg, self.yarg]
  1582. class NthOrderReducible(SingleODESolver):
  1583. r"""
  1584. Solves ODEs that only involve derivatives of the dependent variable using
  1585. a substitution of the form `f^n(x) = g(x)`.
  1586. For example any second order ODE of the form `f''(x) = h(f'(x), x)` can be
  1587. transformed into a pair of 1st order ODEs `g'(x) = h(g(x), x)` and
  1588. `f'(x) = g(x)`. Usually the 1st order ODE for `g` is easier to solve. If
  1589. that gives an explicit solution for `g` then `f` is found simply by
  1590. integration.
  1591. Examples
  1592. ========
  1593. >>> from sympy import Function, dsolve, Eq
  1594. >>> from sympy.abc import x
  1595. >>> f = Function('f')
  1596. >>> eq = Eq(x*f(x).diff(x)**2 + f(x).diff(x, 2), 0)
  1597. >>> dsolve(eq, f(x), hint='nth_order_reducible')
  1598. ... # doctest: +NORMALIZE_WHITESPACE
  1599. Eq(f(x), C1 - sqrt(-1/C2)*log(-C2*sqrt(-1/C2) + x) + sqrt(-1/C2)*log(C2*sqrt(-1/C2) + x))
  1600. """
  1601. hint = "nth_order_reducible"
  1602. has_integral = False
  1603. def _matches(self):
  1604. # Any ODE that can be solved with a substitution and
  1605. # repeated integration e.g.:
  1606. # `d^2/dx^2(y) + x*d/dx(y) = constant
  1607. #f'(x) must be finite for this to work
  1608. eq = self.ode_problem.eq_preprocessed
  1609. func = self.ode_problem.func
  1610. x = self.ode_problem.sym
  1611. r"""
  1612. Matches any differential equation that can be rewritten with a smaller
  1613. order. Only derivatives of ``func`` alone, wrt a single variable,
  1614. are considered, and only in them should ``func`` appear.
  1615. """
  1616. # ODE only handles functions of 1 variable so this affirms that state
  1617. assert len(func.args) == 1
  1618. vc = [d.variable_count[0] for d in eq.atoms(Derivative)
  1619. if d.expr == func and len(d.variable_count) == 1]
  1620. ords = [c for v, c in vc if v == x]
  1621. if len(ords) < 2:
  1622. return False
  1623. self.smallest = min(ords)
  1624. # make sure func does not appear outside of derivatives
  1625. D = Dummy()
  1626. if eq.subs(func.diff(x, self.smallest), D).has(func):
  1627. return False
  1628. return True
  1629. def _get_general_solution(self, *, simplify_flag: bool = True):
  1630. eq = self.ode_problem.eq
  1631. f = self.ode_problem.func.func
  1632. x = self.ode_problem.sym
  1633. n = self.smallest
  1634. # get a unique function name for g
  1635. names = [a.name for a in eq.atoms(AppliedUndef)]
  1636. while True:
  1637. name = Dummy().name
  1638. if name not in names:
  1639. g = Function(name)
  1640. break
  1641. w = f(x).diff(x, n)
  1642. geq = eq.subs(w, g(x))
  1643. gsol = dsolve(geq, g(x))
  1644. if not isinstance(gsol, list):
  1645. gsol = [gsol]
  1646. # Might be multiple solutions to the reduced ODE:
  1647. fsol = []
  1648. for gsoli in gsol:
  1649. fsoli = dsolve(gsoli.subs(g(x), w), f(x)) # or do integration n times
  1650. fsol.append(fsoli)
  1651. return fsol
  1652. class SecondHypergeometric(SingleODESolver):
  1653. r"""
  1654. Solves 2nd order linear differential equations.
  1655. It computes special function solutions which can be expressed using the
  1656. 2F1, 1F1 or 0F1 hypergeometric functions.
  1657. .. math:: y'' + A(x) y' + B(x) y = 0\text{,}
  1658. where `A` and `B` are rational functions.
  1659. These kinds of differential equations have solution of non-Liouvillian form.
  1660. Given linear ODE can be obtained from 2F1 given by
  1661. .. math:: (x^2 - x) y'' + ((a + b + 1) x - c) y' + b a y = 0\text{,}
  1662. where {a, b, c} are arbitrary constants.
  1663. Notes
  1664. =====
  1665. The algorithm should find any solution of the form
  1666. .. math:: y = P(x) _pF_q(..; ..;\frac{\alpha x^k + \beta}{\gamma x^k + \delta})\text{,}
  1667. where pFq is any of 2F1, 1F1 or 0F1 and `P` is an "arbitrary function".
  1668. Currently only the 2F1 case is implemented in SymPy but the other cases are
  1669. described in the paper and could be implemented in future (contributions
  1670. welcome!).
  1671. Examples
  1672. ========
  1673. >>> from sympy import Function, dsolve, pprint
  1674. >>> from sympy.abc import x
  1675. >>> f = Function('f')
  1676. >>> eq = (x*x - x)*f(x).diff(x,2) + (5*x - 1)*f(x).diff(x) + 4*f(x)
  1677. >>> pprint(dsolve(eq, f(x), '2nd_hypergeometric'))
  1678. _
  1679. / / 4 \\ |_ /-1, -1 | \
  1680. |C1 + C2*|log(x) + -----||* | | | x|
  1681. \ \ x + 1// 2 1 \ 1 | /
  1682. f(x) = --------------------------------------------
  1683. 3
  1684. (x - 1)
  1685. References
  1686. ==========
  1687. - "Non-Liouvillian solutions for second order linear ODEs" by L. Chan, E.S. Cheb-Terrab
  1688. """
  1689. hint = "2nd_hypergeometric"
  1690. has_integral = True
  1691. def _matches(self):
  1692. eq = self.ode_problem.eq_preprocessed
  1693. func = self.ode_problem.func
  1694. r = match_2nd_hypergeometric(eq, func)
  1695. self.match_object = None
  1696. if r:
  1697. A, B = r
  1698. d = equivalence_hypergeometric(A, B, func)
  1699. if d:
  1700. if d['type'] == "2F1":
  1701. self.match_object = match_2nd_2F1_hypergeometric(d['I0'], d['k'], d['sing_point'], func)
  1702. if self.match_object is not None:
  1703. self.match_object.update({'A':A, 'B':B})
  1704. # We can extend it for 1F1 and 0F1 type also.
  1705. return self.match_object is not None
  1706. def _get_general_solution(self, *, simplify_flag: bool = True):
  1707. eq = self.ode_problem.eq
  1708. func = self.ode_problem.func
  1709. if self.match_object['type'] == "2F1":
  1710. sol = get_sol_2F1_hypergeometric(eq, func, self.match_object)
  1711. if sol is None:
  1712. raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by"
  1713. + " the hypergeometric method")
  1714. return [sol]
  1715. class NthLinearConstantCoeffHomogeneous(SingleODESolver):
  1716. r"""
  1717. Solves an `n`\th order linear homogeneous differential equation with
  1718. constant coefficients.
  1719. This is an equation of the form
  1720. .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
  1721. + a_0 f(x) = 0\text{.}
  1722. These equations can be solved in a general manner, by taking the roots of
  1723. the characteristic equation `a_n m^n + a_{n-1} m^{n-1} + \cdots + a_1 m +
  1724. a_0 = 0`. The solution will then be the sum of `C_n x^i e^{r x}` terms,
  1725. for each where `C_n` is an arbitrary constant, `r` is a root of the
  1726. characteristic equation and `i` is one of each from 0 to the multiplicity
  1727. of the root - 1 (for example, a root 3 of multiplicity 2 would create the
  1728. terms `C_1 e^{3 x} + C_2 x e^{3 x}`). The exponential is usually expanded
  1729. for complex roots using Euler's equation `e^{I x} = \cos(x) + I \sin(x)`.
  1730. Complex roots always come in conjugate pairs in polynomials with real
  1731. coefficients, so the two roots will be represented (after simplifying the
  1732. constants) as `e^{a x} \left(C_1 \cos(b x) + C_2 \sin(b x)\right)`.
  1733. If SymPy cannot find exact roots to the characteristic equation, a
  1734. :py:class:`~sympy.polys.rootoftools.ComplexRootOf` instance will be return
  1735. instead.
  1736. >>> from sympy import Function, dsolve
  1737. >>> from sympy.abc import x
  1738. >>> f = Function('f')
  1739. >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x),
  1740. ... hint='nth_linear_constant_coeff_homogeneous')
  1741. ... # doctest: +NORMALIZE_WHITESPACE
  1742. Eq(f(x), C5*exp(x*CRootOf(_x**5 + 10*_x - 2, 0))
  1743. + (C1*sin(x*im(CRootOf(_x**5 + 10*_x - 2, 1)))
  1744. + C2*cos(x*im(CRootOf(_x**5 + 10*_x - 2, 1))))*exp(x*re(CRootOf(_x**5 + 10*_x - 2, 1)))
  1745. + (C3*sin(x*im(CRootOf(_x**5 + 10*_x - 2, 3)))
  1746. + C4*cos(x*im(CRootOf(_x**5 + 10*_x - 2, 3))))*exp(x*re(CRootOf(_x**5 + 10*_x - 2, 3))))
  1747. Note that because this method does not involve integration, there is no
  1748. ``nth_linear_constant_coeff_homogeneous_Integral`` hint.
  1749. Examples
  1750. ========
  1751. >>> from sympy import Function, dsolve, pprint
  1752. >>> from sympy.abc import x
  1753. >>> f = Function('f')
  1754. >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) -
  1755. ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x),
  1756. ... hint='nth_linear_constant_coeff_homogeneous'))
  1757. x -2*x
  1758. f(x) = (C1 + C2*x)*e + (C3*sin(x) + C4*cos(x))*e
  1759. References
  1760. ==========
  1761. - https://en.wikipedia.org/wiki/Linear_differential_equation section:
  1762. Nonhomogeneous_equation_with_constant_coefficients
  1763. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1764. Dover 1963, pp. 211
  1765. # indirect doctest
  1766. """
  1767. hint = "nth_linear_constant_coeff_homogeneous"
  1768. has_integral = False
  1769. def _matches(self):
  1770. eq = self.ode_problem.eq_high_order_free
  1771. func = self.ode_problem.func
  1772. order = self.ode_problem.order
  1773. x = self.ode_problem.sym
  1774. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1775. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1776. if not self.r[-1]:
  1777. return True
  1778. else:
  1779. return False
  1780. return False
  1781. def _get_general_solution(self, *, simplify_flag: bool = True):
  1782. fx = self.ode_problem.func
  1783. order = self.ode_problem.order
  1784. roots, collectterms = _get_const_characteristic_eq_sols(self.r, fx, order)
  1785. # A generator of constants
  1786. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1787. gsol_rhs = Add(*[i*j for (i, j) in zip(constants, roots)])
  1788. gsol = Eq(fx, gsol_rhs)
  1789. if simplify_flag:
  1790. gsol = _get_simplified_sol([gsol], fx, collectterms)
  1791. return [gsol]
  1792. class NthLinearConstantCoeffVariationOfParameters(SingleODESolver):
  1793. r"""
  1794. Solves an `n`\th order linear differential equation with constant
  1795. coefficients using the method of variation of parameters.
  1796. This method works on any differential equations of the form
  1797. .. math:: f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x) + a_0
  1798. f(x) = P(x)\text{.}
  1799. This method works by assuming that the particular solution takes the form
  1800. .. math:: \sum_{x=1}^{n} c_i(x) y_i(x)\text{,}
  1801. where `y_i` is the `i`\th solution to the homogeneous equation. The
  1802. solution is then solved using Wronskian's and Cramer's Rule. The
  1803. particular solution is given by
  1804. .. math:: \sum_{x=1}^n \left( \int \frac{W_i(x)}{W(x)} \,dx
  1805. \right) y_i(x) \text{,}
  1806. where `W(x)` is the Wronskian of the fundamental system (the system of `n`
  1807. linearly independent solutions to the homogeneous equation), and `W_i(x)`
  1808. is the Wronskian of the fundamental system with the `i`\th column replaced
  1809. with `[0, 0, \cdots, 0, P(x)]`.
  1810. This method is general enough to solve any `n`\th order inhomogeneous
  1811. linear differential equation with constant coefficients, but sometimes
  1812. SymPy cannot simplify the Wronskian well enough to integrate it. If this
  1813. method hangs, try using the
  1814. ``nth_linear_constant_coeff_variation_of_parameters_Integral`` hint and
  1815. simplifying the integrals manually. Also, prefer using
  1816. ``nth_linear_constant_coeff_undetermined_coefficients`` when it
  1817. applies, because it does not use integration, making it faster and more
  1818. reliable.
  1819. Warning, using simplify=False with
  1820. 'nth_linear_constant_coeff_variation_of_parameters' in
  1821. :py:meth:`~sympy.solvers.ode.dsolve` may cause it to hang, because it will
  1822. not attempt to simplify the Wronskian before integrating. It is
  1823. recommended that you only use simplify=False with
  1824. 'nth_linear_constant_coeff_variation_of_parameters_Integral' for this
  1825. method, especially if the solution to the homogeneous equation has
  1826. trigonometric functions in it.
  1827. Examples
  1828. ========
  1829. >>> from sympy import Function, dsolve, pprint, exp, log
  1830. >>> from sympy.abc import x
  1831. >>> f = Function('f')
  1832. >>> pprint(dsolve(f(x).diff(x, 3) - 3*f(x).diff(x, 2) +
  1833. ... 3*f(x).diff(x) - f(x) - exp(x)*log(x), f(x),
  1834. ... hint='nth_linear_constant_coeff_variation_of_parameters'))
  1835. / / / x*log(x) 11*x\\\ x
  1836. f(x) = |C1 + x*|C2 + x*|C3 + -------- - ----|||*e
  1837. \ \ \ 6 36 ///
  1838. References
  1839. ==========
  1840. - https://en.wikipedia.org/wiki/Variation_of_parameters
  1841. - https://planetmath.org/VariationOfParameters
  1842. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1843. Dover 1963, pp. 233
  1844. # indirect doctest
  1845. """
  1846. hint = "nth_linear_constant_coeff_variation_of_parameters"
  1847. has_integral = True
  1848. def _matches(self):
  1849. eq = self.ode_problem.eq_high_order_free
  1850. func = self.ode_problem.func
  1851. order = self.ode_problem.order
  1852. x = self.ode_problem.sym
  1853. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1854. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1855. if self.r[-1]:
  1856. return True
  1857. else:
  1858. return False
  1859. return False
  1860. def _get_general_solution(self, *, simplify_flag: bool = True):
  1861. eq = self.ode_problem.eq_high_order_free
  1862. f = self.ode_problem.func.func
  1863. x = self.ode_problem.sym
  1864. order = self.ode_problem.order
  1865. roots, collectterms = _get_const_characteristic_eq_sols(self.r, f(x), order)
  1866. # A generator of constants
  1867. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1868. homogen_sol_rhs = Add(*[i*j for (i, j) in zip(constants, roots)])
  1869. homogen_sol = Eq(f(x), homogen_sol_rhs)
  1870. homogen_sol = _solve_variation_of_parameters(eq, f(x), roots, homogen_sol, order, self.r, simplify_flag)
  1871. if simplify_flag:
  1872. homogen_sol = _get_simplified_sol([homogen_sol], f(x), collectterms)
  1873. return [homogen_sol]
  1874. class NthLinearConstantCoeffUndeterminedCoefficients(SingleODESolver):
  1875. r"""
  1876. Solves an `n`\th order linear differential equation with constant
  1877. coefficients using the method of undetermined coefficients.
  1878. This method works on differential equations of the form
  1879. .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x)
  1880. + a_0 f(x) = P(x)\text{,}
  1881. where `P(x)` is a function that has a finite number of linearly
  1882. independent derivatives.
  1883. Functions that fit this requirement are finite sums functions of the form
  1884. `a x^i e^{b x} \sin(c x + d)` or `a x^i e^{b x} \cos(c x + d)`, where `i`
  1885. is a non-negative integer and `a`, `b`, `c`, and `d` are constants. For
  1886. example any polynomial in `x`, functions like `x^2 e^{2 x}`, `x \sin(x)`,
  1887. and `e^x \cos(x)` can all be used. Products of `\sin`'s and `\cos`'s have
  1888. a finite number of derivatives, because they can be expanded into `\sin(a
  1889. x)` and `\cos(b x)` terms. However, SymPy currently cannot do that
  1890. expansion, so you will need to manually rewrite the expression in terms of
  1891. the above to use this method. So, for example, you will need to manually
  1892. convert `\sin^2(x)` into `(1 + \cos(2 x))/2` to properly apply the method
  1893. of undetermined coefficients on it.
  1894. This method works by creating a trial function from the expression and all
  1895. of its linear independent derivatives and substituting them into the
  1896. original ODE. The coefficients for each term will be a system of linear
  1897. equations, which are be solved for and substituted, giving the solution.
  1898. If any of the trial functions are linearly dependent on the solution to
  1899. the homogeneous equation, they are multiplied by sufficient `x` to make
  1900. them linearly independent.
  1901. Examples
  1902. ========
  1903. >>> from sympy import Function, dsolve, pprint, exp, cos
  1904. >>> from sympy.abc import x
  1905. >>> f = Function('f')
  1906. >>> pprint(dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) -
  1907. ... 4*exp(-x)*x**2 + cos(2*x), f(x),
  1908. ... hint='nth_linear_constant_coeff_undetermined_coefficients'))
  1909. / / 3\\
  1910. | | x || -x 4*sin(2*x) 3*cos(2*x)
  1911. f(x) = |C1 + x*|C2 + --||*e - ---------- + ----------
  1912. \ \ 3 // 25 25
  1913. References
  1914. ==========
  1915. - https://en.wikipedia.org/wiki/Method_of_undetermined_coefficients
  1916. - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations",
  1917. Dover 1963, pp. 221
  1918. # indirect doctest
  1919. """
  1920. hint = "nth_linear_constant_coeff_undetermined_coefficients"
  1921. has_integral = False
  1922. def _matches(self):
  1923. eq = self.ode_problem.eq_high_order_free
  1924. func = self.ode_problem.func
  1925. order = self.ode_problem.order
  1926. x = self.ode_problem.sym
  1927. self.r = self.ode_problem.get_linear_coefficients(eq, func, order)
  1928. does_match = False
  1929. if order and self.r and not any(self.r[i].has(x) for i in self.r if i >= 0):
  1930. if self.r[-1]:
  1931. eq_homogeneous = Add(eq, -self.r[-1])
  1932. undetcoeff = _undetermined_coefficients_match(self.r[-1], x, func, eq_homogeneous)
  1933. if undetcoeff['test']:
  1934. self.trialset = undetcoeff['trialset']
  1935. does_match = True
  1936. return does_match
  1937. def _get_general_solution(self, *, simplify_flag: bool = True):
  1938. eq = self.ode_problem.eq
  1939. f = self.ode_problem.func.func
  1940. x = self.ode_problem.sym
  1941. order = self.ode_problem.order
  1942. roots, collectterms = _get_const_characteristic_eq_sols(self.r, f(x), order)
  1943. # A generator of constants
  1944. constants = self.ode_problem.get_numbered_constants(num=len(roots))
  1945. homogen_sol_rhs = Add(*[i*j for (i, j) in zip(constants, roots)])
  1946. homogen_sol = Eq(f(x), homogen_sol_rhs)
  1947. self.r.update({'list': roots, 'sol': homogen_sol, 'simpliy_flag': simplify_flag})
  1948. gsol = _solve_undetermined_coefficients(eq, f(x), order, self.r, self.trialset)
  1949. if simplify_flag:
  1950. gsol = _get_simplified_sol([gsol], f(x), collectterms)
  1951. return [gsol]
  1952. class NthLinearEulerEqHomogeneous(SingleODESolver):
  1953. r"""
  1954. Solves an `n`\th order linear homogeneous variable-coefficient
  1955. Cauchy-Euler equidimensional ordinary differential equation.
  1956. This is an equation with form `0 = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  1957. \cdots`.
  1958. These equations can be solved in a general manner, by substituting
  1959. solutions of the form `f(x) = x^r`, and deriving a characteristic equation
  1960. for `r`. When there are repeated roots, we include extra terms of the
  1961. form `C_{r k} \ln^k(x) x^r`, where `C_{r k}` is an arbitrary integration
  1962. constant, `r` is a root of the characteristic equation, and `k` ranges
  1963. over the multiplicity of `r`. In the cases where the roots are complex,
  1964. solutions of the form `C_1 x^a \sin(b \log(x)) + C_2 x^a \cos(b \log(x))`
  1965. are returned, based on expansions with Euler's formula. The general
  1966. solution is the sum of the terms found. If SymPy cannot find exact roots
  1967. to the characteristic equation, a
  1968. :py:obj:`~.ComplexRootOf` instance will be returned
  1969. instead.
  1970. >>> from sympy import Function, dsolve
  1971. >>> from sympy.abc import x
  1972. >>> f = Function('f')
  1973. >>> dsolve(4*x**2*f(x).diff(x, 2) + f(x), f(x),
  1974. ... hint='nth_linear_euler_eq_homogeneous')
  1975. ... # doctest: +NORMALIZE_WHITESPACE
  1976. Eq(f(x), sqrt(x)*(C1 + C2*log(x)))
  1977. Note that because this method does not involve integration, there is no
  1978. ``nth_linear_euler_eq_homogeneous_Integral`` hint.
  1979. The following is for internal use:
  1980. - ``returns = 'sol'`` returns the solution to the ODE.
  1981. - ``returns = 'list'`` returns a list of linearly independent solutions,
  1982. corresponding to the fundamental solution set, for use with non
  1983. homogeneous solution methods like variation of parameters and
  1984. undetermined coefficients. Note that, though the solutions should be
  1985. linearly independent, this function does not explicitly check that. You
  1986. can do ``assert simplify(wronskian(sollist)) != 0`` to check for linear
  1987. independence. Also, ``assert len(sollist) == order`` will need to pass.
  1988. - ``returns = 'both'``, return a dictionary ``{'sol': <solution to ODE>,
  1989. 'list': <list of linearly independent solutions>}``.
  1990. Examples
  1991. ========
  1992. >>> from sympy import Function, dsolve, pprint
  1993. >>> from sympy.abc import x
  1994. >>> f = Function('f')
  1995. >>> eq = f(x).diff(x, 2)*x**2 - 4*f(x).diff(x)*x + 6*f(x)
  1996. >>> pprint(dsolve(eq, f(x),
  1997. ... hint='nth_linear_euler_eq_homogeneous'))
  1998. 2
  1999. f(x) = x *(C1 + C2*x)
  2000. References
  2001. ==========
  2002. - https://en.wikipedia.org/wiki/Cauchy%E2%80%93Euler_equation
  2003. - C. Bender & S. Orszag, "Advanced Mathematical Methods for Scientists and
  2004. Engineers", Springer 1999, pp. 12
  2005. # indirect doctest
  2006. """
  2007. hint = "nth_linear_euler_eq_homogeneous"
  2008. has_integral = False
  2009. def _matches(self):
  2010. eq = self.ode_problem.eq_preprocessed
  2011. f = self.ode_problem.func.func
  2012. order = self.ode_problem.order
  2013. x = self.ode_problem.sym
  2014. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2015. self.r = None
  2016. does_match = False
  2017. if order and match:
  2018. coeff = match[order]
  2019. factor = x**order / coeff
  2020. self.r = {i: factor*match[i] for i in match}
  2021. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2022. self.r if i >= 0):
  2023. if not self.r[-1]:
  2024. does_match = True
  2025. return does_match
  2026. def _get_general_solution(self, *, simplify_flag: bool = True):
  2027. fx = self.ode_problem.func
  2028. eq = self.ode_problem.eq
  2029. homogen_sol = _get_euler_characteristic_eq_sols(eq, fx, self.r)[0]
  2030. return [homogen_sol]
  2031. class NthLinearEulerEqNonhomogeneousVariationOfParameters(SingleODESolver):
  2032. r"""
  2033. Solves an `n`\th order linear non homogeneous Cauchy-Euler equidimensional
  2034. ordinary differential equation using variation of parameters.
  2035. This is an equation with form `g(x) = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  2036. \cdots`.
  2037. This method works by assuming that the particular solution takes the form
  2038. .. math:: \sum_{x=1}^{n} c_i(x) y_i(x) {a_n} {x^n} \text{, }
  2039. where `y_i` is the `i`\th solution to the homogeneous equation. The
  2040. solution is then solved using Wronskian's and Cramer's Rule. The
  2041. particular solution is given by multiplying eq given below with `a_n x^{n}`
  2042. .. math:: \sum_{x=1}^n \left( \int \frac{W_i(x)}{W(x)} \, dx
  2043. \right) y_i(x) \text{, }
  2044. where `W(x)` is the Wronskian of the fundamental system (the system of `n`
  2045. linearly independent solutions to the homogeneous equation), and `W_i(x)`
  2046. is the Wronskian of the fundamental system with the `i`\th column replaced
  2047. with `[0, 0, \cdots, 0, \frac{x^{- n}}{a_n} g{\left(x \right)}]`.
  2048. This method is general enough to solve any `n`\th order inhomogeneous
  2049. linear differential equation, but sometimes SymPy cannot simplify the
  2050. Wronskian well enough to integrate it. If this method hangs, try using the
  2051. ``nth_linear_constant_coeff_variation_of_parameters_Integral`` hint and
  2052. simplifying the integrals manually. Also, prefer using
  2053. ``nth_linear_constant_coeff_undetermined_coefficients`` when it
  2054. applies, because it does not use integration, making it faster and more
  2055. reliable.
  2056. Warning, using simplify=False with
  2057. 'nth_linear_constant_coeff_variation_of_parameters' in
  2058. :py:meth:`~sympy.solvers.ode.dsolve` may cause it to hang, because it will
  2059. not attempt to simplify the Wronskian before integrating. It is
  2060. recommended that you only use simplify=False with
  2061. 'nth_linear_constant_coeff_variation_of_parameters_Integral' for this
  2062. method, especially if the solution to the homogeneous equation has
  2063. trigonometric functions in it.
  2064. Examples
  2065. ========
  2066. >>> from sympy import Function, dsolve, Derivative
  2067. >>> from sympy.abc import x
  2068. >>> f = Function('f')
  2069. >>> eq = x**2*Derivative(f(x), x, x) - 2*x*Derivative(f(x), x) + 2*f(x) - x**4
  2070. >>> dsolve(eq, f(x),
  2071. ... hint='nth_linear_euler_eq_nonhomogeneous_variation_of_parameters').expand()
  2072. Eq(f(x), C1*x + C2*x**2 + x**4/6)
  2073. """
  2074. hint = "nth_linear_euler_eq_nonhomogeneous_variation_of_parameters"
  2075. has_integral = True
  2076. def _matches(self):
  2077. eq = self.ode_problem.eq_preprocessed
  2078. f = self.ode_problem.func.func
  2079. order = self.ode_problem.order
  2080. x = self.ode_problem.sym
  2081. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2082. self.r = None
  2083. does_match = False
  2084. if order and match:
  2085. coeff = match[order]
  2086. factor = x**order / coeff
  2087. self.r = {i: factor*match[i] for i in match}
  2088. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2089. self.r if i >= 0):
  2090. if self.r[-1]:
  2091. does_match = True
  2092. return does_match
  2093. def _get_general_solution(self, *, simplify_flag: bool = True):
  2094. eq = self.ode_problem.eq
  2095. f = self.ode_problem.func.func
  2096. x = self.ode_problem.sym
  2097. order = self.ode_problem.order
  2098. homogen_sol, roots = _get_euler_characteristic_eq_sols(eq, f(x), self.r)
  2099. self.r[-1] = self.r[-1]/self.r[order]
  2100. sol = _solve_variation_of_parameters(eq, f(x), roots, homogen_sol, order, self.r, simplify_flag)
  2101. return [Eq(f(x), homogen_sol.rhs + (sol.rhs - homogen_sol.rhs)*self.r[order])]
  2102. class NthLinearEulerEqNonhomogeneousUndeterminedCoefficients(SingleODESolver):
  2103. r"""
  2104. Solves an `n`\th order linear non homogeneous Cauchy-Euler equidimensional
  2105. ordinary differential equation using undetermined coefficients.
  2106. This is an equation with form `g(x) = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x)
  2107. \cdots`.
  2108. These equations can be solved in a general manner, by substituting
  2109. solutions of the form `x = exp(t)`, and deriving a characteristic equation
  2110. of form `g(exp(t)) = b_0 f(t) + b_1 f'(t) + b_2 f''(t) \cdots` which can
  2111. be then solved by nth_linear_constant_coeff_undetermined_coefficients if
  2112. g(exp(t)) has finite number of linearly independent derivatives.
  2113. Functions that fit this requirement are finite sums functions of the form
  2114. `a x^i e^{b x} \sin(c x + d)` or `a x^i e^{b x} \cos(c x + d)`, where `i`
  2115. is a non-negative integer and `a`, `b`, `c`, and `d` are constants. For
  2116. example any polynomial in `x`, functions like `x^2 e^{2 x}`, `x \sin(x)`,
  2117. and `e^x \cos(x)` can all be used. Products of `\sin`'s and `\cos`'s have
  2118. a finite number of derivatives, because they can be expanded into `\sin(a
  2119. x)` and `\cos(b x)` terms. However, SymPy currently cannot do that
  2120. expansion, so you will need to manually rewrite the expression in terms of
  2121. the above to use this method. So, for example, you will need to manually
  2122. convert `\sin^2(x)` into `(1 + \cos(2 x))/2` to properly apply the method
  2123. of undetermined coefficients on it.
  2124. After replacement of x by exp(t), this method works by creating a trial function
  2125. from the expression and all of its linear independent derivatives and
  2126. substituting them into the original ODE. The coefficients for each term
  2127. will be a system of linear equations, which are be solved for and
  2128. substituted, giving the solution. If any of the trial functions are linearly
  2129. dependent on the solution to the homogeneous equation, they are multiplied
  2130. by sufficient `x` to make them linearly independent.
  2131. Examples
  2132. ========
  2133. >>> from sympy import dsolve, Function, Derivative, log
  2134. >>> from sympy.abc import x
  2135. >>> f = Function('f')
  2136. >>> eq = x**2*Derivative(f(x), x, x) - 2*x*Derivative(f(x), x) + 2*f(x) - log(x)
  2137. >>> dsolve(eq, f(x),
  2138. ... hint='nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients').expand()
  2139. Eq(f(x), C1*x + C2*x**2 + log(x)/2 + 3/4)
  2140. """
  2141. hint = "nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients"
  2142. has_integral = False
  2143. def _matches(self):
  2144. eq = self.ode_problem.eq_high_order_free
  2145. f = self.ode_problem.func.func
  2146. order = self.ode_problem.order
  2147. x = self.ode_problem.sym
  2148. match = self.ode_problem.get_linear_coefficients(eq, f(x), order)
  2149. self.r = None
  2150. does_match = False
  2151. if order and match:
  2152. coeff = match[order]
  2153. factor = x**order / coeff
  2154. self.r = {i: factor*match[i] for i in match}
  2155. if self.r and all(_test_term(self.r[i], f(x), i) for i in
  2156. self.r if i >= 0):
  2157. if self.r[-1]:
  2158. e, re = posify(self.r[-1].subs(x, exp(x)))
  2159. undetcoeff = _undetermined_coefficients_match(e.subs(re), x)
  2160. if undetcoeff['test']:
  2161. does_match = True
  2162. return does_match
  2163. def _get_general_solution(self, *, simplify_flag: bool = True):
  2164. f = self.ode_problem.func.func
  2165. x = self.ode_problem.sym
  2166. chareq, eq, symbol = S.Zero, S.Zero, Dummy('x')
  2167. for i in self.r.keys():
  2168. if i >= 0:
  2169. chareq += (self.r[i]*diff(x**symbol, x, i)*x**-symbol).expand()
  2170. for i in range(1, degree(Poly(chareq, symbol))+1):
  2171. eq += chareq.coeff(symbol**i)*diff(f(x), x, i)
  2172. if chareq.as_coeff_add(symbol)[0]:
  2173. eq += chareq.as_coeff_add(symbol)[0]*f(x)
  2174. e, re = posify(self.r[-1].subs(x, exp(x)))
  2175. eq += e.subs(re)
  2176. self.const_undet_instance = NthLinearConstantCoeffUndeterminedCoefficients(SingleODEProblem(eq, f(x), x))
  2177. sol = self.const_undet_instance.get_general_solution(simplify = simplify_flag)[0]
  2178. sol = sol.subs(x, log(x)) # type: ignore
  2179. sol = sol.subs(f(log(x)), f(x)).expand() # type: ignore
  2180. return [sol]
  2181. class SecondLinearBessel(SingleODESolver):
  2182. r"""
  2183. Gives solution of the Bessel differential equation
  2184. .. math :: x^2 \frac{d^2y}{dx^2} + x \frac{dy}{dx} y(x) + (x^2-n^2) y(x)
  2185. if `n` is integer then the solution is of the form ``Eq(f(x), C0 besselj(n,x)
  2186. + C1 bessely(n,x))`` as both the solutions are linearly independent else if
  2187. `n` is a fraction then the solution is of the form ``Eq(f(x), C0 besselj(n,x)
  2188. + C1 besselj(-n,x))`` which can also transform into ``Eq(f(x), C0 besselj(n,x)
  2189. + C1 bessely(n,x))``.
  2190. Examples
  2191. ========
  2192. >>> from sympy.abc import x
  2193. >>> from sympy import Symbol
  2194. >>> v = Symbol('v', positive=True)
  2195. >>> from sympy import dsolve, Function
  2196. >>> f = Function('f')
  2197. >>> y = f(x)
  2198. >>> genform = x**2*y.diff(x, 2) + x*y.diff(x) + (x**2 - v**2)*y
  2199. >>> dsolve(genform)
  2200. Eq(f(x), C1*besselj(v, x) + C2*bessely(v, x))
  2201. References
  2202. ==========
  2203. https://math24.net/bessel-differential-equation.html
  2204. """
  2205. hint = "2nd_linear_bessel"
  2206. has_integral = False
  2207. def _matches(self):
  2208. eq = self.ode_problem.eq_high_order_free
  2209. f = self.ode_problem.func
  2210. order = self.ode_problem.order
  2211. x = self.ode_problem.sym
  2212. df = f.diff(x)
  2213. a = Wild('a', exclude=[f,df])
  2214. b = Wild('b', exclude=[x, f,df])
  2215. a4 = Wild('a4', exclude=[x,f,df])
  2216. b4 = Wild('b4', exclude=[x,f,df])
  2217. c4 = Wild('c4', exclude=[x,f,df])
  2218. d4 = Wild('d4', exclude=[x,f,df])
  2219. a3 = Wild('a3', exclude=[f, df, f.diff(x, 2)])
  2220. b3 = Wild('b3', exclude=[f, df, f.diff(x, 2)])
  2221. c3 = Wild('c3', exclude=[f, df, f.diff(x, 2)])
  2222. deq = a3*(f.diff(x, 2)) + b3*df + c3*f
  2223. r = collect(eq,
  2224. [f.diff(x, 2), df, f]).match(deq)
  2225. if order == 2 and r:
  2226. if not all(r[key].is_polynomial() for key in r):
  2227. n, d = eq.as_numer_denom()
  2228. eq = expand(n)
  2229. r = collect(eq,
  2230. [f.diff(x, 2), df, f]).match(deq)
  2231. if r and r[a3] != 0:
  2232. # leading coeff of f(x).diff(x, 2)
  2233. coeff = factor(r[a3]).match(a4*(x-b)**b4)
  2234. if coeff:
  2235. # if coeff[b4] = 0 means constant coefficient
  2236. if coeff[b4] == 0:
  2237. return False
  2238. point = coeff[b]
  2239. else:
  2240. return False
  2241. if point:
  2242. r[a3] = simplify(r[a3].subs(x, x+point))
  2243. r[b3] = simplify(r[b3].subs(x, x+point))
  2244. r[c3] = simplify(r[c3].subs(x, x+point))
  2245. # making a3 in the form of x**2
  2246. r[a3] = cancel(r[a3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2247. r[b3] = cancel(r[b3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2248. r[c3] = cancel(r[c3]/(coeff[a4]*(x)**(-2+coeff[b4])))
  2249. # checking if b3 is of form c*(x-b)
  2250. coeff1 = factor(r[b3]).match(a4*(x))
  2251. if coeff1 is None:
  2252. return False
  2253. # c3 maybe of very complex form so I am simply checking (a - b) form
  2254. # if yes later I will match with the standard form of bessel in a and b
  2255. # a, b are wild variable defined above.
  2256. _coeff2 = expand(r[c3]).match(a - b)
  2257. if _coeff2 is None:
  2258. return False
  2259. # matching with standard form for c3
  2260. coeff2 = factor(_coeff2[a]).match(c4**2*(x)**(2*a4))
  2261. if coeff2 is None:
  2262. return False
  2263. if _coeff2[b] == 0:
  2264. coeff2[d4] = 0
  2265. else:
  2266. coeff2[d4] = factor(_coeff2[b]).match(d4**2)[d4]
  2267. self.rn = {'n':coeff2[d4], 'a4':coeff2[c4], 'd4':coeff2[a4]}
  2268. self.rn['c4'] = coeff1[a4]
  2269. self.rn['b4'] = point
  2270. return True
  2271. return False
  2272. def _get_general_solution(self, *, simplify_flag: bool = True):
  2273. f = self.ode_problem.func.func
  2274. x = self.ode_problem.sym
  2275. n = self.rn['n']
  2276. a4 = self.rn['a4']
  2277. c4 = self.rn['c4']
  2278. d4 = self.rn['d4']
  2279. b4 = self.rn['b4']
  2280. n = sqrt(n**2 + Rational(1, 4)*(c4 - 1)**2)
  2281. (C1, C2) = self.ode_problem.get_numbered_constants(num=2)
  2282. return [Eq(f(x), ((x**(Rational(1-c4,2)))*(C1*besselj(n/d4,a4*x**d4/d4)
  2283. + C2*bessely(n/d4,a4*x**d4/d4))).subs(x, x-b4))]
  2284. class SecondLinearAiry(SingleODESolver):
  2285. r"""
  2286. Gives solution of the Airy differential equation
  2287. .. math :: \frac{d^2y}{dx^2} + (a + b x) y(x) = 0
  2288. in terms of Airy special functions airyai and airybi.
  2289. Examples
  2290. ========
  2291. >>> from sympy import dsolve, Function
  2292. >>> from sympy.abc import x
  2293. >>> f = Function("f")
  2294. >>> eq = f(x).diff(x, 2) - x*f(x)
  2295. >>> dsolve(eq)
  2296. Eq(f(x), C1*airyai(x) + C2*airybi(x))
  2297. """
  2298. hint = "2nd_linear_airy"
  2299. has_integral = False
  2300. def _matches(self):
  2301. eq = self.ode_problem.eq_high_order_free
  2302. f = self.ode_problem.func
  2303. order = self.ode_problem.order
  2304. x = self.ode_problem.sym
  2305. df = f.diff(x)
  2306. a4 = Wild('a4', exclude=[x,f,df])
  2307. b4 = Wild('b4', exclude=[x,f,df])
  2308. match = self.ode_problem.get_linear_coefficients(eq, f, order)
  2309. does_match = False
  2310. if order == 2 and match and match[2] != 0:
  2311. if match[1].is_zero:
  2312. self.rn = cancel(match[0]/match[2]).match(a4+b4*x)
  2313. if self.rn and self.rn[b4] != 0:
  2314. self.rn = {'b':self.rn[a4],'m':self.rn[b4]}
  2315. does_match = True
  2316. return does_match
  2317. def _get_general_solution(self, *, simplify_flag: bool = True):
  2318. f = self.ode_problem.func.func
  2319. x = self.ode_problem.sym
  2320. (C1, C2) = self.ode_problem.get_numbered_constants(num=2)
  2321. b = self.rn['b']
  2322. m = self.rn['m']
  2323. if m.is_positive:
  2324. arg = - b/cbrt(m)**2 - cbrt(m)*x
  2325. elif m.is_negative:
  2326. arg = - b/cbrt(-m)**2 + cbrt(-m)*x
  2327. else:
  2328. arg = - b/cbrt(-m)**2 + cbrt(-m)*x
  2329. return [Eq(f(x), C1*airyai(arg) + C2*airybi(arg))]
  2330. class LieGroup(SingleODESolver):
  2331. r"""
  2332. This hint implements the Lie group method of solving first order differential
  2333. equations. The aim is to convert the given differential equation from the
  2334. given coordinate system into another coordinate system where it becomes
  2335. invariant under the one-parameter Lie group of translations. The converted
  2336. ODE can be easily solved by quadrature. It makes use of the
  2337. :py:meth:`sympy.solvers.ode.infinitesimals` function which returns the
  2338. infinitesimals of the transformation.
  2339. The coordinates `r` and `s` can be found by solving the following Partial
  2340. Differential Equations.
  2341. .. math :: \xi\frac{\partial r}{\partial x} + \eta\frac{\partial r}{\partial y}
  2342. = 0
  2343. .. math :: \xi\frac{\partial s}{\partial x} + \eta\frac{\partial s}{\partial y}
  2344. = 1
  2345. The differential equation becomes separable in the new coordinate system
  2346. .. math :: \frac{ds}{dr} = \frac{\frac{\partial s}{\partial x} +
  2347. h(x, y)\frac{\partial s}{\partial y}}{
  2348. \frac{\partial r}{\partial x} + h(x, y)\frac{\partial r}{\partial y}}
  2349. After finding the solution by integration, it is then converted back to the original
  2350. coordinate system by substituting `r` and `s` in terms of `x` and `y` again.
  2351. Examples
  2352. ========
  2353. >>> from sympy import Function, dsolve, exp, pprint
  2354. >>> from sympy.abc import x
  2355. >>> f = Function('f')
  2356. >>> pprint(dsolve(f(x).diff(x) + 2*x*f(x) - x*exp(-x**2), f(x),
  2357. ... hint='lie_group'))
  2358. / 2\ 2
  2359. | x | -x
  2360. f(x) = |C1 + --|*e
  2361. \ 2 /
  2362. References
  2363. ==========
  2364. - Solving differential equations by Symmetry Groups,
  2365. John Starrett, pp. 1 - pp. 14
  2366. """
  2367. hint = "lie_group"
  2368. has_integral = False
  2369. def _has_additional_params(self):
  2370. return 'xi' in self.ode_problem.params and 'eta' in self.ode_problem.params
  2371. def _matches(self):
  2372. eq = self.ode_problem.eq
  2373. f = self.ode_problem.func.func
  2374. order = self.ode_problem.order
  2375. x = self.ode_problem.sym
  2376. df = f(x).diff(x)
  2377. y = Dummy('y')
  2378. d = Wild('d', exclude=[df, f(x).diff(x, 2)])
  2379. e = Wild('e', exclude=[df])
  2380. does_match = False
  2381. if self._has_additional_params() and order == 1:
  2382. xi = self.ode_problem.params['xi']
  2383. eta = self.ode_problem.params['eta']
  2384. self.r3 = {'xi': xi, 'eta': eta}
  2385. r = collect(eq, df, exact=True).match(d + e * df)
  2386. if r:
  2387. r['d'] = d
  2388. r['e'] = e
  2389. r['y'] = y
  2390. r[d] = r[d].subs(f(x), y)
  2391. r[e] = r[e].subs(f(x), y)
  2392. self.r3.update(r)
  2393. does_match = True
  2394. return does_match
  2395. def _get_general_solution(self, *, simplify_flag: bool = True):
  2396. eq = self.ode_problem.eq
  2397. x = self.ode_problem.sym
  2398. func = self.ode_problem.func
  2399. order = self.ode_problem.order
  2400. df = func.diff(x)
  2401. try:
  2402. eqsol = solve(eq, df)
  2403. except NotImplementedError:
  2404. eqsol = []
  2405. desols = []
  2406. for s in eqsol:
  2407. sol = _ode_lie_group(s, func, order, match=self.r3)
  2408. if sol:
  2409. desols.extend(sol)
  2410. if desols == []:
  2411. raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by"
  2412. + " the lie group method")
  2413. return desols
  2414. solver_map = {
  2415. 'factorable': Factorable,
  2416. 'nth_linear_constant_coeff_homogeneous': NthLinearConstantCoeffHomogeneous,
  2417. 'nth_linear_euler_eq_homogeneous': NthLinearEulerEqHomogeneous,
  2418. 'nth_linear_constant_coeff_undetermined_coefficients': NthLinearConstantCoeffUndeterminedCoefficients,
  2419. 'nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients': NthLinearEulerEqNonhomogeneousUndeterminedCoefficients,
  2420. 'separable': Separable,
  2421. '1st_exact': FirstExact,
  2422. '1st_linear': FirstLinear,
  2423. 'Bernoulli': Bernoulli,
  2424. 'Riccati_special_minus2': RiccatiSpecial,
  2425. '1st_rational_riccati': RationalRiccati,
  2426. '1st_homogeneous_coeff_best': HomogeneousCoeffBest,
  2427. '1st_homogeneous_coeff_subs_indep_div_dep': HomogeneousCoeffSubsIndepDivDep,
  2428. '1st_homogeneous_coeff_subs_dep_div_indep': HomogeneousCoeffSubsDepDivIndep,
  2429. 'almost_linear': AlmostLinear,
  2430. 'linear_coefficients': LinearCoefficients,
  2431. 'separable_reduced': SeparableReduced,
  2432. 'nth_linear_constant_coeff_variation_of_parameters': NthLinearConstantCoeffVariationOfParameters,
  2433. 'nth_linear_euler_eq_nonhomogeneous_variation_of_parameters': NthLinearEulerEqNonhomogeneousVariationOfParameters,
  2434. 'Liouville': Liouville,
  2435. '2nd_linear_airy': SecondLinearAiry,
  2436. '2nd_linear_bessel': SecondLinearBessel,
  2437. '2nd_hypergeometric': SecondHypergeometric,
  2438. 'nth_order_reducible': NthOrderReducible,
  2439. '2nd_nonlinear_autonomous_conserved': SecondNonlinearAutonomousConserved,
  2440. 'nth_algebraic': NthAlgebraic,
  2441. 'lie_group': LieGroup,
  2442. }
  2443. # Avoid circular import:
  2444. from .ode import dsolve, ode_sol_simplicity, odesimp, homogeneous_order