single.py 107 KB

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