solveset.py 148 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131
  1. """
  2. This module contains functions to:
  3. - solve a single equation for a single variable, in any domain either real or complex.
  4. - solve a single transcendental equation for a single variable in any domain either real or complex.
  5. (currently supports solving in real domain only)
  6. - solve a system of linear equations with N variables and M equations.
  7. - solve a system of Non Linear Equations with N variables and M equations
  8. """
  9. from sympy.core.sympify import sympify
  10. from sympy.core import (S, Pow, Dummy, pi, Expr, Wild, Mul,
  11. Add, Basic)
  12. from sympy.core.containers import Tuple
  13. from sympy.core.function import (Lambda, expand_complex, AppliedUndef,
  14. expand_log, _mexpand, expand_trig, nfloat)
  15. from sympy.core.mod import Mod
  16. from sympy.core.numbers import I, Number, Rational, oo
  17. from sympy.core.intfunc import integer_log
  18. from sympy.core.relational import Eq, Ne, Relational
  19. from sympy.core.sorting import default_sort_key, ordered
  20. from sympy.core.symbol import Symbol, _uniquely_named_symbol
  21. from sympy.core.sympify import _sympify
  22. from sympy.core.traversal import preorder_traversal
  23. from sympy.external.gmpy import gcd as number_gcd, lcm as number_lcm
  24. from sympy.polys.matrices.linsolve import _linear_eq_to_dict
  25. from sympy.polys.polyroots import UnsolvableFactorError
  26. from sympy.simplify.simplify import simplify, fraction, trigsimp, nsimplify
  27. from sympy.simplify import powdenest, logcombine
  28. from sympy.functions import (log, tan, cot, sin, cos, sec, csc, exp,
  29. acos, asin, atan, acot, acsc, asec,
  30. piecewise_fold, Piecewise)
  31. from sympy.functions.combinatorial.numbers import totient
  32. from sympy.functions.elementary.complexes import Abs, arg, re, im
  33. from sympy.functions.elementary.hyperbolic import (HyperbolicFunction,
  34. sinh, cosh, tanh, coth, sech, csch,
  35. asinh, acosh, atanh, acoth, asech, acsch)
  36. from sympy.functions.elementary.miscellaneous import real_root
  37. from sympy.functions.elementary.trigonometric import TrigonometricFunction
  38. from sympy.logic.boolalg import And, BooleanTrue
  39. from sympy.sets import (FiniteSet, imageset, Interval, Intersection,
  40. Union, ConditionSet, ImageSet, Complement, Contains)
  41. from sympy.sets.sets import Set, ProductSet
  42. from sympy.matrices import zeros, Matrix, MatrixBase
  43. from sympy.ntheory.factor_ import divisors
  44. from sympy.ntheory.residue_ntheory import discrete_log, nthroot_mod
  45. from sympy.polys import (roots, Poly, degree, together, PolynomialError,
  46. RootOf, factor, lcm, gcd)
  47. from sympy.polys.polyerrors import CoercionFailed
  48. from sympy.polys.polytools import invert, groebner, poly
  49. from sympy.polys.solvers import (sympy_eqs_to_ring, solve_lin_sys,
  50. PolyNonlinearError)
  51. from sympy.polys.matrices.linsolve import _linsolve
  52. from sympy.solvers.solvers import (checksol, denoms, unrad,
  53. _simple_dens, recast_to_symbols)
  54. from sympy.solvers.polysys import solve_poly_system
  55. from sympy.utilities import filldedent
  56. from sympy.utilities.iterables import (numbered_symbols, has_dups,
  57. is_sequence, iterable)
  58. from sympy.calculus.util import periodicity, continuous_domain, function_range
  59. from types import GeneratorType
  60. class NonlinearError(ValueError):
  61. """Raised when unexpectedly encountering nonlinear equations"""
  62. pass
  63. def _masked(f, *atoms):
  64. """Return ``f``, with all objects given by ``atoms`` replaced with
  65. Dummy symbols, ``d``, and the list of replacements, ``(d, e)``,
  66. where ``e`` is an object of type given by ``atoms`` in which
  67. any other instances of atoms have been recursively replaced with
  68. Dummy symbols, too. The tuples are ordered so that if they are
  69. applied in sequence, the origin ``f`` will be restored.
  70. Examples
  71. ========
  72. >>> from sympy import cos
  73. >>> from sympy.abc import x
  74. >>> from sympy.solvers.solveset import _masked
  75. >>> f = cos(cos(x) + 1)
  76. >>> f, reps = _masked(cos(1 + cos(x)), cos)
  77. >>> f
  78. _a1
  79. >>> reps
  80. [(_a1, cos(_a0 + 1)), (_a0, cos(x))]
  81. >>> for d, e in reps:
  82. ... f = f.xreplace({d: e})
  83. >>> f
  84. cos(cos(x) + 1)
  85. """
  86. sym = numbered_symbols('a', cls=Dummy, real=True)
  87. mask = []
  88. for a in ordered(f.atoms(*atoms)):
  89. for i in mask:
  90. a = a.replace(*i)
  91. mask.append((a, next(sym)))
  92. for i, (o, n) in enumerate(mask):
  93. f = f.replace(o, n)
  94. mask[i] = (n, o)
  95. mask = list(reversed(mask))
  96. return f, mask
  97. def _invert(f_x, y, x, domain=S.Complexes):
  98. r"""
  99. Reduce the complex valued equation $f(x) = y$ to a set of equations
  100. $$\left\{g(x) = h_1(y),\ g(x) = h_2(y),\ \dots,\ g(x) = h_n(y) \right\}$$
  101. where $g(x)$ is a simpler function than $f(x)$. The return value is a tuple
  102. $(g(x), \mathrm{set}_h)$, where $g(x)$ is a function of $x$ and $\mathrm{set}_h$ is
  103. the set of function $\left\{h_1(y), h_2(y), \dots, h_n(y)\right\}$.
  104. Here, $y$ is not necessarily a symbol.
  105. $\mathrm{set}_h$ contains the functions, along with the information
  106. about the domain in which they are valid, through set
  107. operations. For instance, if :math:`y = |x| - n` is inverted
  108. in the real domain, then $\mathrm{set}_h$ is not simply
  109. $\{-n, n\}$ as the nature of `n` is unknown; rather, it is:
  110. $$ \left(\left[0, \infty\right) \cap \left\{n\right\}\right) \cup
  111. \left(\left(-\infty, 0\right] \cap \left\{- n\right\}\right)$$
  112. By default, the complex domain is used which means that inverting even
  113. seemingly simple functions like $\exp(x)$ will give very different
  114. results from those obtained in the real domain.
  115. (In the case of $\exp(x)$, the inversion via $\log$ is multi-valued
  116. in the complex domain, having infinitely many branches.)
  117. If you are working with real values only (or you are not sure which
  118. function to use) you should probably set the domain to
  119. ``S.Reals`` (or use ``invert_real`` which does that automatically).
  120. Examples
  121. ========
  122. >>> from sympy.solvers.solveset import invert_complex, invert_real
  123. >>> from sympy.abc import x, y
  124. >>> from sympy import exp
  125. When does exp(x) == y?
  126. >>> invert_complex(exp(x), y, x)
  127. (x, ImageSet(Lambda(_n, I*(2*_n*pi + arg(y)) + log(Abs(y))), Integers))
  128. >>> invert_real(exp(x), y, x)
  129. (x, Intersection({log(y)}, Reals))
  130. When does exp(x) == 1?
  131. >>> invert_complex(exp(x), 1, x)
  132. (x, ImageSet(Lambda(_n, 2*_n*I*pi), Integers))
  133. >>> invert_real(exp(x), 1, x)
  134. (x, {0})
  135. See Also
  136. ========
  137. invert_real, invert_complex
  138. """
  139. x = sympify(x)
  140. if not x.is_Symbol:
  141. raise ValueError("x must be a symbol")
  142. f_x = sympify(f_x)
  143. if x not in f_x.free_symbols:
  144. raise ValueError("Inverse of constant function doesn't exist")
  145. y = sympify(y)
  146. if x in y.free_symbols:
  147. raise ValueError("y should be independent of x ")
  148. if domain.is_subset(S.Reals):
  149. x1, s = _invert_real(f_x, FiniteSet(y), x)
  150. else:
  151. x1, s = _invert_complex(f_x, FiniteSet(y), x)
  152. # f couldn't be inverted completely; return unmodified.
  153. if x1 != x:
  154. return x1, s
  155. # Avoid adding gratuitous intersections with S.Complexes. Actual
  156. # conditions should be handled by the respective inverters.
  157. if domain is S.Complexes:
  158. return x1, s
  159. if isinstance(s, FiniteSet):
  160. return x1, s.intersect(domain)
  161. # "Fancier" solution sets like those obtained by inversion of trigonometric
  162. # functions already include general validity conditions (i.e. conditions on
  163. # the domain of the respective inverse functions), so we should avoid adding
  164. # blanket intersections with S.Reals. But subsets of R (or C) must still be
  165. # accounted for.
  166. if domain is S.Reals:
  167. return x1, s
  168. else:
  169. return x1, s.intersect(domain)
  170. invert_complex = _invert
  171. def invert_real(f_x, y, x):
  172. """
  173. Inverts a real-valued function. Same as :func:`invert_complex`, but sets
  174. the domain to ``S.Reals`` before inverting.
  175. """
  176. return _invert(f_x, y, x, S.Reals)
  177. def _invert_real(f, g_ys, symbol):
  178. """Helper function for _invert."""
  179. if f == symbol or g_ys is S.EmptySet:
  180. return (symbol, g_ys)
  181. n = Dummy('n', real=True)
  182. if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
  183. return _invert_real(f.exp,
  184. imageset(Lambda(n, log(n)), g_ys),
  185. symbol)
  186. if hasattr(f, 'inverse') and f.inverse() is not None and not isinstance(f, (
  187. TrigonometricFunction,
  188. HyperbolicFunction,
  189. )):
  190. if len(f.args) > 1:
  191. raise ValueError("Only functions with one argument are supported.")
  192. return _invert_real(f.args[0],
  193. imageset(Lambda(n, f.inverse()(n)), g_ys),
  194. symbol)
  195. if isinstance(f, Abs):
  196. return _invert_abs(f.args[0], g_ys, symbol)
  197. if f.is_Add:
  198. # f = g + h
  199. g, h = f.as_independent(symbol)
  200. if g is not S.Zero:
  201. return _invert_real(h, imageset(Lambda(n, n - g), g_ys), symbol)
  202. if f.is_Mul:
  203. # f = g*h
  204. g, h = f.as_independent(symbol)
  205. if g is not S.One:
  206. return _invert_real(h, imageset(Lambda(n, n/g), g_ys), symbol)
  207. if f.is_Pow:
  208. base, expo = f.args
  209. base_has_sym = base.has(symbol)
  210. expo_has_sym = expo.has(symbol)
  211. if not expo_has_sym:
  212. if expo.is_rational:
  213. num, den = expo.as_numer_denom()
  214. if den % 2 == 0 and num % 2 == 1 and den.is_zero is False:
  215. # Here we have f(x)**(num/den) = y
  216. # where den is nonzero and even and y is an element
  217. # of the set g_ys.
  218. # den is even, so we are only interested in the cases
  219. # where both f(x) and y are positive.
  220. # Restricting y to be positive (using the set g_ys_pos)
  221. # means that y**(den/num) is always positive.
  222. # Therefore it isn't necessary to also constrain f(x)
  223. # to be positive because we are only going to
  224. # find solutions of f(x) = y**(d/n)
  225. # where the rhs is already required to be positive.
  226. root = Lambda(n, real_root(n, expo))
  227. g_ys_pos = g_ys & Interval(0, oo)
  228. res = imageset(root, g_ys_pos)
  229. _inv, _set = _invert_real(base, res, symbol)
  230. return (_inv, _set)
  231. if den % 2 == 1:
  232. root = Lambda(n, real_root(n, expo))
  233. res = imageset(root, g_ys)
  234. if num % 2 == 0:
  235. neg_res = imageset(Lambda(n, -n), res)
  236. return _invert_real(base, res + neg_res, symbol)
  237. if num % 2 == 1:
  238. return _invert_real(base, res, symbol)
  239. elif expo.is_irrational:
  240. root = Lambda(n, real_root(n, expo))
  241. g_ys_pos = g_ys & Interval(0, oo)
  242. res = imageset(root, g_ys_pos)
  243. return _invert_real(base, res, symbol)
  244. else:
  245. # indeterminate exponent, e.g. Float or parity of
  246. # num, den of rational could not be determined
  247. pass # use default return
  248. if not base_has_sym:
  249. rhs = g_ys.args[0]
  250. if base.is_positive:
  251. return _invert_real(expo,
  252. imageset(Lambda(n, log(n, base, evaluate=False)), g_ys), symbol)
  253. elif base.is_negative:
  254. s, b = integer_log(rhs, base)
  255. if b:
  256. return _invert_real(expo, FiniteSet(s), symbol)
  257. else:
  258. return (expo, S.EmptySet)
  259. elif base.is_zero:
  260. one = Eq(rhs, 1)
  261. if one == S.true:
  262. # special case: 0**x - 1
  263. return _invert_real(expo, FiniteSet(0), symbol)
  264. elif one == S.false:
  265. return (expo, S.EmptySet)
  266. if isinstance(f, (TrigonometricFunction, HyperbolicFunction)):
  267. return _invert_trig_hyp_real(f, g_ys, symbol)
  268. return (f, g_ys)
  269. # Dictionaries of inverses will be cached after first use.
  270. _trig_inverses = None
  271. _hyp_inverses = None
  272. def _invert_trig_hyp_real(f, g_ys, symbol):
  273. """Helper function for inverting trigonometric and hyperbolic functions.
  274. This helper only handles inversion over the reals.
  275. For trigonometric functions only finite `g_ys` sets are implemented.
  276. For hyperbolic functions the set `g_ys` is checked against the domain of the
  277. respective inverse functions. Infinite `g_ys` sets are also supported.
  278. """
  279. if isinstance(f, HyperbolicFunction):
  280. n = Dummy('n', real=True)
  281. if isinstance(f, sinh):
  282. # asinh is defined over R.
  283. return _invert_real(f.args[0], imageset(n, asinh(n), g_ys), symbol)
  284. if isinstance(f, cosh):
  285. g_ys_dom = g_ys.intersect(Interval(1, oo))
  286. if isinstance(g_ys_dom, Intersection):
  287. # could not properly resolve domain check
  288. if isinstance(g_ys, FiniteSet):
  289. # If g_ys is a `FiniteSet`` it should be sufficient to just
  290. # let the calling `_invert_real()` add an intersection with
  291. # `S.Reals` (or a subset `domain`) to ensure that only valid
  292. # (real) solutions are returned.
  293. # This avoids adding "too many" Intersections or
  294. # ConditionSets in the returned set.
  295. g_ys_dom = g_ys
  296. else:
  297. return (f, g_ys)
  298. return _invert_real(f.args[0], Union(
  299. imageset(n, acosh(n), g_ys_dom),
  300. imageset(n, -acosh(n), g_ys_dom)), symbol)
  301. if isinstance(f, sech):
  302. g_ys_dom = g_ys.intersect(Interval.Lopen(0, 1))
  303. if isinstance(g_ys_dom, Intersection):
  304. if isinstance(g_ys, FiniteSet):
  305. g_ys_dom = g_ys
  306. else:
  307. return (f, g_ys)
  308. return _invert_real(f.args[0], Union(
  309. imageset(n, asech(n), g_ys_dom),
  310. imageset(n, -asech(n), g_ys_dom)), symbol)
  311. if isinstance(f, tanh):
  312. g_ys_dom = g_ys.intersect(Interval.open(-1, 1))
  313. if isinstance(g_ys_dom, Intersection):
  314. if isinstance(g_ys, FiniteSet):
  315. g_ys_dom = g_ys
  316. else:
  317. return (f, g_ys)
  318. return _invert_real(f.args[0],
  319. imageset(n, atanh(n), g_ys_dom), symbol)
  320. if isinstance(f, coth):
  321. g_ys_dom = g_ys - Interval(-1, 1)
  322. if isinstance(g_ys_dom, Complement):
  323. if isinstance(g_ys, FiniteSet):
  324. g_ys_dom = g_ys
  325. else:
  326. return (f, g_ys)
  327. return _invert_real(f.args[0],
  328. imageset(n, acoth(n), g_ys_dom), symbol)
  329. if isinstance(f, csch):
  330. g_ys_dom = g_ys - FiniteSet(0)
  331. if isinstance(g_ys_dom, Complement):
  332. if isinstance(g_ys, FiniteSet):
  333. g_ys_dom = g_ys
  334. else:
  335. return (f, g_ys)
  336. return _invert_real(f.args[0],
  337. imageset(n, acsch(n), g_ys_dom), symbol)
  338. elif isinstance(f, TrigonometricFunction) and isinstance(g_ys, FiniteSet):
  339. def _get_trig_inverses(func):
  340. global _trig_inverses
  341. if _trig_inverses is None:
  342. _trig_inverses = {
  343. sin : ((asin, lambda y: pi-asin(y)), 2*pi, Interval(-1, 1)),
  344. cos : ((acos, lambda y: -acos(y)), 2*pi, Interval(-1, 1)),
  345. tan : ((atan,), pi, S.Reals),
  346. cot : ((acot,), pi, S.Reals),
  347. sec : ((asec, lambda y: -asec(y)), 2*pi,
  348. Union(Interval(-oo, -1), Interval(1, oo))),
  349. csc : ((acsc, lambda y: pi-acsc(y)), 2*pi,
  350. Union(Interval(-oo, -1), Interval(1, oo)))}
  351. return _trig_inverses[func]
  352. invs, period, rng = _get_trig_inverses(f.func)
  353. n = Dummy('n', integer=True)
  354. def create_return_set(g):
  355. # returns ConditionSet that will be part of the final (x, set) tuple
  356. invsimg = Union(*[
  357. imageset(n, period*n + inv(g), S.Integers) for inv in invs])
  358. inv_f, inv_g_ys = _invert_real(f.args[0], invsimg, symbol)
  359. if inv_f == symbol: # inversion successful
  360. conds = rng.contains(g)
  361. return ConditionSet(symbol, conds, inv_g_ys)
  362. else:
  363. return ConditionSet(symbol, Eq(f, g), S.Reals)
  364. retset = Union(*[create_return_set(g) for g in g_ys])
  365. return (symbol, retset)
  366. else:
  367. return (f, g_ys)
  368. def _invert_trig_hyp_complex(f, g_ys, symbol):
  369. """Helper function for inverting trigonometric and hyperbolic functions.
  370. This helper only handles inversion over the complex numbers.
  371. Only finite `g_ys` sets are implemented.
  372. Handling of singularities is only implemented for hyperbolic equations.
  373. In case of a symbolic element g in g_ys a ConditionSet may be returned.
  374. """
  375. if isinstance(f, TrigonometricFunction) and isinstance(g_ys, FiniteSet):
  376. def inv(trig):
  377. if isinstance(trig, (sin, csc)):
  378. F = asin if isinstance(trig, sin) else acsc
  379. return (
  380. lambda a: 2*n*pi + F(a),
  381. lambda a: 2*n*pi + pi - F(a))
  382. if isinstance(trig, (cos, sec)):
  383. F = acos if isinstance(trig, cos) else asec
  384. return (
  385. lambda a: 2*n*pi + F(a),
  386. lambda a: 2*n*pi - F(a))
  387. if isinstance(trig, (tan, cot)):
  388. return (lambda a: n*pi + trig.inverse()(a),)
  389. n = Dummy('n', integer=True)
  390. invs = S.EmptySet
  391. for L in inv(f):
  392. invs += Union(*[imageset(Lambda(n, L(g)), S.Integers) for g in g_ys])
  393. return _invert_complex(f.args[0], invs, symbol)
  394. elif isinstance(f, HyperbolicFunction) and isinstance(g_ys, FiniteSet):
  395. # There are two main options regarding singularities / domain checking
  396. # for symbolic elements in g_ys:
  397. # 1. Add a "catch-all" intersection with S.Complexes.
  398. # 2. ConditionSets.
  399. # At present ConditionSets seem to work better and have the additional
  400. # benefit of representing the precise conditions that must be satisfied.
  401. # The conditions are also rather straightforward. (At most two isolated
  402. # points.)
  403. def _get_hyp_inverses(func):
  404. global _hyp_inverses
  405. if _hyp_inverses is None:
  406. _hyp_inverses = {
  407. sinh : ((asinh, lambda y: I*pi-asinh(y)), 2*I*pi, ()),
  408. cosh : ((acosh, lambda y: -acosh(y)), 2*I*pi, ()),
  409. tanh : ((atanh,), I*pi, (-1, 1)),
  410. coth : ((acoth,), I*pi, (-1, 1)),
  411. sech : ((asech, lambda y: -asech(y)), 2*I*pi, (0, )),
  412. csch : ((acsch, lambda y: I*pi-acsch(y)), 2*I*pi, (0, ))}
  413. return _hyp_inverses[func]
  414. # invs: iterable of main inverses, e.g. (acosh, -acosh).
  415. # excl: iterable of singularities to be checked for.
  416. invs, period, excl = _get_hyp_inverses(f.func)
  417. n = Dummy('n', integer=True)
  418. def create_return_set(g):
  419. # returns ConditionSet that will be part of the final (x, set) tuple
  420. invsimg = Union(*[
  421. imageset(n, period*n + inv(g), S.Integers) for inv in invs])
  422. inv_f, inv_g_ys = _invert_complex(f.args[0], invsimg, symbol)
  423. if inv_f == symbol: # inversion successful
  424. conds = And(*[Ne(g, e) for e in excl])
  425. return ConditionSet(symbol, conds, inv_g_ys)
  426. else:
  427. return ConditionSet(symbol, Eq(f, g), S.Complexes)
  428. retset = Union(*[create_return_set(g) for g in g_ys])
  429. return (symbol, retset)
  430. else:
  431. return (f, g_ys)
  432. def _invert_complex(f, g_ys, symbol):
  433. """Helper function for _invert."""
  434. if f == symbol or g_ys is S.EmptySet:
  435. return (symbol, g_ys)
  436. n = Dummy('n')
  437. if f.is_Add:
  438. # f = g + h
  439. g, h = f.as_independent(symbol)
  440. if g is not S.Zero:
  441. return _invert_complex(h, imageset(Lambda(n, n - g), g_ys), symbol)
  442. if f.is_Mul:
  443. # f = g*h
  444. g, h = f.as_independent(symbol)
  445. if g is not S.One:
  446. if g in {S.NegativeInfinity, S.ComplexInfinity, S.Infinity}:
  447. return (h, S.EmptySet)
  448. return _invert_complex(h, imageset(Lambda(n, n/g), g_ys), symbol)
  449. if f.is_Pow:
  450. base, expo = f.args
  451. # special case: g**r = 0
  452. # Could be improved like `_invert_real` to handle more general cases.
  453. if expo.is_Rational and g_ys == FiniteSet(0):
  454. if expo.is_positive:
  455. return _invert_complex(base, g_ys, symbol)
  456. if hasattr(f, 'inverse') and f.inverse() is not None and \
  457. not isinstance(f, TrigonometricFunction) and \
  458. not isinstance(f, HyperbolicFunction) and \
  459. not isinstance(f, exp):
  460. if len(f.args) > 1:
  461. raise ValueError("Only functions with one argument are supported.")
  462. return _invert_complex(f.args[0],
  463. imageset(Lambda(n, f.inverse()(n)), g_ys), symbol)
  464. if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1):
  465. if isinstance(g_ys, ImageSet):
  466. # can solve up to `(d*exp(exp(...(exp(a*x + b))...) + c)` format.
  467. # Further can be improved to `(d*exp(exp(...(exp(a*x**n + b*x**(n-1) + ... + f))...) + c)`.
  468. g_ys_expr = g_ys.lamda.expr
  469. g_ys_vars = g_ys.lamda.variables
  470. k = Dummy('k{}'.format(len(g_ys_vars)))
  471. g_ys_vars_1 = (k,) + g_ys_vars
  472. exp_invs = Union(*[imageset(Lambda((g_ys_vars_1,), (I*(2*k*pi + arg(g_ys_expr))
  473. + log(Abs(g_ys_expr)))), S.Integers**(len(g_ys_vars_1)))])
  474. return _invert_complex(f.exp, exp_invs, symbol)
  475. elif isinstance(g_ys, FiniteSet):
  476. exp_invs = Union(*[imageset(Lambda(n, I*(2*n*pi + arg(g_y)) +
  477. log(Abs(g_y))), S.Integers)
  478. for g_y in g_ys if g_y != 0])
  479. return _invert_complex(f.exp, exp_invs, symbol)
  480. if isinstance(f, (TrigonometricFunction, HyperbolicFunction)):
  481. return _invert_trig_hyp_complex(f, g_ys, symbol)
  482. return (f, g_ys)
  483. def _invert_abs(f, g_ys, symbol):
  484. """Helper function for inverting absolute value functions.
  485. Returns the complete result of inverting an absolute value
  486. function along with the conditions which must also be satisfied.
  487. If it is certain that all these conditions are met, a :class:`~.FiniteSet`
  488. of all possible solutions is returned. If any condition cannot be
  489. satisfied, an :class:`~.EmptySet` is returned. Otherwise, a
  490. :class:`~.ConditionSet` of the solutions, with all the required conditions
  491. specified, is returned.
  492. """
  493. if not g_ys.is_FiniteSet:
  494. # this could be used for FiniteSet, but the
  495. # results are more compact if they aren't, e.g.
  496. # ConditionSet(x, Contains(n, Interval(0, oo)), {-n, n}) vs
  497. # Union(Intersection(Interval(0, oo), {n}), Intersection(Interval(-oo, 0), {-n}))
  498. # for the solution of abs(x) - n
  499. pos = Intersection(g_ys, Interval(0, S.Infinity))
  500. parg = _invert_real(f, pos, symbol)
  501. narg = _invert_real(-f, pos, symbol)
  502. if parg[0] != narg[0]:
  503. raise NotImplementedError
  504. return parg[0], Union(narg[1], parg[1])
  505. # check conditions: all these must be true. If any are unknown
  506. # then return them as conditions which must be satisfied
  507. unknown = []
  508. for a in g_ys.args:
  509. ok = a.is_nonnegative if a.is_Number else a.is_positive
  510. if ok is None:
  511. unknown.append(a)
  512. elif not ok:
  513. return symbol, S.EmptySet
  514. if unknown:
  515. conditions = And(*[Contains(i, Interval(0, oo))
  516. for i in unknown])
  517. else:
  518. conditions = True
  519. n = Dummy('n', real=True)
  520. # this is slightly different than above: instead of solving
  521. # +/-f on positive values, here we solve for f on +/- g_ys
  522. g_x, values = _invert_real(f, Union(
  523. imageset(Lambda(n, n), g_ys),
  524. imageset(Lambda(n, -n), g_ys)), symbol)
  525. return g_x, ConditionSet(g_x, conditions, values)
  526. def domain_check(f, symbol, p):
  527. """Returns False if point p is infinite or any subexpression of f
  528. is infinite or becomes so after replacing symbol with p. If none of
  529. these conditions is met then True will be returned.
  530. Examples
  531. ========
  532. >>> from sympy import Mul, oo
  533. >>> from sympy.abc import x
  534. >>> from sympy.solvers.solveset import domain_check
  535. >>> g = 1/(1 + (1/(x + 1))**2)
  536. >>> domain_check(g, x, -1)
  537. False
  538. >>> domain_check(x**2, x, 0)
  539. True
  540. >>> domain_check(1/x, x, oo)
  541. False
  542. * The function relies on the assumption that the original form
  543. of the equation has not been changed by automatic simplification.
  544. >>> domain_check(x/x, x, 0) # x/x is automatically simplified to 1
  545. True
  546. * To deal with automatic evaluations use evaluate=False:
  547. >>> domain_check(Mul(x, 1/x, evaluate=False), x, 0)
  548. False
  549. """
  550. f, p = sympify(f), sympify(p)
  551. if p.is_infinite:
  552. return False
  553. return _domain_check(f, symbol, p)
  554. def _domain_check(f, symbol, p):
  555. # helper for domain check
  556. if f.is_Atom and f.is_finite:
  557. return True
  558. elif f.subs(symbol, p).is_infinite:
  559. return False
  560. elif isinstance(f, Piecewise):
  561. # Check the cases of the Piecewise in turn. There might be invalid
  562. # expressions in later cases that don't apply e.g.
  563. # solveset(Piecewise((0, Eq(x, 0)), (1/x, True)), x)
  564. for expr, cond in f.args:
  565. condsubs = cond.subs(symbol, p)
  566. if condsubs is S.false:
  567. continue
  568. elif condsubs is S.true:
  569. return _domain_check(expr, symbol, p)
  570. else:
  571. # We don't know which case of the Piecewise holds. On this
  572. # basis we cannot decide whether any solution is in or out of
  573. # the domain. Ideally this function would allow returning a
  574. # symbolic condition for the validity of the solution that
  575. # could be handled in the calling code. In the mean time we'll
  576. # give this particular solution the benefit of the doubt and
  577. # let it pass.
  578. return True
  579. else:
  580. # TODO : We should not blindly recurse through all args of arbitrary expressions like this
  581. return all(_domain_check(g, symbol, p)
  582. for g in f.args)
  583. def _is_finite_with_finite_vars(f, domain=S.Complexes):
  584. """
  585. Return True if the given expression is finite. For symbols that
  586. do not assign a value for `complex` and/or `real`, the domain will
  587. be used to assign a value; symbols that do not assign a value
  588. for `finite` will be made finite. All other assumptions are
  589. left unmodified.
  590. """
  591. def assumptions(s):
  592. A = s.assumptions0
  593. A.setdefault('finite', A.get('finite', True))
  594. if domain.is_subset(S.Reals):
  595. # if this gets set it will make complex=True, too
  596. A.setdefault('real', True)
  597. else:
  598. # don't change 'real' because being complex implies
  599. # nothing about being real
  600. A.setdefault('complex', True)
  601. return A
  602. reps = {s: Dummy(**assumptions(s)) for s in f.free_symbols}
  603. return f.xreplace(reps).is_finite
  604. def _is_function_class_equation(func_class, f, symbol):
  605. """ Tests whether the equation is an equation of the given function class.
  606. The given equation belongs to the given function class if it is
  607. comprised of functions of the function class which are multiplied by
  608. or added to expressions independent of the symbol. In addition, the
  609. arguments of all such functions must be linear in the symbol as well.
  610. Examples
  611. ========
  612. >>> from sympy.solvers.solveset import _is_function_class_equation
  613. >>> from sympy import tan, sin, tanh, sinh, exp
  614. >>> from sympy.abc import x
  615. >>> from sympy.functions.elementary.trigonometric import TrigonometricFunction
  616. >>> from sympy.functions.elementary.hyperbolic import HyperbolicFunction
  617. >>> _is_function_class_equation(TrigonometricFunction, exp(x) + tan(x), x)
  618. False
  619. >>> _is_function_class_equation(TrigonometricFunction, tan(x) + sin(x), x)
  620. True
  621. >>> _is_function_class_equation(TrigonometricFunction, tan(x**2), x)
  622. False
  623. >>> _is_function_class_equation(TrigonometricFunction, tan(x + 2), x)
  624. True
  625. >>> _is_function_class_equation(HyperbolicFunction, tanh(x) + sinh(x), x)
  626. True
  627. """
  628. if f.is_Mul or f.is_Add:
  629. return all(_is_function_class_equation(func_class, arg, symbol)
  630. for arg in f.args)
  631. if f.is_Pow:
  632. if not f.exp.has(symbol):
  633. return _is_function_class_equation(func_class, f.base, symbol)
  634. else:
  635. return False
  636. if not f.has(symbol):
  637. return True
  638. if isinstance(f, func_class):
  639. try:
  640. g = Poly(f.args[0], symbol)
  641. return g.degree() <= 1
  642. except PolynomialError:
  643. return False
  644. else:
  645. return False
  646. def _solve_as_rational(f, symbol, domain):
  647. """ solve rational functions"""
  648. f = together(_mexpand(f, recursive=True), deep=True)
  649. g, h = fraction(f)
  650. if not h.has(symbol):
  651. try:
  652. return _solve_as_poly(g, symbol, domain)
  653. except NotImplementedError:
  654. # The polynomial formed from g could end up having
  655. # coefficients in a ring over which finding roots
  656. # isn't implemented yet, e.g. ZZ[a] for some symbol a
  657. return ConditionSet(symbol, Eq(f, 0), domain)
  658. except CoercionFailed:
  659. # contained oo, zoo or nan
  660. return S.EmptySet
  661. else:
  662. valid_solns = _solveset(g, symbol, domain)
  663. invalid_solns = _solveset(h, symbol, domain)
  664. return valid_solns - invalid_solns
  665. class _SolveTrig1Error(Exception):
  666. """Raised when _solve_trig1 heuristics do not apply"""
  667. def _solve_trig(f, symbol, domain):
  668. """Function to call other helpers to solve trigonometric equations """
  669. # If f is composed of a single trig function (potentially appearing multiple
  670. # times) we should solve by either inverting directly or inverting after a
  671. # suitable change of variable.
  672. #
  673. # _solve_trig is currently only called by _solveset for trig/hyperbolic
  674. # functions of an argument linear in x. Inverting a symbolic argument should
  675. # include a guard against division by zero in order to have a result that is
  676. # consistent with similar processing done by _solve_trig1.
  677. # (Ideally _invert should add these conditions by itself.)
  678. trig_expr, count = None, 0
  679. for expr in preorder_traversal(f):
  680. if isinstance(expr, (TrigonometricFunction,
  681. HyperbolicFunction)) and expr.has(symbol):
  682. if not trig_expr:
  683. trig_expr, count = expr, 1
  684. elif expr == trig_expr:
  685. count += 1
  686. else:
  687. trig_expr, count = False, 0
  688. break
  689. if count == 1:
  690. # direct inversion
  691. x, sol = _invert(f, 0, symbol, domain)
  692. if x == symbol:
  693. cond = True
  694. if trig_expr.free_symbols - {symbol}:
  695. a, h = trig_expr.args[0].as_independent(symbol, as_Add=True)
  696. m, h = h.as_independent(symbol, as_Add=False)
  697. num, den = m.as_numer_denom()
  698. cond = Ne(num, 0) & Ne(den, 0)
  699. return ConditionSet(symbol, cond, sol)
  700. else:
  701. return ConditionSet(symbol, Eq(f, 0), domain)
  702. elif count:
  703. # solve by change of variable
  704. y = Dummy('y')
  705. f_cov = f.subs(trig_expr, y)
  706. sol_cov = solveset(f_cov, y, domain)
  707. if isinstance(sol_cov, FiniteSet):
  708. return Union(
  709. *[_solve_trig(trig_expr-s, symbol, domain) for s in sol_cov])
  710. sol = None
  711. try:
  712. # multiple trig/hyp functions; solve by rewriting to exp
  713. sol = _solve_trig1(f, symbol, domain)
  714. except _SolveTrig1Error:
  715. try:
  716. # multiple trig/hyp functions; solve by rewriting to tan(x/2)
  717. sol = _solve_trig2(f, symbol, domain)
  718. except ValueError:
  719. raise NotImplementedError(filldedent('''
  720. Solution to this kind of trigonometric equations
  721. is yet to be implemented'''))
  722. return sol
  723. def _solve_trig1(f, symbol, domain):
  724. """Primary solver for trigonometric and hyperbolic equations
  725. Returns either the solution set as a ConditionSet (auto-evaluated to a
  726. union of ImageSets if no variables besides 'symbol' are involved) or
  727. raises _SolveTrig1Error if f == 0 cannot be solved.
  728. Notes
  729. =====
  730. Algorithm:
  731. 1. Do a change of variable x -> mu*x in arguments to trigonometric and
  732. hyperbolic functions, in order to reduce them to small integers. (This
  733. step is crucial to keep the degrees of the polynomials of step 4 low.)
  734. 2. Rewrite trigonometric/hyperbolic functions as exponentials.
  735. 3. Proceed to a 2nd change of variable, replacing exp(I*x) or exp(x) by y.
  736. 4. Solve the resulting rational equation.
  737. 5. Use invert_complex or invert_real to return to the original variable.
  738. 6. If the coefficients of 'symbol' were symbolic in nature, add the
  739. necessary consistency conditions in a ConditionSet.
  740. """
  741. # Prepare change of variable
  742. x = Dummy('x')
  743. if _is_function_class_equation(HyperbolicFunction, f, symbol):
  744. cov = exp(x)
  745. inverter = invert_real if domain.is_subset(S.Reals) else invert_complex
  746. else:
  747. cov = exp(I*x)
  748. inverter = invert_complex
  749. f = trigsimp(f)
  750. f_original = f
  751. trig_functions = f.atoms(TrigonometricFunction, HyperbolicFunction)
  752. trig_arguments = [e.args[0] for e in trig_functions]
  753. # trigsimp may have reduced the equation to an expression
  754. # that is independent of 'symbol' (e.g. cos**2+sin**2)
  755. if not any(a.has(symbol) for a in trig_arguments):
  756. return solveset(f_original, symbol, domain)
  757. denominators = []
  758. numerators = []
  759. for ar in trig_arguments:
  760. try:
  761. poly_ar = Poly(ar, symbol)
  762. except PolynomialError:
  763. raise _SolveTrig1Error("trig argument is not a polynomial")
  764. if poly_ar.degree() > 1: # degree >1 still bad
  765. raise _SolveTrig1Error("degree of variable must not exceed one")
  766. if poly_ar.degree() == 0: # degree 0, don't care
  767. continue
  768. c = poly_ar.all_coeffs()[0] # got the coefficient of 'symbol'
  769. numerators.append(fraction(c)[0])
  770. denominators.append(fraction(c)[1])
  771. mu = lcm(denominators)/gcd(numerators)
  772. f = f.subs(symbol, mu*x)
  773. f = f.rewrite(exp)
  774. f = together(f)
  775. g, h = fraction(f)
  776. y = Dummy('y')
  777. g, h = g.expand(), h.expand()
  778. g, h = g.subs(cov, y), h.subs(cov, y)
  779. if g.has(x) or h.has(x):
  780. raise _SolveTrig1Error("change of variable not possible")
  781. solns = solveset_complex(g, y) - solveset_complex(h, y)
  782. if isinstance(solns, ConditionSet):
  783. raise _SolveTrig1Error("polynomial has ConditionSet solution")
  784. if isinstance(solns, FiniteSet):
  785. if any(isinstance(s, RootOf) for s in solns):
  786. raise _SolveTrig1Error("polynomial results in RootOf object")
  787. # revert the change of variable
  788. cov = cov.subs(x, symbol/mu)
  789. result = Union(*[inverter(cov, s, symbol)[1] for s in solns])
  790. # In case of symbolic coefficients, the solution set is only valid
  791. # if numerator and denominator of mu are non-zero.
  792. if mu.has(Symbol):
  793. syms = (mu).atoms(Symbol)
  794. munum, muden = fraction(mu)
  795. condnum = munum.as_independent(*syms, as_Add=False)[1]
  796. condden = muden.as_independent(*syms, as_Add=False)[1]
  797. cond = And(Ne(condnum, 0), Ne(condden, 0))
  798. else:
  799. cond = True
  800. # Actual conditions are returned as part of the ConditionSet. Adding an
  801. # intersection with C would only complicate some solution sets due to
  802. # current limitations of intersection code. (e.g. #19154)
  803. if domain is S.Complexes:
  804. # This is a slight abuse of ConditionSet. Ideally this should
  805. # be some kind of "PiecewiseSet". (See #19507 discussion)
  806. return ConditionSet(symbol, cond, result)
  807. else:
  808. return ConditionSet(symbol, cond, Intersection(result, domain))
  809. elif solns is S.EmptySet:
  810. return S.EmptySet
  811. else:
  812. raise _SolveTrig1Error("polynomial solutions must form FiniteSet")
  813. def _solve_trig2(f, symbol, domain):
  814. """Secondary helper to solve trigonometric equations,
  815. called when first helper fails """
  816. f = trigsimp(f)
  817. f_original = f
  818. trig_functions = f.atoms(sin, cos, tan, sec, cot, csc)
  819. trig_arguments = [e.args[0] for e in trig_functions]
  820. denominators = []
  821. numerators = []
  822. # todo: This solver can be extended to hyperbolics if the
  823. # analogous change of variable to tanh (instead of tan)
  824. # is used.
  825. if not trig_functions:
  826. return ConditionSet(symbol, Eq(f_original, 0), domain)
  827. # todo: The pre-processing below (extraction of numerators, denominators,
  828. # gcd, lcm, mu, etc.) should be updated to the enhanced version in
  829. # _solve_trig1. (See #19507)
  830. for ar in trig_arguments:
  831. try:
  832. poly_ar = Poly(ar, symbol)
  833. except PolynomialError:
  834. raise ValueError("give up, we cannot solve if this is not a polynomial in x")
  835. if poly_ar.degree() > 1: # degree >1 still bad
  836. raise ValueError("degree of variable inside polynomial should not exceed one")
  837. if poly_ar.degree() == 0: # degree 0, don't care
  838. continue
  839. c = poly_ar.all_coeffs()[0] # got the coefficient of 'symbol'
  840. try:
  841. numerators.append(Rational(c).p)
  842. denominators.append(Rational(c).q)
  843. except TypeError:
  844. return ConditionSet(symbol, Eq(f_original, 0), domain)
  845. x = Dummy('x')
  846. mu = Rational(2)*number_lcm(*denominators)/number_gcd(*numerators)
  847. f = f.subs(symbol, mu*x)
  848. f = f.rewrite(tan)
  849. f = expand_trig(f)
  850. f = together(f)
  851. g, h = fraction(f)
  852. y = Dummy('y')
  853. g, h = g.expand(), h.expand()
  854. g, h = g.subs(tan(x), y), h.subs(tan(x), y)
  855. if g.has(x) or h.has(x):
  856. return ConditionSet(symbol, Eq(f_original, 0), domain)
  857. solns = solveset(g, y, S.Reals) - solveset(h, y, S.Reals)
  858. if isinstance(solns, FiniteSet):
  859. result = Union(*[invert_real(tan(symbol/mu), s, symbol)[1]
  860. for s in solns])
  861. dsol = invert_real(tan(symbol/mu), oo, symbol)[1]
  862. if degree(h) > degree(g): # If degree(denom)>degree(num) then there
  863. result = Union(result, dsol) # would be another sol at Lim(denom-->oo)
  864. return Intersection(result, domain)
  865. elif solns is S.EmptySet:
  866. return S.EmptySet
  867. else:
  868. return ConditionSet(symbol, Eq(f_original, 0), S.Reals)
  869. def _solve_as_poly(f, symbol, domain=S.Complexes):
  870. """
  871. Solve the equation using polynomial techniques if it already is a
  872. polynomial equation or, with a change of variables, can be made so.
  873. """
  874. result = None
  875. if f.is_polynomial(symbol):
  876. solns = roots(f, symbol, cubics=True, quartics=True,
  877. quintics=True, domain='EX')
  878. num_roots = sum(solns.values())
  879. if degree(f, symbol) <= num_roots:
  880. result = FiniteSet(*solns.keys())
  881. else:
  882. poly = Poly(f, symbol)
  883. solns = poly.all_roots()
  884. if poly.degree() <= len(solns):
  885. result = FiniteSet(*solns)
  886. else:
  887. result = ConditionSet(symbol, Eq(f, 0), domain)
  888. else:
  889. poly = Poly(f)
  890. if poly is None:
  891. result = ConditionSet(symbol, Eq(f, 0), domain)
  892. gens = [g for g in poly.gens if g.has(symbol)]
  893. if len(gens) == 1:
  894. poly = Poly(poly, gens[0])
  895. gen = poly.gen
  896. deg = poly.degree()
  897. poly = Poly(poly.as_expr(), poly.gen, composite=True)
  898. poly_solns = FiniteSet(*roots(poly, cubics=True, quartics=True,
  899. quintics=True).keys())
  900. if len(poly_solns) < deg:
  901. result = ConditionSet(symbol, Eq(f, 0), domain)
  902. if gen != symbol:
  903. y = Dummy('y')
  904. inverter = invert_real if domain.is_subset(S.Reals) else invert_complex
  905. lhs, rhs_s = inverter(gen, y, symbol)
  906. if lhs == symbol:
  907. result = Union(*[rhs_s.subs(y, s) for s in poly_solns])
  908. if isinstance(result, FiniteSet) and isinstance(gen, Pow
  909. ) and gen.base.is_Rational:
  910. result = FiniteSet(*[expand_log(i) for i in result])
  911. else:
  912. result = ConditionSet(symbol, Eq(f, 0), domain)
  913. else:
  914. result = ConditionSet(symbol, Eq(f, 0), domain)
  915. if result is not None:
  916. if isinstance(result, FiniteSet):
  917. # this is to simplify solutions like -sqrt(-I) to sqrt(2)/2
  918. # - sqrt(2)*I/2. We are not expanding for solution with symbols
  919. # or undefined functions because that makes the solution more complicated.
  920. # For example, expand_complex(a) returns re(a) + I*im(a)
  921. if all(s.atoms(Symbol, AppliedUndef) == set() and not isinstance(s, RootOf)
  922. for s in result):
  923. s = Dummy('s')
  924. result = imageset(Lambda(s, expand_complex(s)), result)
  925. if isinstance(result, FiniteSet) and domain != S.Complexes:
  926. # Avoid adding gratuitous intersections with S.Complexes. Actual
  927. # conditions should be handled elsewhere.
  928. result = result.intersection(domain)
  929. return result
  930. else:
  931. return ConditionSet(symbol, Eq(f, 0), domain)
  932. def _solve_radical(f, unradf, symbol, solveset_solver):
  933. """ Helper function to solve equations with radicals """
  934. res = unradf
  935. eq, cov = res if res else (f, [])
  936. if not cov:
  937. result = solveset_solver(eq, symbol) - \
  938. Union(*[solveset_solver(g, symbol) for g in denoms(f, symbol)])
  939. else:
  940. y, yeq = cov
  941. if not solveset_solver(y - I, y):
  942. yreal = Dummy('yreal', real=True)
  943. yeq = yeq.xreplace({y: yreal})
  944. eq = eq.xreplace({y: yreal})
  945. y = yreal
  946. g_y_s = solveset_solver(yeq, symbol)
  947. f_y_sols = solveset_solver(eq, y)
  948. result = Union(*[imageset(Lambda(y, g_y), f_y_sols)
  949. for g_y in g_y_s])
  950. def check_finiteset(solutions):
  951. f_set = [] # solutions for FiniteSet
  952. c_set = [] # solutions for ConditionSet
  953. for s in solutions:
  954. if checksol(f, symbol, s):
  955. f_set.append(s)
  956. else:
  957. c_set.append(s)
  958. return FiniteSet(*f_set) + ConditionSet(symbol, Eq(f, 0), FiniteSet(*c_set))
  959. def check_set(solutions):
  960. if solutions is S.EmptySet:
  961. return solutions
  962. elif isinstance(solutions, ConditionSet):
  963. # XXX: Maybe the base set should be checked?
  964. return solutions
  965. elif isinstance(solutions, FiniteSet):
  966. return check_finiteset(solutions)
  967. elif isinstance(solutions, Complement):
  968. A, B = solutions.args
  969. return Complement(check_set(A), B)
  970. elif isinstance(solutions, Union):
  971. return Union(*[check_set(s) for s in solutions.args])
  972. else:
  973. # XXX: There should be more cases checked here. The cases above
  974. # are all those that come up in the test suite for now.
  975. return solutions
  976. solution_set = check_set(result)
  977. return solution_set
  978. def _solve_abs(f, symbol, domain):
  979. """ Helper function to solve equation involving absolute value function """
  980. if not domain.is_subset(S.Reals):
  981. raise ValueError(filldedent('''
  982. Absolute values cannot be inverted in the
  983. complex domain.'''))
  984. p, q, r = Wild('p'), Wild('q'), Wild('r')
  985. pattern_match = f.match(p*Abs(q) + r) or {}
  986. f_p, f_q, f_r = [pattern_match.get(i, S.Zero) for i in (p, q, r)]
  987. if not (f_p.is_zero or f_q.is_zero):
  988. domain = continuous_domain(f_q, symbol, domain)
  989. from .inequalities import solve_univariate_inequality
  990. q_pos_cond = solve_univariate_inequality(f_q >= 0, symbol,
  991. relational=False, domain=domain, continuous=True)
  992. q_neg_cond = q_pos_cond.complement(domain)
  993. sols_q_pos = solveset_real(f_p*f_q + f_r,
  994. symbol).intersect(q_pos_cond)
  995. sols_q_neg = solveset_real(f_p*(-f_q) + f_r,
  996. symbol).intersect(q_neg_cond)
  997. return Union(sols_q_pos, sols_q_neg)
  998. else:
  999. return ConditionSet(symbol, Eq(f, 0), domain)
  1000. def solve_decomposition(f, symbol, domain):
  1001. """
  1002. Function to solve equations via the principle of "Decomposition
  1003. and Rewriting".
  1004. Examples
  1005. ========
  1006. >>> from sympy import exp, sin, Symbol, pprint, S
  1007. >>> from sympy.solvers.solveset import solve_decomposition as sd
  1008. >>> x = Symbol('x')
  1009. >>> f1 = exp(2*x) - 3*exp(x) + 2
  1010. >>> sd(f1, x, S.Reals)
  1011. {0, log(2)}
  1012. >>> f2 = sin(x)**2 + 2*sin(x) + 1
  1013. >>> pprint(sd(f2, x, S.Reals), use_unicode=False)
  1014. 3*pi
  1015. {2*n*pi + ---- | n in Integers}
  1016. 2
  1017. >>> f3 = sin(x + 2)
  1018. >>> pprint(sd(f3, x, S.Reals), use_unicode=False)
  1019. {2*n*pi - 2 | n in Integers} U {2*n*pi - 2 + pi | n in Integers}
  1020. """
  1021. from sympy.solvers.decompogen import decompogen
  1022. # decompose the given function
  1023. g_s = decompogen(f, symbol)
  1024. # `y_s` represents the set of values for which the function `g` is to be
  1025. # solved.
  1026. # `solutions` represent the solutions of the equations `g = y_s` or
  1027. # `g = 0` depending on the type of `y_s`.
  1028. # As we are interested in solving the equation: f = 0
  1029. y_s = FiniteSet(0)
  1030. for g in g_s:
  1031. frange = function_range(g, symbol, domain)
  1032. y_s = Intersection(frange, y_s)
  1033. result = S.EmptySet
  1034. if isinstance(y_s, FiniteSet):
  1035. for y in y_s:
  1036. solutions = solveset(Eq(g, y), symbol, domain)
  1037. if not isinstance(solutions, ConditionSet):
  1038. result += solutions
  1039. else:
  1040. if isinstance(y_s, ImageSet):
  1041. iter_iset = (y_s,)
  1042. elif isinstance(y_s, Union):
  1043. iter_iset = y_s.args
  1044. elif y_s is S.EmptySet:
  1045. # y_s is not in the range of g in g_s, so no solution exists
  1046. #in the given domain
  1047. return S.EmptySet
  1048. for iset in iter_iset:
  1049. new_solutions = solveset(Eq(iset.lamda.expr, g), symbol, domain)
  1050. dummy_var = tuple(iset.lamda.expr.free_symbols)[0]
  1051. (base_set,) = iset.base_sets
  1052. if isinstance(new_solutions, FiniteSet):
  1053. new_exprs = new_solutions
  1054. elif isinstance(new_solutions, Intersection):
  1055. if isinstance(new_solutions.args[1], FiniteSet):
  1056. new_exprs = new_solutions.args[1]
  1057. for new_expr in new_exprs:
  1058. result += ImageSet(Lambda(dummy_var, new_expr), base_set)
  1059. if result is S.EmptySet:
  1060. return ConditionSet(symbol, Eq(f, 0), domain)
  1061. y_s = result
  1062. return y_s
  1063. def _solveset(f, symbol, domain, _check=False):
  1064. """Helper for solveset to return a result from an expression
  1065. that has already been sympify'ed and is known to contain the
  1066. given symbol."""
  1067. # _check controls whether the answer is checked or not
  1068. from sympy.simplify.simplify import signsimp
  1069. if isinstance(f, BooleanTrue):
  1070. return domain
  1071. orig_f = f
  1072. if f.is_Mul:
  1073. coeff, f = f.as_independent(symbol, as_Add=False)
  1074. if coeff in {S.ComplexInfinity, S.NegativeInfinity, S.Infinity}:
  1075. f = together(orig_f)
  1076. elif f.is_Add:
  1077. a, h = f.as_independent(symbol)
  1078. m, h = h.as_independent(symbol, as_Add=False)
  1079. if m not in {S.ComplexInfinity, S.Zero, S.Infinity,
  1080. S.NegativeInfinity}:
  1081. f = a/m + h # XXX condition `m != 0` should be added to soln
  1082. # assign the solvers to use
  1083. solver = lambda f, x, domain=domain: _solveset(f, x, domain)
  1084. inverter = lambda f, rhs, symbol: _invert(f, rhs, symbol, domain)
  1085. result = S.EmptySet
  1086. if f.expand().is_zero:
  1087. return domain
  1088. elif not f.has(symbol):
  1089. return S.EmptySet
  1090. elif f.is_Mul and all(_is_finite_with_finite_vars(m, domain)
  1091. for m in f.args):
  1092. # if f(x) and g(x) are both finite we can say that the solution of
  1093. # f(x)*g(x) == 0 is same as Union(f(x) == 0, g(x) == 0) is not true in
  1094. # general. g(x) can grow to infinitely large for the values where
  1095. # f(x) == 0. To be sure that we are not silently allowing any
  1096. # wrong solutions we are using this technique only if both f and g are
  1097. # finite for a finite input.
  1098. result = Union(*[solver(m, symbol) for m in f.args])
  1099. elif (_is_function_class_equation(TrigonometricFunction, f, symbol) or \
  1100. _is_function_class_equation(HyperbolicFunction, f, symbol)):
  1101. result = _solve_trig(f, symbol, domain)
  1102. elif isinstance(f, arg):
  1103. a = f.args[0]
  1104. result = Intersection(_solveset(re(a) > 0, symbol, domain),
  1105. _solveset(im(a), symbol, domain))
  1106. elif f.is_Piecewise:
  1107. expr_set_pairs = f.as_expr_set_pairs(domain)
  1108. for (expr, in_set) in expr_set_pairs:
  1109. if in_set.is_Relational:
  1110. in_set = in_set.as_set()
  1111. solns = solver(expr, symbol, in_set)
  1112. result += solns
  1113. elif isinstance(f, Eq):
  1114. result = solver(Add(f.lhs, -f.rhs, evaluate=False), symbol, domain)
  1115. elif f.is_Relational:
  1116. from .inequalities import solve_univariate_inequality
  1117. try:
  1118. result = solve_univariate_inequality(
  1119. f, symbol, domain=domain, relational=False)
  1120. except NotImplementedError:
  1121. result = ConditionSet(symbol, f, domain)
  1122. return result
  1123. elif _is_modular(f, symbol):
  1124. result = _solve_modular(f, symbol, domain)
  1125. else:
  1126. lhs, rhs_s = inverter(f, 0, symbol)
  1127. if lhs == symbol:
  1128. # do some very minimal simplification since
  1129. # repeated inversion may have left the result
  1130. # in a state that other solvers (e.g. poly)
  1131. # would have simplified; this is done here
  1132. # rather than in the inverter since here it
  1133. # is only done once whereas there it would
  1134. # be repeated for each step of the inversion
  1135. if isinstance(rhs_s, FiniteSet):
  1136. rhs_s = FiniteSet(*[Mul(*
  1137. signsimp(i).as_content_primitive())
  1138. for i in rhs_s])
  1139. result = rhs_s
  1140. elif isinstance(rhs_s, FiniteSet):
  1141. for equation in [lhs - rhs for rhs in rhs_s]:
  1142. if equation == f:
  1143. u = unrad(f, symbol)
  1144. if u:
  1145. result += _solve_radical(equation, u,
  1146. symbol,
  1147. solver)
  1148. elif equation.has(Abs):
  1149. result += _solve_abs(f, symbol, domain)
  1150. else:
  1151. result_rational = _solve_as_rational(equation, symbol, domain)
  1152. if not isinstance(result_rational, ConditionSet):
  1153. result += result_rational
  1154. else:
  1155. # may be a transcendental type equation
  1156. t_result = _transolve(equation, symbol, domain)
  1157. if isinstance(t_result, ConditionSet):
  1158. # might need factoring; this is expensive so we
  1159. # have delayed until now. To avoid recursion
  1160. # errors look for a non-trivial factoring into
  1161. # a product of symbol dependent terms; I think
  1162. # that something that factors as a Pow would
  1163. # have already been recognized by now.
  1164. factored = equation.factor()
  1165. if factored.is_Mul and equation != factored:
  1166. _, dep = factored.as_independent(symbol)
  1167. if not dep.is_Add:
  1168. # non-trivial factoring of equation
  1169. # but use form with constants
  1170. # in case they need special handling
  1171. t_results = []
  1172. for fac in Mul.make_args(factored):
  1173. if fac.has(symbol):
  1174. t_results.append(solver(fac, symbol))
  1175. t_result = Union(*t_results)
  1176. result += t_result
  1177. else:
  1178. result += solver(equation, symbol)
  1179. elif rhs_s is not S.EmptySet:
  1180. result = ConditionSet(symbol, Eq(f, 0), domain)
  1181. if isinstance(result, ConditionSet):
  1182. if isinstance(f, Expr):
  1183. num, den = f.as_numer_denom()
  1184. if den.has(symbol):
  1185. _result = _solveset(num, symbol, domain)
  1186. if not isinstance(_result, ConditionSet):
  1187. singularities = _solveset(den, symbol, domain)
  1188. result = _result - singularities
  1189. if _check:
  1190. if isinstance(result, ConditionSet):
  1191. # it wasn't solved or has enumerated all conditions
  1192. # -- leave it alone
  1193. return result
  1194. # whittle away all but the symbol-containing core
  1195. # to use this for testing
  1196. if isinstance(orig_f, Expr):
  1197. fx = orig_f.as_independent(symbol, as_Add=True)[1]
  1198. fx = fx.as_independent(symbol, as_Add=False)[1]
  1199. else:
  1200. fx = orig_f
  1201. if isinstance(result, FiniteSet):
  1202. # check the result for invalid solutions
  1203. result = FiniteSet(*[s for s in result
  1204. if isinstance(s, RootOf)
  1205. or domain_check(fx, symbol, s)])
  1206. return result
  1207. def _is_modular(f, symbol):
  1208. """
  1209. Helper function to check below mentioned types of modular equations.
  1210. ``A - Mod(B, C) = 0``
  1211. A -> This can or cannot be a function of symbol.
  1212. B -> This is surely a function of symbol.
  1213. C -> It is an integer.
  1214. Parameters
  1215. ==========
  1216. f : Expr
  1217. The equation to be checked.
  1218. symbol : Symbol
  1219. The concerned variable for which the equation is to be checked.
  1220. Examples
  1221. ========
  1222. >>> from sympy import symbols, exp, Mod
  1223. >>> from sympy.solvers.solveset import _is_modular as check
  1224. >>> x, y = symbols('x y')
  1225. >>> check(Mod(x, 3) - 1, x)
  1226. True
  1227. >>> check(Mod(x, 3) - 1, y)
  1228. False
  1229. >>> check(Mod(x, 3)**2 - 5, x)
  1230. False
  1231. >>> check(Mod(x, 3)**2 - y, x)
  1232. False
  1233. >>> check(exp(Mod(x, 3)) - 1, x)
  1234. False
  1235. >>> check(Mod(3, y) - 1, y)
  1236. False
  1237. """
  1238. if not f.has(Mod):
  1239. return False
  1240. # extract modterms from f.
  1241. modterms = list(f.atoms(Mod))
  1242. return (len(modterms) == 1 and # only one Mod should be present
  1243. modterms[0].args[0].has(symbol) and # B-> function of symbol
  1244. modterms[0].args[1].is_integer and # C-> to be an integer.
  1245. any(isinstance(term, Mod)
  1246. for term in list(_term_factors(f))) # free from other funcs
  1247. )
  1248. def _invert_modular(modterm, rhs, n, symbol):
  1249. """
  1250. Helper function to invert modular equation.
  1251. ``Mod(a, m) - rhs = 0``
  1252. Generally it is inverted as (a, ImageSet(Lambda(n, m*n + rhs), S.Integers)).
  1253. More simplified form will be returned if possible.
  1254. If it is not invertible then (modterm, rhs) is returned.
  1255. The following cases arise while inverting equation ``Mod(a, m) - rhs = 0``:
  1256. 1. If a is symbol then m*n + rhs is the required solution.
  1257. 2. If a is an instance of ``Add`` then we try to find two symbol independent
  1258. parts of a and the symbol independent part gets transferred to the other
  1259. side and again the ``_invert_modular`` is called on the symbol
  1260. dependent part.
  1261. 3. If a is an instance of ``Mul`` then same as we done in ``Add`` we separate
  1262. out the symbol dependent and symbol independent parts and transfer the
  1263. symbol independent part to the rhs with the help of invert and again the
  1264. ``_invert_modular`` is called on the symbol dependent part.
  1265. 4. If a is an instance of ``Pow`` then two cases arise as following:
  1266. - If a is of type (symbol_indep)**(symbol_dep) then the remainder is
  1267. evaluated with the help of discrete_log function and then the least
  1268. period is being found out with the help of totient function.
  1269. period*n + remainder is the required solution in this case.
  1270. For reference: (https://en.wikipedia.org/wiki/Euler's_theorem)
  1271. - If a is of type (symbol_dep)**(symbol_indep) then we try to find all
  1272. primitive solutions list with the help of nthroot_mod function.
  1273. m*n + rem is the general solution where rem belongs to solutions list
  1274. from nthroot_mod function.
  1275. Parameters
  1276. ==========
  1277. modterm, rhs : Expr
  1278. The modular equation to be inverted, ``modterm - rhs = 0``
  1279. symbol : Symbol
  1280. The variable in the equation to be inverted.
  1281. n : Dummy
  1282. Dummy variable for output g_n.
  1283. Returns
  1284. =======
  1285. A tuple (f_x, g_n) is being returned where f_x is modular independent function
  1286. of symbol and g_n being set of values f_x can have.
  1287. Examples
  1288. ========
  1289. >>> from sympy import symbols, exp, Mod, Dummy, S
  1290. >>> from sympy.solvers.solveset import _invert_modular as invert_modular
  1291. >>> x, y = symbols('x y')
  1292. >>> n = Dummy('n')
  1293. >>> invert_modular(Mod(exp(x), 7), S(5), n, x)
  1294. (Mod(exp(x), 7), 5)
  1295. >>> invert_modular(Mod(x, 7), S(5), n, x)
  1296. (x, ImageSet(Lambda(_n, 7*_n + 5), Integers))
  1297. >>> invert_modular(Mod(3*x + 8, 7), S(5), n, x)
  1298. (x, ImageSet(Lambda(_n, 7*_n + 6), Integers))
  1299. >>> invert_modular(Mod(x**4, 7), S(5), n, x)
  1300. (x, EmptySet)
  1301. >>> invert_modular(Mod(2**(x**2 + x + 1), 7), S(2), n, x)
  1302. (x**2 + x + 1, ImageSet(Lambda(_n, 3*_n + 1), Naturals0))
  1303. """
  1304. a, m = modterm.args
  1305. if rhs.is_integer is False:
  1306. return symbol, S.EmptySet
  1307. if rhs.is_real is False or any(term.is_real is False
  1308. for term in list(_term_factors(a))):
  1309. # Check for complex arguments
  1310. return modterm, rhs
  1311. if abs(rhs) >= abs(m):
  1312. # if rhs has value greater than value of m.
  1313. return symbol, S.EmptySet
  1314. if a == symbol:
  1315. return symbol, ImageSet(Lambda(n, m*n + rhs), S.Integers)
  1316. if a.is_Add:
  1317. # g + h = a
  1318. g, h = a.as_independent(symbol)
  1319. if g is not S.Zero:
  1320. x_indep_term = rhs - Mod(g, m)
  1321. return _invert_modular(Mod(h, m), Mod(x_indep_term, m), n, symbol)
  1322. if a.is_Mul:
  1323. # g*h = a
  1324. g, h = a.as_independent(symbol)
  1325. if g is not S.One:
  1326. x_indep_term = rhs*invert(g, m)
  1327. return _invert_modular(Mod(h, m), Mod(x_indep_term, m), n, symbol)
  1328. if a.is_Pow:
  1329. # base**expo = a
  1330. base, expo = a.args
  1331. if expo.has(symbol) and not base.has(symbol):
  1332. # remainder -> solution independent of n of equation.
  1333. # m, rhs are made coprime by dividing number_gcd(m, rhs)
  1334. if not m.is_Integer and rhs.is_Integer and a.base.is_Integer:
  1335. return modterm, rhs
  1336. mdiv = m.p // number_gcd(m.p, rhs.p)
  1337. try:
  1338. remainder = discrete_log(mdiv, rhs.p, a.base.p)
  1339. except ValueError: # log does not exist
  1340. return modterm, rhs
  1341. # period -> coefficient of n in the solution and also referred as
  1342. # the least period of expo in which it is repeats itself.
  1343. # (a**(totient(m)) - 1) divides m. Here is link of theorem:
  1344. # (https://en.wikipedia.org/wiki/Euler's_theorem)
  1345. period = totient(m)
  1346. for p in divisors(period):
  1347. # there might a lesser period exist than totient(m).
  1348. if pow(a.base, p, m / number_gcd(m.p, a.base.p)) == 1:
  1349. period = p
  1350. break
  1351. # recursion is not applied here since _invert_modular is currently
  1352. # not smart enough to handle infinite rhs as here expo has infinite
  1353. # rhs = ImageSet(Lambda(n, period*n + remainder), S.Naturals0).
  1354. return expo, ImageSet(Lambda(n, period*n + remainder), S.Naturals0)
  1355. elif base.has(symbol) and not expo.has(symbol):
  1356. try:
  1357. remainder_list = nthroot_mod(rhs, expo, m, all_roots=True)
  1358. if remainder_list == []:
  1359. return symbol, S.EmptySet
  1360. except (ValueError, NotImplementedError):
  1361. return modterm, rhs
  1362. g_n = S.EmptySet
  1363. for rem in remainder_list:
  1364. g_n += ImageSet(Lambda(n, m*n + rem), S.Integers)
  1365. return base, g_n
  1366. return modterm, rhs
  1367. def _solve_modular(f, symbol, domain):
  1368. r"""
  1369. Helper function for solving modular equations of type ``A - Mod(B, C) = 0``,
  1370. where A can or cannot be a function of symbol, B is surely a function of
  1371. symbol and C is an integer.
  1372. Currently ``_solve_modular`` is only able to solve cases
  1373. where A is not a function of symbol.
  1374. Parameters
  1375. ==========
  1376. f : Expr
  1377. The modular equation to be solved, ``f = 0``
  1378. symbol : Symbol
  1379. The variable in the equation to be solved.
  1380. domain : Set
  1381. A set over which the equation is solved. It has to be a subset of
  1382. Integers.
  1383. Returns
  1384. =======
  1385. A set of integer solutions satisfying the given modular equation.
  1386. A ``ConditionSet`` if the equation is unsolvable.
  1387. Examples
  1388. ========
  1389. >>> from sympy.solvers.solveset import _solve_modular as solve_modulo
  1390. >>> from sympy import S, Symbol, sin, Intersection, Interval, Mod
  1391. >>> x = Symbol('x')
  1392. >>> solve_modulo(Mod(5*x - 8, 7) - 3, x, S.Integers)
  1393. ImageSet(Lambda(_n, 7*_n + 5), Integers)
  1394. >>> solve_modulo(Mod(5*x - 8, 7) - 3, x, S.Reals) # domain should be subset of integers.
  1395. ConditionSet(x, Eq(Mod(5*x + 6, 7) - 3, 0), Reals)
  1396. >>> solve_modulo(-7 + Mod(x, 5), x, S.Integers)
  1397. EmptySet
  1398. >>> solve_modulo(Mod(12**x, 21) - 18, x, S.Integers)
  1399. ImageSet(Lambda(_n, 6*_n + 2), Naturals0)
  1400. >>> solve_modulo(Mod(sin(x), 7) - 3, x, S.Integers) # not solvable
  1401. ConditionSet(x, Eq(Mod(sin(x), 7) - 3, 0), Integers)
  1402. >>> solve_modulo(3 - Mod(x, 5), x, Intersection(S.Integers, Interval(0, 100)))
  1403. Intersection(ImageSet(Lambda(_n, 5*_n + 3), Integers), Range(0, 101, 1))
  1404. """
  1405. # extract modterm and g_y from f
  1406. unsolved_result = ConditionSet(symbol, Eq(f, 0), domain)
  1407. modterm = list(f.atoms(Mod))[0]
  1408. rhs = -S.One*(f.subs(modterm, S.Zero))
  1409. if f.as_coefficients_dict()[modterm].is_negative:
  1410. # checks if coefficient of modterm is negative in main equation.
  1411. rhs *= -S.One
  1412. if not domain.is_subset(S.Integers):
  1413. return unsolved_result
  1414. if rhs.has(symbol):
  1415. # TODO Case: A-> function of symbol, can be extended here
  1416. # in future.
  1417. return unsolved_result
  1418. n = Dummy('n', integer=True)
  1419. f_x, g_n = _invert_modular(modterm, rhs, n, symbol)
  1420. if f_x == modterm and g_n == rhs:
  1421. return unsolved_result
  1422. if f_x == symbol:
  1423. if domain is not S.Integers:
  1424. return domain.intersect(g_n)
  1425. return g_n
  1426. if isinstance(g_n, ImageSet):
  1427. lamda_expr = g_n.lamda.expr
  1428. lamda_vars = g_n.lamda.variables
  1429. base_sets = g_n.base_sets
  1430. sol_set = _solveset(f_x - lamda_expr, symbol, S.Integers)
  1431. if isinstance(sol_set, FiniteSet):
  1432. tmp_sol = S.EmptySet
  1433. for sol in sol_set:
  1434. tmp_sol += ImageSet(Lambda(lamda_vars, sol), *base_sets)
  1435. sol_set = tmp_sol
  1436. else:
  1437. sol_set = ImageSet(Lambda(lamda_vars, sol_set), *base_sets)
  1438. return domain.intersect(sol_set)
  1439. return unsolved_result
  1440. def _term_factors(f):
  1441. """
  1442. Iterator to get the factors of all terms present
  1443. in the given equation.
  1444. Parameters
  1445. ==========
  1446. f : Expr
  1447. Equation that needs to be addressed
  1448. Returns
  1449. =======
  1450. Factors of all terms present in the equation.
  1451. Examples
  1452. ========
  1453. >>> from sympy import symbols
  1454. >>> from sympy.solvers.solveset import _term_factors
  1455. >>> x = symbols('x')
  1456. >>> list(_term_factors(-2 - x**2 + x*(x + 1)))
  1457. [-2, -1, x**2, x, x + 1]
  1458. """
  1459. for add_arg in Add.make_args(f):
  1460. yield from Mul.make_args(add_arg)
  1461. def _solve_exponential(lhs, rhs, symbol, domain):
  1462. r"""
  1463. Helper function for solving (supported) exponential equations.
  1464. Exponential equations are the sum of (currently) at most
  1465. two terms with one or both of them having a power with a
  1466. symbol-dependent exponent.
  1467. For example
  1468. .. math:: 5^{2x + 3} - 5^{3x - 1}
  1469. .. math:: 4^{5 - 9x} - e^{2 - x}
  1470. Parameters
  1471. ==========
  1472. lhs, rhs : Expr
  1473. The exponential equation to be solved, `lhs = rhs`
  1474. symbol : Symbol
  1475. The variable in which the equation is solved
  1476. domain : Set
  1477. A set over which the equation is solved.
  1478. Returns
  1479. =======
  1480. A set of solutions satisfying the given equation.
  1481. A ``ConditionSet`` if the equation is unsolvable or
  1482. if the assumptions are not properly defined, in that case
  1483. a different style of ``ConditionSet`` is returned having the
  1484. solution(s) of the equation with the desired assumptions.
  1485. Examples
  1486. ========
  1487. >>> from sympy.solvers.solveset import _solve_exponential as solve_expo
  1488. >>> from sympy import symbols, S
  1489. >>> x = symbols('x', real=True)
  1490. >>> a, b = symbols('a b')
  1491. >>> solve_expo(2**x + 3**x - 5**x, 0, x, S.Reals) # not solvable
  1492. ConditionSet(x, Eq(2**x + 3**x - 5**x, 0), Reals)
  1493. >>> solve_expo(a**x - b**x, 0, x, S.Reals) # solvable but incorrect assumptions
  1494. ConditionSet(x, (a > 0) & (b > 0), {0})
  1495. >>> solve_expo(3**(2*x) - 2**(x + 3), 0, x, S.Reals)
  1496. {-3*log(2)/(-2*log(3) + log(2))}
  1497. >>> solve_expo(2**x - 4**x, 0, x, S.Reals)
  1498. {0}
  1499. * Proof of correctness of the method
  1500. The logarithm function is the inverse of the exponential function.
  1501. The defining relation between exponentiation and logarithm is:
  1502. .. math:: {\log_b x} = y \enspace if \enspace b^y = x
  1503. Therefore if we are given an equation with exponent terms, we can
  1504. convert every term to its corresponding logarithmic form. This is
  1505. achieved by taking logarithms and expanding the equation using
  1506. logarithmic identities so that it can easily be handled by ``solveset``.
  1507. For example:
  1508. .. math:: 3^{2x} = 2^{x + 3}
  1509. Taking log both sides will reduce the equation to
  1510. .. math:: (2x)\log(3) = (x + 3)\log(2)
  1511. This form can be easily handed by ``solveset``.
  1512. """
  1513. unsolved_result = ConditionSet(symbol, Eq(lhs - rhs, 0), domain)
  1514. newlhs = powdenest(lhs)
  1515. if lhs != newlhs:
  1516. # it may also be advantageous to factor the new expr
  1517. neweq = factor(newlhs - rhs)
  1518. if neweq != (lhs - rhs):
  1519. return _solveset(neweq, symbol, domain) # try again with _solveset
  1520. if not (isinstance(lhs, Add) and len(lhs.args) == 2):
  1521. # solving for the sum of more than two powers is possible
  1522. # but not yet implemented
  1523. return unsolved_result
  1524. if rhs != 0:
  1525. return unsolved_result
  1526. a, b = list(ordered(lhs.args))
  1527. a_term = a.as_independent(symbol)[1]
  1528. b_term = b.as_independent(symbol)[1]
  1529. a_base, a_exp = a_term.as_base_exp()
  1530. b_base, b_exp = b_term.as_base_exp()
  1531. if domain.is_subset(S.Reals):
  1532. conditions = And(
  1533. a_base > 0,
  1534. b_base > 0,
  1535. Eq(im(a_exp), 0),
  1536. Eq(im(b_exp), 0))
  1537. else:
  1538. conditions = And(
  1539. Ne(a_base, 0),
  1540. Ne(b_base, 0))
  1541. L, R = (expand_log(log(i), force=True) for i in (a, -b))
  1542. solutions = _solveset(L - R, symbol, domain)
  1543. return ConditionSet(symbol, conditions, solutions)
  1544. def _is_exponential(f, symbol):
  1545. r"""
  1546. Return ``True`` if one or more terms contain ``symbol`` only in
  1547. exponents, else ``False``.
  1548. Parameters
  1549. ==========
  1550. f : Expr
  1551. The equation to be checked
  1552. symbol : Symbol
  1553. The variable in which the equation is checked
  1554. Examples
  1555. ========
  1556. >>> from sympy import symbols, cos, exp
  1557. >>> from sympy.solvers.solveset import _is_exponential as check
  1558. >>> x, y = symbols('x y')
  1559. >>> check(y, y)
  1560. False
  1561. >>> check(x**y - 1, y)
  1562. True
  1563. >>> check(x**y*2**y - 1, y)
  1564. True
  1565. >>> check(exp(x + 3) + 3**x, x)
  1566. True
  1567. >>> check(cos(2**x), x)
  1568. False
  1569. * Philosophy behind the helper
  1570. The function extracts each term of the equation and checks if it is
  1571. of exponential form w.r.t ``symbol``.
  1572. """
  1573. rv = False
  1574. for expr_arg in _term_factors(f):
  1575. if symbol not in expr_arg.free_symbols:
  1576. continue
  1577. if (isinstance(expr_arg, Pow) and
  1578. symbol not in expr_arg.base.free_symbols or
  1579. isinstance(expr_arg, exp)):
  1580. rv = True # symbol in exponent
  1581. else:
  1582. return False # dependent on symbol in non-exponential way
  1583. return rv
  1584. def _solve_logarithm(lhs, rhs, symbol, domain):
  1585. r"""
  1586. Helper to solve logarithmic equations which are reducible
  1587. to a single instance of `\log`.
  1588. Logarithmic equations are (currently) the equations that contains
  1589. `\log` terms which can be reduced to a single `\log` term or
  1590. a constant using various logarithmic identities.
  1591. For example:
  1592. .. math:: \log(x) + \log(x - 4)
  1593. can be reduced to:
  1594. .. math:: \log(x(x - 4))
  1595. Parameters
  1596. ==========
  1597. lhs, rhs : Expr
  1598. The logarithmic equation to be solved, `lhs = rhs`
  1599. symbol : Symbol
  1600. The variable in which the equation is solved
  1601. domain : Set
  1602. A set over which the equation is solved.
  1603. Returns
  1604. =======
  1605. A set of solutions satisfying the given equation.
  1606. A ``ConditionSet`` if the equation is unsolvable.
  1607. Examples
  1608. ========
  1609. >>> from sympy import symbols, log, S
  1610. >>> from sympy.solvers.solveset import _solve_logarithm as solve_log
  1611. >>> x = symbols('x')
  1612. >>> f = log(x - 3) + log(x + 3)
  1613. >>> solve_log(f, 0, x, S.Reals)
  1614. {-sqrt(10), sqrt(10)}
  1615. * Proof of correctness
  1616. A logarithm is another way to write exponent and is defined by
  1617. .. math:: {\log_b x} = y \enspace if \enspace b^y = x
  1618. When one side of the equation contains a single logarithm, the
  1619. equation can be solved by rewriting the equation as an equivalent
  1620. exponential equation as defined above. But if one side contains
  1621. more than one logarithm, we need to use the properties of logarithm
  1622. to condense it into a single logarithm.
  1623. Take for example
  1624. .. math:: \log(2x) - 15 = 0
  1625. contains single logarithm, therefore we can directly rewrite it to
  1626. exponential form as
  1627. .. math:: x = \frac{e^{15}}{2}
  1628. But if the equation has more than one logarithm as
  1629. .. math:: \log(x - 3) + \log(x + 3) = 0
  1630. we use logarithmic identities to convert it into a reduced form
  1631. Using,
  1632. .. math:: \log(a) + \log(b) = \log(ab)
  1633. the equation becomes,
  1634. .. math:: \log((x - 3)(x + 3))
  1635. This equation contains one logarithm and can be solved by rewriting
  1636. to exponents.
  1637. """
  1638. new_lhs = logcombine(lhs, force=True)
  1639. new_f = new_lhs - rhs
  1640. return _solveset(new_f, symbol, domain)
  1641. def _is_logarithmic(f, symbol):
  1642. r"""
  1643. Return ``True`` if the equation is in the form
  1644. `a\log(f(x)) + b\log(g(x)) + ... + c` else ``False``.
  1645. Parameters
  1646. ==========
  1647. f : Expr
  1648. The equation to be checked
  1649. symbol : Symbol
  1650. The variable in which the equation is checked
  1651. Returns
  1652. =======
  1653. ``True`` if the equation is logarithmic otherwise ``False``.
  1654. Examples
  1655. ========
  1656. >>> from sympy import symbols, tan, log
  1657. >>> from sympy.solvers.solveset import _is_logarithmic as check
  1658. >>> x, y = symbols('x y')
  1659. >>> check(log(x + 2) - log(x + 3), x)
  1660. True
  1661. >>> check(tan(log(2*x)), x)
  1662. False
  1663. >>> check(x*log(x), x)
  1664. False
  1665. >>> check(x + log(x), x)
  1666. False
  1667. >>> check(y + log(x), x)
  1668. True
  1669. * Philosophy behind the helper
  1670. The function extracts each term and checks whether it is
  1671. logarithmic w.r.t ``symbol``.
  1672. """
  1673. rv = False
  1674. for term in Add.make_args(f):
  1675. saw_log = False
  1676. for term_arg in Mul.make_args(term):
  1677. if symbol not in term_arg.free_symbols:
  1678. continue
  1679. if isinstance(term_arg, log):
  1680. if saw_log:
  1681. return False # more than one log in term
  1682. saw_log = True
  1683. else:
  1684. return False # dependent on symbol in non-log way
  1685. if saw_log:
  1686. rv = True
  1687. return rv
  1688. def _is_lambert(f, symbol):
  1689. r"""
  1690. If this returns ``False`` then the Lambert solver (``_solve_lambert``) will not be called.
  1691. Explanation
  1692. ===========
  1693. Quick check for cases that the Lambert solver might be able to handle.
  1694. 1. Equations containing more than two operands and `symbol`s involving any of
  1695. `Pow`, `exp`, `HyperbolicFunction`,`TrigonometricFunction`, `log` terms.
  1696. 2. In `Pow`, `exp` the exponent should have `symbol` whereas for
  1697. `HyperbolicFunction`,`TrigonometricFunction`, `log` should contain `symbol`.
  1698. 3. For `HyperbolicFunction`,`TrigonometricFunction` the number of trigonometric functions in
  1699. equation should be less than number of symbols. (since `A*cos(x) + B*sin(x) - c`
  1700. is not the Lambert type).
  1701. Some forms of lambert equations are:
  1702. 1. X**X = C
  1703. 2. X*(B*log(X) + D)**A = C
  1704. 3. A*log(B*X + A) + d*X = C
  1705. 4. (B*X + A)*exp(d*X + g) = C
  1706. 5. g*exp(B*X + h) - B*X = C
  1707. 6. A*D**(E*X + g) - B*X = C
  1708. 7. A*cos(X) + B*sin(X) - D*X = C
  1709. 8. A*cosh(X) + B*sinh(X) - D*X = C
  1710. Where X is any variable,
  1711. A, B, C, D, E are any constants,
  1712. g, h are linear functions or log terms.
  1713. Parameters
  1714. ==========
  1715. f : Expr
  1716. The equation to be checked
  1717. symbol : Symbol
  1718. The variable in which the equation is checked
  1719. Returns
  1720. =======
  1721. If this returns ``False`` then the Lambert solver (``_solve_lambert``) will not be called.
  1722. Examples
  1723. ========
  1724. >>> from sympy.solvers.solveset import _is_lambert
  1725. >>> from sympy import symbols, cosh, sinh, log
  1726. >>> x = symbols('x')
  1727. >>> _is_lambert(3*log(x) - x*log(3), x)
  1728. True
  1729. >>> _is_lambert(log(log(x - 3)) + log(x-3), x)
  1730. True
  1731. >>> _is_lambert(cosh(x) - sinh(x), x)
  1732. False
  1733. >>> _is_lambert((x**2 - 2*x + 1).subs(x, (log(x) + 3*x)**2 - 1), x)
  1734. True
  1735. See Also
  1736. ========
  1737. _solve_lambert
  1738. """
  1739. term_factors = list(_term_factors(f.expand()))
  1740. # total number of symbols in equation
  1741. no_of_symbols = len([arg for arg in term_factors if arg.has(symbol)])
  1742. # total number of trigonometric terms in equation
  1743. no_of_trig = len([arg for arg in term_factors \
  1744. if arg.has(HyperbolicFunction, TrigonometricFunction)])
  1745. if f.is_Add and no_of_symbols >= 2:
  1746. # `log`, `HyperbolicFunction`, `TrigonometricFunction` should have symbols
  1747. # and no_of_trig < no_of_symbols
  1748. lambert_funcs = (log, HyperbolicFunction, TrigonometricFunction)
  1749. if any(isinstance(arg, lambert_funcs)\
  1750. for arg in term_factors if arg.has(symbol)):
  1751. if no_of_trig < no_of_symbols:
  1752. return True
  1753. # here, `Pow`, `exp` exponent should have symbols
  1754. elif any(isinstance(arg, (Pow, exp)) \
  1755. for arg in term_factors if (arg.as_base_exp()[1]).has(symbol)):
  1756. return True
  1757. return False
  1758. def _transolve(f, symbol, domain):
  1759. r"""
  1760. Function to solve transcendental equations. It is a helper to
  1761. ``solveset`` and should be used internally. ``_transolve``
  1762. currently supports the following class of equations:
  1763. - Exponential equations
  1764. - Logarithmic equations
  1765. Parameters
  1766. ==========
  1767. f : Any transcendental equation that needs to be solved.
  1768. This needs to be an expression, which is assumed
  1769. to be equal to ``0``.
  1770. symbol : The variable for which the equation is solved.
  1771. This needs to be of class ``Symbol``.
  1772. domain : A set over which the equation is solved.
  1773. This needs to be of class ``Set``.
  1774. Returns
  1775. =======
  1776. Set
  1777. A set of values for ``symbol`` for which ``f`` is equal to
  1778. zero. An ``EmptySet`` is returned if ``f`` does not have solutions
  1779. in respective domain. A ``ConditionSet`` is returned as unsolved
  1780. object if algorithms to evaluate complete solution are not
  1781. yet implemented.
  1782. How to use ``_transolve``
  1783. =========================
  1784. ``_transolve`` should not be used as an independent function, because
  1785. it assumes that the equation (``f``) and the ``symbol`` comes from
  1786. ``solveset`` and might have undergone a few modification(s).
  1787. To use ``_transolve`` as an independent function the equation (``f``)
  1788. and the ``symbol`` should be passed as they would have been by
  1789. ``solveset``.
  1790. Examples
  1791. ========
  1792. >>> from sympy.solvers.solveset import _transolve as transolve
  1793. >>> from sympy.solvers.solvers import _tsolve as tsolve
  1794. >>> from sympy import symbols, S, pprint
  1795. >>> x = symbols('x', real=True) # assumption added
  1796. >>> transolve(5**(x - 3) - 3**(2*x + 1), x, S.Reals)
  1797. {-(log(3) + 3*log(5))/(-log(5) + 2*log(3))}
  1798. How ``_transolve`` works
  1799. ========================
  1800. ``_transolve`` uses two types of helper functions to solve equations
  1801. of a particular class:
  1802. Identifying helpers: To determine whether a given equation
  1803. belongs to a certain class of equation or not. Returns either
  1804. ``True`` or ``False``.
  1805. Solving helpers: Once an equation is identified, a corresponding
  1806. helper either solves the equation or returns a form of the equation
  1807. that ``solveset`` might better be able to handle.
  1808. * Philosophy behind the module
  1809. The purpose of ``_transolve`` is to take equations which are not
  1810. already polynomial in their generator(s) and to either recast them
  1811. as such through a valid transformation or to solve them outright.
  1812. A pair of helper functions for each class of supported
  1813. transcendental functions are employed for this purpose. One
  1814. identifies the transcendental form of an equation and the other
  1815. either solves it or recasts it into a tractable form that can be
  1816. solved by ``solveset``.
  1817. For example, an equation in the form `ab^{f(x)} - cd^{g(x)} = 0`
  1818. can be transformed to
  1819. `\log(a) + f(x)\log(b) - \log(c) - g(x)\log(d) = 0`
  1820. (under certain assumptions) and this can be solved with ``solveset``
  1821. if `f(x)` and `g(x)` are in polynomial form.
  1822. How ``_transolve`` is better than ``_tsolve``
  1823. =============================================
  1824. 1) Better output
  1825. ``_transolve`` provides expressions in a more simplified form.
  1826. Consider a simple exponential equation
  1827. >>> f = 3**(2*x) - 2**(x + 3)
  1828. >>> pprint(transolve(f, x, S.Reals), use_unicode=False)
  1829. -3*log(2)
  1830. {------------------}
  1831. -2*log(3) + log(2)
  1832. >>> pprint(tsolve(f, x), use_unicode=False)
  1833. / 3 \
  1834. | --------|
  1835. | log(2/9)|
  1836. [-log\2 /]
  1837. 2) Extensible
  1838. The API of ``_transolve`` is designed such that it is easily
  1839. extensible, i.e. the code that solves a given class of
  1840. equations is encapsulated in a helper and not mixed in with
  1841. the code of ``_transolve`` itself.
  1842. 3) Modular
  1843. ``_transolve`` is designed to be modular i.e, for every class of
  1844. equation a separate helper for identification and solving is
  1845. implemented. This makes it easy to change or modify any of the
  1846. method implemented directly in the helpers without interfering
  1847. with the actual structure of the API.
  1848. 4) Faster Computation
  1849. Solving equation via ``_transolve`` is much faster as compared to
  1850. ``_tsolve``. In ``solve``, attempts are made computing every possibility
  1851. to get the solutions. This series of attempts makes solving a bit
  1852. slow. In ``_transolve``, computation begins only after a particular
  1853. type of equation is identified.
  1854. How to add new class of equations
  1855. =================================
  1856. Adding a new class of equation solver is a three-step procedure:
  1857. - Identify the type of the equations
  1858. Determine the type of the class of equations to which they belong:
  1859. it could be of ``Add``, ``Pow``, etc. types. Separate internal functions
  1860. are used for each type. Write identification and solving helpers
  1861. and use them from within the routine for the given type of equation
  1862. (after adding it, if necessary). Something like:
  1863. .. code-block:: python
  1864. def add_type(lhs, rhs, x):
  1865. ....
  1866. if _is_exponential(lhs, x):
  1867. new_eq = _solve_exponential(lhs, rhs, x)
  1868. ....
  1869. rhs, lhs = eq.as_independent(x)
  1870. if lhs.is_Add:
  1871. result = add_type(lhs, rhs, x)
  1872. - Define the identification helper.
  1873. - Define the solving helper.
  1874. Apart from this, a few other things needs to be taken care while
  1875. adding an equation solver:
  1876. - Naming conventions:
  1877. Name of the identification helper should be as
  1878. ``_is_class`` where class will be the name or abbreviation
  1879. of the class of equation. The solving helper will be named as
  1880. ``_solve_class``.
  1881. For example: for exponential equations it becomes
  1882. ``_is_exponential`` and ``_solve_expo``.
  1883. - The identifying helpers should take two input parameters,
  1884. the equation to be checked and the variable for which a solution
  1885. is being sought, while solving helpers would require an additional
  1886. domain parameter.
  1887. - Be sure to consider corner cases.
  1888. - Add tests for each helper.
  1889. - Add a docstring to your helper that describes the method
  1890. implemented.
  1891. The documentation of the helpers should identify:
  1892. - the purpose of the helper,
  1893. - the method used to identify and solve the equation,
  1894. - a proof of correctness
  1895. - the return values of the helpers
  1896. """
  1897. def add_type(lhs, rhs, symbol, domain):
  1898. """
  1899. Helper for ``_transolve`` to handle equations of
  1900. ``Add`` type, i.e. equations taking the form as
  1901. ``a*f(x) + b*g(x) + .... = c``.
  1902. For example: 4**x + 8**x = 0
  1903. """
  1904. result = ConditionSet(symbol, Eq(lhs - rhs, 0), domain)
  1905. # check if it is exponential type equation
  1906. if _is_exponential(lhs, symbol):
  1907. result = _solve_exponential(lhs, rhs, symbol, domain)
  1908. # check if it is logarithmic type equation
  1909. elif _is_logarithmic(lhs, symbol):
  1910. result = _solve_logarithm(lhs, rhs, symbol, domain)
  1911. return result
  1912. result = ConditionSet(symbol, Eq(f, 0), domain)
  1913. # invert_complex handles the call to the desired inverter based
  1914. # on the domain specified.
  1915. lhs, rhs_s = invert_complex(f, 0, symbol, domain)
  1916. if isinstance(rhs_s, FiniteSet):
  1917. assert (len(rhs_s.args)) == 1
  1918. rhs = rhs_s.args[0]
  1919. if lhs.is_Add:
  1920. result = add_type(lhs, rhs, symbol, domain)
  1921. else:
  1922. result = rhs_s
  1923. return result
  1924. def solveset(f, symbol=None, domain=S.Complexes):
  1925. r"""Solves a given inequality or equation with set as output
  1926. Parameters
  1927. ==========
  1928. f : Expr or a relational.
  1929. The target equation or inequality
  1930. symbol : Symbol
  1931. The variable for which the equation is solved
  1932. domain : Set
  1933. The domain over which the equation is solved
  1934. Returns
  1935. =======
  1936. Set
  1937. A set of values for `symbol` for which `f` is True or is equal to
  1938. zero. An :class:`~.EmptySet` is returned if `f` is False or nonzero.
  1939. A :class:`~.ConditionSet` is returned as unsolved object if algorithms
  1940. to evaluate complete solution are not yet implemented.
  1941. ``solveset`` claims to be complete in the solution set that it returns.
  1942. Raises
  1943. ======
  1944. NotImplementedError
  1945. The algorithms to solve inequalities in complex domain are
  1946. not yet implemented.
  1947. ValueError
  1948. The input is not valid.
  1949. RuntimeError
  1950. It is a bug, please report to the github issue tracker.
  1951. Notes
  1952. =====
  1953. Python interprets 0 and 1 as False and True, respectively, but
  1954. in this function they refer to solutions of an expression. So 0 and 1
  1955. return the domain and EmptySet, respectively, while True and False
  1956. return the opposite (as they are assumed to be solutions of relational
  1957. expressions).
  1958. See Also
  1959. ========
  1960. solveset_real: solver for real domain
  1961. solveset_complex: solver for complex domain
  1962. Examples
  1963. ========
  1964. >>> from sympy import exp, sin, Symbol, pprint, S, Eq
  1965. >>> from sympy.solvers.solveset import solveset, solveset_real
  1966. * The default domain is complex. Not specifying a domain will lead
  1967. to the solving of the equation in the complex domain (and this
  1968. is not affected by the assumptions on the symbol):
  1969. >>> x = Symbol('x')
  1970. >>> pprint(solveset(exp(x) - 1, x), use_unicode=False)
  1971. {2*n*I*pi | n in Integers}
  1972. >>> x = Symbol('x', real=True)
  1973. >>> pprint(solveset(exp(x) - 1, x), use_unicode=False)
  1974. {2*n*I*pi | n in Integers}
  1975. * If you want to use ``solveset`` to solve the equation in the
  1976. real domain, provide a real domain. (Using ``solveset_real``
  1977. does this automatically.)
  1978. >>> R = S.Reals
  1979. >>> x = Symbol('x')
  1980. >>> solveset(exp(x) - 1, x, R)
  1981. {0}
  1982. >>> solveset_real(exp(x) - 1, x)
  1983. {0}
  1984. The solution is unaffected by assumptions on the symbol:
  1985. >>> p = Symbol('p', positive=True)
  1986. >>> pprint(solveset(p**2 - 4))
  1987. {-2, 2}
  1988. When a :class:`~.ConditionSet` is returned, symbols with assumptions that
  1989. would alter the set are replaced with more generic symbols:
  1990. >>> i = Symbol('i', imaginary=True)
  1991. >>> solveset(Eq(i**2 + i*sin(i), 1), i, domain=S.Reals)
  1992. ConditionSet(_R, Eq(_R**2 + _R*sin(_R) - 1, 0), Reals)
  1993. * Inequalities can be solved over the real domain only. Use of a complex
  1994. domain leads to a NotImplementedError.
  1995. >>> solveset(exp(x) > 1, x, R)
  1996. Interval.open(0, oo)
  1997. """
  1998. f = sympify(f)
  1999. symbol = sympify(symbol)
  2000. if f is S.true:
  2001. return domain
  2002. if f is S.false:
  2003. return S.EmptySet
  2004. if not isinstance(f, (Expr, Relational, Number)):
  2005. raise ValueError("%s is not a valid SymPy expression" % f)
  2006. if not isinstance(symbol, (Expr, Relational)) and symbol is not None:
  2007. raise ValueError("%s is not a valid SymPy symbol" % (symbol,))
  2008. if not isinstance(domain, Set):
  2009. raise ValueError("%s is not a valid domain" %(domain))
  2010. free_symbols = f.free_symbols
  2011. if f.has(Piecewise):
  2012. f = piecewise_fold(f)
  2013. if symbol is None and not free_symbols:
  2014. b = Eq(f, 0)
  2015. if b is S.true:
  2016. return domain
  2017. elif b is S.false:
  2018. return S.EmptySet
  2019. else:
  2020. raise NotImplementedError(filldedent('''
  2021. relationship between value and 0 is unknown: %s''' % b))
  2022. if symbol is None:
  2023. if len(free_symbols) == 1:
  2024. symbol = free_symbols.pop()
  2025. elif free_symbols:
  2026. raise ValueError(filldedent('''
  2027. The independent variable must be specified for a
  2028. multivariate equation.'''))
  2029. elif not isinstance(symbol, Symbol):
  2030. f, s, swap = recast_to_symbols([f], [symbol])
  2031. # the xreplace will be needed if a ConditionSet is returned
  2032. return solveset(f[0], s[0], domain).xreplace(swap)
  2033. # solveset should ignore assumptions on symbols
  2034. newsym = None
  2035. if domain.is_subset(S.Reals):
  2036. if symbol._assumptions_orig != {'real': True}:
  2037. newsym = Dummy('R', real=True)
  2038. elif domain.is_subset(S.Complexes):
  2039. if symbol._assumptions_orig != {'complex': True}:
  2040. newsym = Dummy('C', complex=True)
  2041. if newsym is not None:
  2042. rv = solveset(f.xreplace({symbol: newsym}), newsym, domain)
  2043. # try to use the original symbol if possible
  2044. try:
  2045. _rv = rv.xreplace({newsym: symbol})
  2046. except TypeError:
  2047. _rv = rv
  2048. if rv.dummy_eq(_rv):
  2049. rv = _rv
  2050. return rv
  2051. # Abs has its own handling method which avoids the
  2052. # rewriting property that the first piece of abs(x)
  2053. # is for x >= 0 and the 2nd piece for x < 0 -- solutions
  2054. # can look better if the 2nd condition is x <= 0. Since
  2055. # the solution is a set, duplication of results is not
  2056. # an issue, e.g. {y, -y} when y is 0 will be {0}
  2057. f, mask = _masked(f, Abs)
  2058. f = f.rewrite(Piecewise) # everything that's not an Abs
  2059. for d, e in mask:
  2060. # everything *in* an Abs
  2061. e = e.func(e.args[0].rewrite(Piecewise))
  2062. f = f.xreplace({d: e})
  2063. f = piecewise_fold(f)
  2064. return _solveset(f, symbol, domain, _check=True)
  2065. def solveset_real(f, symbol):
  2066. return solveset(f, symbol, S.Reals)
  2067. def solveset_complex(f, symbol):
  2068. return solveset(f, symbol, S.Complexes)
  2069. def _solveset_multi(eqs, syms, domains):
  2070. '''Basic implementation of a multivariate solveset.
  2071. For internal use (not ready for public consumption)'''
  2072. rep = {}
  2073. for sym, dom in zip(syms, domains):
  2074. if dom is S.Reals:
  2075. rep[sym] = Symbol(sym.name, real=True)
  2076. eqs = [eq.subs(rep) for eq in eqs]
  2077. syms = [sym.subs(rep) for sym in syms]
  2078. syms = tuple(syms)
  2079. if len(eqs) == 0:
  2080. return ProductSet(*domains)
  2081. if len(syms) == 1:
  2082. sym = syms[0]
  2083. domain = domains[0]
  2084. solsets = [solveset(eq, sym, domain) for eq in eqs]
  2085. solset = Intersection(*solsets)
  2086. return ImageSet(Lambda((sym,), (sym,)), solset).doit()
  2087. eqs = sorted(eqs, key=lambda eq: len(eq.free_symbols & set(syms)))
  2088. for n, eq in enumerate(eqs):
  2089. sols = []
  2090. all_handled = True
  2091. for sym in syms:
  2092. if sym not in eq.free_symbols:
  2093. continue
  2094. sol = solveset(eq, sym, domains[syms.index(sym)])
  2095. if isinstance(sol, FiniteSet):
  2096. i = syms.index(sym)
  2097. symsp = syms[:i] + syms[i+1:]
  2098. domainsp = domains[:i] + domains[i+1:]
  2099. eqsp = eqs[:n] + eqs[n+1:]
  2100. for s in sol:
  2101. eqsp_sub = [eq.subs(sym, s) for eq in eqsp]
  2102. sol_others = _solveset_multi(eqsp_sub, symsp, domainsp)
  2103. fun = Lambda((symsp,), symsp[:i] + (s,) + symsp[i:])
  2104. sols.append(ImageSet(fun, sol_others).doit())
  2105. else:
  2106. all_handled = False
  2107. if all_handled:
  2108. return Union(*sols)
  2109. def solvify(f, symbol, domain):
  2110. """Solves an equation using solveset and returns the solution in accordance
  2111. with the `solve` output API.
  2112. Returns
  2113. =======
  2114. We classify the output based on the type of solution returned by `solveset`.
  2115. Solution | Output
  2116. ----------------------------------------
  2117. FiniteSet | list
  2118. ImageSet, | list (if `f` is periodic)
  2119. Union |
  2120. Union | list (with FiniteSet)
  2121. EmptySet | empty list
  2122. Others | None
  2123. Raises
  2124. ======
  2125. NotImplementedError
  2126. A ConditionSet is the input.
  2127. Examples
  2128. ========
  2129. >>> from sympy.solvers.solveset import solvify
  2130. >>> from sympy.abc import x
  2131. >>> from sympy import S, tan, sin, exp
  2132. >>> solvify(x**2 - 9, x, S.Reals)
  2133. [-3, 3]
  2134. >>> solvify(sin(x) - 1, x, S.Reals)
  2135. [pi/2]
  2136. >>> solvify(tan(x), x, S.Reals)
  2137. [0]
  2138. >>> solvify(exp(x) - 1, x, S.Complexes)
  2139. >>> solvify(exp(x) - 1, x, S.Reals)
  2140. [0]
  2141. """
  2142. solution_set = solveset(f, symbol, domain)
  2143. result = None
  2144. if solution_set is S.EmptySet:
  2145. result = []
  2146. elif isinstance(solution_set, ConditionSet):
  2147. raise NotImplementedError('solveset is unable to solve this equation.')
  2148. elif isinstance(solution_set, FiniteSet):
  2149. result = list(solution_set)
  2150. else:
  2151. period = periodicity(f, symbol)
  2152. if period is not None:
  2153. solutions = S.EmptySet
  2154. iter_solutions = ()
  2155. if isinstance(solution_set, ImageSet):
  2156. iter_solutions = (solution_set,)
  2157. elif isinstance(solution_set, Union):
  2158. if all(isinstance(i, ImageSet) for i in solution_set.args):
  2159. iter_solutions = solution_set.args
  2160. for solution in iter_solutions:
  2161. solutions += solution.intersect(Interval(0, period, False, True))
  2162. if isinstance(solutions, FiniteSet):
  2163. result = list(solutions)
  2164. else:
  2165. solution = solution_set.intersect(domain)
  2166. if isinstance(solution, Union):
  2167. # concerned about only FiniteSet with Union but not about ImageSet
  2168. # if required could be extend
  2169. if any(isinstance(i, FiniteSet) for i in solution.args):
  2170. result = [sol for soln in solution.args \
  2171. for sol in soln.args if isinstance(soln,FiniteSet)]
  2172. else:
  2173. return None
  2174. elif isinstance(solution, FiniteSet):
  2175. result += solution
  2176. return result
  2177. ###############################################################################
  2178. ################################ LINSOLVE #####################################
  2179. ###############################################################################
  2180. def linear_coeffs(eq, *syms, dict=False):
  2181. """Return a list whose elements are the coefficients of the
  2182. corresponding symbols in the sum of terms in ``eq``.
  2183. The additive constant is returned as the last element of the
  2184. list.
  2185. Raises
  2186. ======
  2187. NonlinearError
  2188. The equation contains a nonlinear term
  2189. ValueError
  2190. duplicate or unordered symbols are passed
  2191. Parameters
  2192. ==========
  2193. dict - (default False) when True, return coefficients as a
  2194. dictionary with coefficients keyed to syms that were present;
  2195. key 1 gives the constant term
  2196. Examples
  2197. ========
  2198. >>> from sympy.solvers.solveset import linear_coeffs
  2199. >>> from sympy.abc import x, y, z
  2200. >>> linear_coeffs(3*x + 2*y - 1, x, y)
  2201. [3, 2, -1]
  2202. It is not necessary to expand the expression:
  2203. >>> linear_coeffs(x + y*(z*(x*3 + 2) + 3), x)
  2204. [3*y*z + 1, y*(2*z + 3)]
  2205. When nonlinear is detected, an error will be raised:
  2206. * even if they would cancel after expansion (so the
  2207. situation does not pass silently past the caller's
  2208. attention)
  2209. >>> eq = 1/x*(x - 1) + 1/x
  2210. >>> linear_coeffs(eq.expand(), x)
  2211. [0, 1]
  2212. >>> linear_coeffs(eq, x)
  2213. Traceback (most recent call last):
  2214. ...
  2215. NonlinearError:
  2216. nonlinear in given generators
  2217. * when there are cross terms
  2218. >>> linear_coeffs(x*(y + 1), x, y)
  2219. Traceback (most recent call last):
  2220. ...
  2221. NonlinearError:
  2222. symbol-dependent cross-terms encountered
  2223. * when there are terms that contain an expression
  2224. dependent on the symbols that is not linear
  2225. >>> linear_coeffs(x**2, x)
  2226. Traceback (most recent call last):
  2227. ...
  2228. NonlinearError:
  2229. nonlinear in given generators
  2230. """
  2231. eq = _sympify(eq)
  2232. if len(syms) == 1 and iterable(syms[0]) and not isinstance(syms[0], Basic):
  2233. raise ValueError('expecting unpacked symbols, *syms')
  2234. symset = set(syms)
  2235. if len(symset) != len(syms):
  2236. raise ValueError('duplicate symbols given')
  2237. try:
  2238. d, c = _linear_eq_to_dict([eq], symset)
  2239. d = d[0]
  2240. c = c[0]
  2241. except PolyNonlinearError as err:
  2242. raise NonlinearError(str(err))
  2243. if dict:
  2244. if c:
  2245. d[S.One] = c
  2246. return d
  2247. rv = [S.Zero]*(len(syms) + 1)
  2248. rv[-1] = c
  2249. for i, k in enumerate(syms):
  2250. if k not in d:
  2251. continue
  2252. rv[i] = d[k]
  2253. return rv
  2254. def linear_eq_to_matrix(equations, *symbols):
  2255. r"""
  2256. Converts a given System of Equations into Matrix form. Here ``equations``
  2257. must be a linear system of equations in ``symbols``. Element ``M[i, j]``
  2258. corresponds to the coefficient of the jth symbol in the ith equation.
  2259. The Matrix form corresponds to the augmented matrix form. For example:
  2260. .. math::
  2261. 4x + 2y + 3z & = 1 \\
  2262. 3x + y + z & = -6 \\
  2263. 2x + 4y + 9z & = 2
  2264. This system will return :math:`A` and :math:`b` as:
  2265. .. math::
  2266. A = \left[\begin{array}{ccc}
  2267. 4 & 2 & 3 \\
  2268. 3 & 1 & 1 \\
  2269. 2 & 4 & 9
  2270. \end{array}\right] \\
  2271. .. math::
  2272. b = \left[\begin{array}{c}
  2273. 1 \\ -6 \\ 2
  2274. \end{array}\right]
  2275. The only simplification performed is to convert
  2276. ``Eq(a, b)`` :math:`\Rightarrow a - b`.
  2277. Raises
  2278. ======
  2279. NonlinearError
  2280. The equations contain a nonlinear term.
  2281. ValueError
  2282. The symbols are not given or are not unique.
  2283. Examples
  2284. ========
  2285. >>> from sympy import linear_eq_to_matrix, symbols
  2286. >>> c, x, y, z = symbols('c, x, y, z')
  2287. The coefficients (numerical or symbolic) of the symbols will
  2288. be returned as matrices:
  2289. >>> eqns = [c*x + z - 1 - c, y + z, x - y]
  2290. >>> A, b = linear_eq_to_matrix(eqns, [x, y, z])
  2291. >>> A
  2292. Matrix([
  2293. [c, 0, 1],
  2294. [0, 1, 1],
  2295. [1, -1, 0]])
  2296. >>> b
  2297. Matrix([
  2298. [c + 1],
  2299. [ 0],
  2300. [ 0]])
  2301. This routine does not simplify expressions and will raise an error
  2302. if nonlinearity is encountered:
  2303. >>> eqns = [
  2304. ... (x**2 - 3*x)/(x - 3) - 3,
  2305. ... y**2 - 3*y - y*(y - 4) + x - 4]
  2306. >>> linear_eq_to_matrix(eqns, [x, y])
  2307. Traceback (most recent call last):
  2308. ...
  2309. NonlinearError:
  2310. symbol-dependent term can be ignored using `strict=False`
  2311. Simplifying these equations will discard the removable singularity in the
  2312. first and reveal the linear structure of the second:
  2313. >>> [e.simplify() for e in eqns]
  2314. [x - 3, x + y - 4]
  2315. Any such simplification needed to eliminate nonlinear terms must be done
  2316. *before* calling this routine.
  2317. """
  2318. if not symbols:
  2319. raise ValueError(filldedent('''
  2320. Symbols must be given, for which coefficients
  2321. are to be found.
  2322. '''))
  2323. # Check if 'symbols' is a set and raise an error if it is
  2324. if isinstance(symbols[0], set):
  2325. raise TypeError(
  2326. "Unordered 'set' type is not supported as input for symbols.")
  2327. if hasattr(symbols[0], '__iter__'):
  2328. symbols = symbols[0]
  2329. if has_dups(symbols):
  2330. raise ValueError('Symbols must be unique')
  2331. equations = sympify(equations)
  2332. if isinstance(equations, MatrixBase):
  2333. equations = list(equations)
  2334. elif isinstance(equations, (Expr, Eq)):
  2335. equations = [equations]
  2336. elif not is_sequence(equations):
  2337. raise ValueError(filldedent('''
  2338. Equation(s) must be given as a sequence, Expr,
  2339. Eq or Matrix.
  2340. '''))
  2341. # construct the dictionaries
  2342. try:
  2343. eq, c = _linear_eq_to_dict(equations, symbols)
  2344. except PolyNonlinearError as err:
  2345. raise NonlinearError(str(err))
  2346. # prepare output matrices
  2347. n, m = shape = len(eq), len(symbols)
  2348. ix = dict(zip(symbols, range(m)))
  2349. A = zeros(*shape)
  2350. for row, d in enumerate(eq):
  2351. for k in d:
  2352. col = ix[k]
  2353. A[row, col] = d[k]
  2354. b = Matrix(n, 1, [-i for i in c])
  2355. return A, b
  2356. def linsolve(system, *symbols):
  2357. r"""
  2358. Solve system of $N$ linear equations with $M$ variables; both
  2359. underdetermined and overdetermined systems are supported.
  2360. The possible number of solutions is zero, one or infinite.
  2361. Zero solutions throws a ValueError, whereas infinite
  2362. solutions are represented parametrically in terms of the given
  2363. symbols. For unique solution a :class:`~.FiniteSet` of ordered tuples
  2364. is returned.
  2365. All standard input formats are supported:
  2366. For the given set of equations, the respective input types
  2367. are given below:
  2368. .. math:: 3x + 2y - z = 1
  2369. .. math:: 2x - 2y + 4z = -2
  2370. .. math:: 2x - y + 2z = 0
  2371. * Augmented matrix form, ``system`` given below:
  2372. $$ \text{system} = \left[{array}{cccc}
  2373. 3 & 2 & -1 & 1\\
  2374. 2 & -2 & 4 & -2\\
  2375. 2 & -1 & 2 & 0
  2376. \end{array}\right] $$
  2377. ::
  2378. system = Matrix([[3, 2, -1, 1], [2, -2, 4, -2], [2, -1, 2, 0]])
  2379. * List of equations form
  2380. ::
  2381. system = [3x + 2y - z - 1, 2x - 2y + 4z + 2, 2x - y + 2z]
  2382. * Input $A$ and $b$ in matrix form (from $Ax = b$) are given as:
  2383. $$ A = \left[\begin{array}{ccc}
  2384. 3 & 2 & -1 \\
  2385. 2 & -2 & 4 \\
  2386. 2 & -1 & 2
  2387. \end{array}\right] \ \ b = \left[\begin{array}{c}
  2388. 1 \\ -2 \\ 0
  2389. \end{array}\right] $$
  2390. ::
  2391. A = Matrix([[3, 2, -1], [2, -2, 4], [2, -1, 2]])
  2392. b = Matrix([[1], [-2], [0]])
  2393. system = (A, b)
  2394. Symbols can always be passed but are actually only needed
  2395. when 1) a system of equations is being passed and 2) the
  2396. system is passed as an underdetermined matrix and one wants
  2397. to control the name of the free variables in the result.
  2398. An error is raised if no symbols are used for case 1, but if
  2399. no symbols are provided for case 2, internally generated symbols
  2400. will be provided. When providing symbols for case 2, there should
  2401. be at least as many symbols are there are columns in matrix A.
  2402. The algorithm used here is Gauss-Jordan elimination, which
  2403. results, after elimination, in a row echelon form matrix.
  2404. Returns
  2405. =======
  2406. A FiniteSet containing an ordered tuple of values for the
  2407. unknowns for which the `system` has a solution. (Wrapping
  2408. the tuple in FiniteSet is used to maintain a consistent
  2409. output format throughout solveset.)
  2410. Returns EmptySet, if the linear system is inconsistent.
  2411. Raises
  2412. ======
  2413. ValueError
  2414. The input is not valid.
  2415. The symbols are not given.
  2416. Examples
  2417. ========
  2418. >>> from sympy import Matrix, linsolve, symbols
  2419. >>> x, y, z = symbols("x, y, z")
  2420. >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
  2421. >>> b = Matrix([3, 6, 9])
  2422. >>> A
  2423. Matrix([
  2424. [1, 2, 3],
  2425. [4, 5, 6],
  2426. [7, 8, 10]])
  2427. >>> b
  2428. Matrix([
  2429. [3],
  2430. [6],
  2431. [9]])
  2432. >>> linsolve((A, b), [x, y, z])
  2433. {(-1, 2, 0)}
  2434. * Parametric Solution: In case the system is underdetermined, the
  2435. function will return a parametric solution in terms of the given
  2436. symbols. Those that are free will be returned unchanged. e.g. in
  2437. the system below, `z` is returned as the solution for variable z;
  2438. it can take on any value.
  2439. >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
  2440. >>> b = Matrix([3, 6, 9])
  2441. >>> linsolve((A, b), x, y, z)
  2442. {(z - 1, 2 - 2*z, z)}
  2443. If no symbols are given, internally generated symbols will be used.
  2444. The ``tau0`` in the third position indicates (as before) that the third
  2445. variable -- whatever it is named -- can take on any value:
  2446. >>> linsolve((A, b))
  2447. {(tau0 - 1, 2 - 2*tau0, tau0)}
  2448. * List of equations as input
  2449. >>> Eqns = [3*x + 2*y - z - 1, 2*x - 2*y + 4*z + 2, - x + y/2 - z]
  2450. >>> linsolve(Eqns, x, y, z)
  2451. {(1, -2, -2)}
  2452. * Augmented matrix as input
  2453. >>> aug = Matrix([[2, 1, 3, 1], [2, 6, 8, 3], [6, 8, 18, 5]])
  2454. >>> aug
  2455. Matrix([
  2456. [2, 1, 3, 1],
  2457. [2, 6, 8, 3],
  2458. [6, 8, 18, 5]])
  2459. >>> linsolve(aug, x, y, z)
  2460. {(3/10, 2/5, 0)}
  2461. * Solve for symbolic coefficients
  2462. >>> a, b, c, d, e, f = symbols('a, b, c, d, e, f')
  2463. >>> eqns = [a*x + b*y - c, d*x + e*y - f]
  2464. >>> linsolve(eqns, x, y)
  2465. {((-b*f + c*e)/(a*e - b*d), (a*f - c*d)/(a*e - b*d))}
  2466. * A degenerate system returns solution as set of given
  2467. symbols.
  2468. >>> system = Matrix(([0, 0, 0], [0, 0, 0], [0, 0, 0]))
  2469. >>> linsolve(system, x, y)
  2470. {(x, y)}
  2471. * For an empty system linsolve returns empty set
  2472. >>> linsolve([], x)
  2473. EmptySet
  2474. * An error is raised if any nonlinearity is detected, even
  2475. if it could be removed with expansion
  2476. >>> linsolve([x*(1/x - 1)], x)
  2477. Traceback (most recent call last):
  2478. ...
  2479. NonlinearError: nonlinear term: 1/x
  2480. >>> linsolve([x*(y + 1)], x, y)
  2481. Traceback (most recent call last):
  2482. ...
  2483. NonlinearError: nonlinear cross-term: x*(y + 1)
  2484. >>> linsolve([x**2 - 1], x)
  2485. Traceback (most recent call last):
  2486. ...
  2487. NonlinearError: nonlinear term: x**2
  2488. """
  2489. if not system:
  2490. return S.EmptySet
  2491. # If second argument is an iterable
  2492. if symbols and hasattr(symbols[0], '__iter__'):
  2493. symbols = symbols[0]
  2494. sym_gen = isinstance(symbols, GeneratorType)
  2495. dup_msg = 'duplicate symbols given'
  2496. b = None # if we don't get b the input was bad
  2497. # unpack system
  2498. if hasattr(system, '__iter__'):
  2499. # 1). (A, b)
  2500. if len(system) == 2 and isinstance(system[0], MatrixBase):
  2501. A, b = system
  2502. # 2). (eq1, eq2, ...)
  2503. if not isinstance(system[0], MatrixBase):
  2504. if sym_gen or not symbols:
  2505. raise ValueError(filldedent('''
  2506. When passing a system of equations, the explicit
  2507. symbols for which a solution is being sought must
  2508. be given as a sequence, too.
  2509. '''))
  2510. if len(set(symbols)) != len(symbols):
  2511. raise ValueError(dup_msg)
  2512. #
  2513. # Pass to the sparse solver implemented in polys. It is important
  2514. # that we do not attempt to convert the equations to a matrix
  2515. # because that would be very inefficient for large sparse systems
  2516. # of equations.
  2517. #
  2518. eqs = system
  2519. eqs = [sympify(eq) for eq in eqs]
  2520. try:
  2521. sol = _linsolve(eqs, symbols)
  2522. except PolyNonlinearError as exc:
  2523. # e.g. cos(x) contains an element of the set of generators
  2524. raise NonlinearError(str(exc))
  2525. if sol is None:
  2526. return S.EmptySet
  2527. sol = FiniteSet(Tuple(*(sol.get(sym, sym) for sym in symbols)))
  2528. return sol
  2529. elif isinstance(system, MatrixBase) and not (
  2530. symbols and not isinstance(symbols, GeneratorType) and
  2531. isinstance(symbols[0], MatrixBase)):
  2532. # 3). A augmented with b
  2533. A, b = system[:, :-1], system[:, -1:]
  2534. if b is None:
  2535. raise ValueError("Invalid arguments")
  2536. if sym_gen:
  2537. symbols = [next(symbols) for i in range(A.cols)]
  2538. symset = set(symbols)
  2539. if any(symset & (A.free_symbols | b.free_symbols)):
  2540. raise ValueError(filldedent('''
  2541. At least one of the symbols provided
  2542. already appears in the system to be solved.
  2543. One way to avoid this is to use Dummy symbols in
  2544. the generator, e.g. numbered_symbols('%s', cls=Dummy)
  2545. ''' % symbols[0].name.rstrip('1234567890')))
  2546. elif len(symset) != len(symbols):
  2547. raise ValueError(dup_msg)
  2548. if not symbols:
  2549. symbols = [Dummy() for _ in range(A.cols)]
  2550. name = _uniquely_named_symbol('tau', (A, b),
  2551. compare=lambda i: str(i).rstrip('1234567890')).name
  2552. gen = numbered_symbols(name)
  2553. else:
  2554. gen = None
  2555. # This is just a wrapper for solve_lin_sys
  2556. eqs = []
  2557. rows = A.tolist()
  2558. for rowi, bi in zip(rows, b):
  2559. terms = [elem * sym for elem, sym in zip(rowi, symbols) if elem]
  2560. terms.append(-bi)
  2561. eqs.append(Add(*terms))
  2562. eqs, ring = sympy_eqs_to_ring(eqs, symbols)
  2563. sol = solve_lin_sys(eqs, ring, _raw=False)
  2564. if sol is None:
  2565. return S.EmptySet
  2566. #sol = {sym:val for sym, val in sol.items() if sym != val}
  2567. sol = FiniteSet(Tuple(*(sol.get(sym, sym) for sym in symbols)))
  2568. if gen is not None:
  2569. solsym = sol.free_symbols
  2570. rep = {sym: next(gen) for sym in symbols if sym in solsym}
  2571. sol = sol.subs(rep)
  2572. return sol
  2573. ##############################################################################
  2574. # ------------------------------nonlinsolve ---------------------------------#
  2575. ##############################################################################
  2576. def _return_conditionset(eqs, symbols):
  2577. # return conditionset
  2578. eqs = (Eq(lhs, 0) for lhs in eqs)
  2579. condition_set = ConditionSet(
  2580. Tuple(*symbols), And(*eqs), S.Complexes**len(symbols))
  2581. return condition_set
  2582. def substitution(system, symbols, result=[{}], known_symbols=[],
  2583. exclude=[], all_symbols=None):
  2584. r"""
  2585. Solves the `system` using substitution method. It is used in
  2586. :func:`~.nonlinsolve`. This will be called from :func:`~.nonlinsolve` when any
  2587. equation(s) is non polynomial equation.
  2588. Parameters
  2589. ==========
  2590. system : list of equations
  2591. The target system of equations
  2592. symbols : list of symbols to be solved.
  2593. The variable(s) for which the system is solved
  2594. known_symbols : list of solved symbols
  2595. Values are known for these variable(s)
  2596. result : An empty list or list of dict
  2597. If No symbol values is known then empty list otherwise
  2598. symbol as keys and corresponding value in dict.
  2599. exclude : Set of expression.
  2600. Mostly denominator expression(s) of the equations of the system.
  2601. Final solution should not satisfy these expressions.
  2602. all_symbols : known_symbols + symbols(unsolved).
  2603. Returns
  2604. =======
  2605. A FiniteSet of ordered tuple of values of `all_symbols` for which the
  2606. `system` has solution. Order of values in the tuple is same as symbols
  2607. present in the parameter `all_symbols`. If parameter `all_symbols` is None
  2608. then same as symbols present in the parameter `symbols`.
  2609. Please note that general FiniteSet is unordered, the solution returned
  2610. here is not simply a FiniteSet of solutions, rather it is a FiniteSet of
  2611. ordered tuple, i.e. the first & only argument to FiniteSet is a tuple of
  2612. solutions, which is ordered, & hence the returned solution is ordered.
  2613. Also note that solution could also have been returned as an ordered tuple,
  2614. FiniteSet is just a wrapper `{}` around the tuple. It has no other
  2615. significance except for the fact it is just used to maintain a consistent
  2616. output format throughout the solveset.
  2617. Raises
  2618. ======
  2619. ValueError
  2620. The input is not valid.
  2621. The symbols are not given.
  2622. AttributeError
  2623. The input symbols are not :class:`~.Symbol` type.
  2624. Examples
  2625. ========
  2626. >>> from sympy import symbols, substitution
  2627. >>> x, y = symbols('x, y', real=True)
  2628. >>> substitution([x + y], [x], [{y: 1}], [y], set([]), [x, y])
  2629. {(-1, 1)}
  2630. * When you want a soln not satisfying $x + 1 = 0$
  2631. >>> substitution([x + y], [x], [{y: 1}], [y], set([x + 1]), [y, x])
  2632. EmptySet
  2633. >>> substitution([x + y], [x], [{y: 1}], [y], set([x - 1]), [y, x])
  2634. {(1, -1)}
  2635. >>> substitution([x + y - 1, y - x**2 + 5], [x, y])
  2636. {(-3, 4), (2, -1)}
  2637. * Returns both real and complex solution
  2638. >>> x, y, z = symbols('x, y, z')
  2639. >>> from sympy import exp, sin
  2640. >>> substitution([exp(x) - sin(y), y**2 - 4], [x, y])
  2641. {(ImageSet(Lambda(_n, I*(2*_n*pi + pi) + log(sin(2))), Integers), -2),
  2642. (ImageSet(Lambda(_n, 2*_n*I*pi + log(sin(2))), Integers), 2)}
  2643. >>> eqs = [z**2 + exp(2*x) - sin(y), -3 + exp(-y)]
  2644. >>> substitution(eqs, [y, z])
  2645. {(-log(3), -sqrt(-exp(2*x) - sin(log(3)))),
  2646. (-log(3), sqrt(-exp(2*x) - sin(log(3)))),
  2647. (ImageSet(Lambda(_n, 2*_n*I*pi - log(3)), Integers),
  2648. ImageSet(Lambda(_n, -sqrt(-exp(2*x) + sin(2*_n*I*pi - log(3)))), Integers)),
  2649. (ImageSet(Lambda(_n, 2*_n*I*pi - log(3)), Integers),
  2650. ImageSet(Lambda(_n, sqrt(-exp(2*x) + sin(2*_n*I*pi - log(3)))), Integers))}
  2651. """
  2652. if not system:
  2653. return S.EmptySet
  2654. for i, e in enumerate(system):
  2655. if isinstance(e, Eq):
  2656. system[i] = e.lhs - e.rhs
  2657. if not symbols:
  2658. msg = ('Symbols must be given, for which solution of the '
  2659. 'system is to be found.')
  2660. raise ValueError(filldedent(msg))
  2661. if not is_sequence(symbols):
  2662. msg = ('symbols should be given as a sequence, e.g. a list.'
  2663. 'Not type %s: %s')
  2664. raise TypeError(filldedent(msg % (type(symbols), symbols)))
  2665. if not getattr(symbols[0], 'is_Symbol', False):
  2666. msg = ('Iterable of symbols must be given as '
  2667. 'second argument, not type %s: %s')
  2668. raise ValueError(filldedent(msg % (type(symbols[0]), symbols[0])))
  2669. # By default `all_symbols` will be same as `symbols`
  2670. if all_symbols is None:
  2671. all_symbols = symbols
  2672. old_result = result
  2673. # storing complements and intersection for particular symbol
  2674. complements = {}
  2675. intersections = {}
  2676. # when total_solveset_call equals total_conditionset
  2677. # it means that solveset failed to solve all eqs.
  2678. total_conditionset = -1
  2679. total_solveset_call = -1
  2680. def _unsolved_syms(eq, sort=False):
  2681. """Returns the unsolved symbol present
  2682. in the equation `eq`.
  2683. """
  2684. free = eq.free_symbols
  2685. unsolved = (free - set(known_symbols)) & set(all_symbols)
  2686. if sort:
  2687. unsolved = list(unsolved)
  2688. unsolved.sort(key=default_sort_key)
  2689. return unsolved
  2690. # sort such that equation with the fewest potential symbols is first.
  2691. # means eq with less number of variable first in the list.
  2692. eqs_in_better_order = list(
  2693. ordered(system, lambda _: len(_unsolved_syms(_))))
  2694. def add_intersection_complement(result, intersection_dict, complement_dict):
  2695. # If solveset has returned some intersection/complement
  2696. # for any symbol, it will be added in the final solution.
  2697. final_result = []
  2698. for res in result:
  2699. res_copy = res
  2700. for key_res, value_res in res.items():
  2701. intersect_set, complement_set = None, None
  2702. for key_sym, value_sym in intersection_dict.items():
  2703. if key_sym == key_res:
  2704. intersect_set = value_sym
  2705. for key_sym, value_sym in complement_dict.items():
  2706. if key_sym == key_res:
  2707. complement_set = value_sym
  2708. if intersect_set or complement_set:
  2709. new_value = FiniteSet(value_res)
  2710. if intersect_set and intersect_set != S.Complexes:
  2711. new_value = Intersection(new_value, intersect_set)
  2712. if complement_set:
  2713. new_value = Complement(new_value, complement_set)
  2714. if new_value is S.EmptySet:
  2715. res_copy = None
  2716. break
  2717. elif new_value.is_FiniteSet and len(new_value) == 1:
  2718. res_copy[key_res] = set(new_value).pop()
  2719. else:
  2720. res_copy[key_res] = new_value
  2721. if res_copy is not None:
  2722. final_result.append(res_copy)
  2723. return final_result
  2724. def _extract_main_soln(sym, sol, soln_imageset):
  2725. """Separate the Complements, Intersections, ImageSet lambda expr and
  2726. its base_set. This function returns the unmasked sol from different classes
  2727. of sets and also returns the appended ImageSet elements in a
  2728. soln_imageset dict: `{unmasked element: ImageSet}`.
  2729. """
  2730. # if there is union, then need to check
  2731. # Complement, Intersection, Imageset.
  2732. # Order should not be changed.
  2733. if isinstance(sol, ConditionSet):
  2734. # extracts any solution in ConditionSet
  2735. sol = sol.base_set
  2736. if isinstance(sol, Complement):
  2737. # extract solution and complement
  2738. complements[sym] = sol.args[1]
  2739. sol = sol.args[0]
  2740. # complement will be added at the end
  2741. # using `add_intersection_complement` method
  2742. # if there is union of Imageset or other in soln.
  2743. # no testcase is written for this if block
  2744. if isinstance(sol, Union):
  2745. sol_args = sol.args
  2746. sol = S.EmptySet
  2747. # We need in sequence so append finteset elements
  2748. # and then imageset or other.
  2749. for sol_arg2 in sol_args:
  2750. if isinstance(sol_arg2, FiniteSet):
  2751. sol += sol_arg2
  2752. else:
  2753. # ImageSet, Intersection, complement then
  2754. # append them directly
  2755. sol += FiniteSet(sol_arg2)
  2756. if isinstance(sol, Intersection):
  2757. # Interval/Set will be at 0th index always
  2758. if sol.args[0] not in (S.Reals, S.Complexes):
  2759. # Sometimes solveset returns soln with intersection
  2760. # S.Reals or S.Complexes. We don't consider that
  2761. # intersection.
  2762. intersections[sym] = sol.args[0]
  2763. sol = sol.args[1]
  2764. # after intersection and complement Imageset should
  2765. # be checked.
  2766. if isinstance(sol, ImageSet):
  2767. soln_imagest = sol
  2768. expr2 = sol.lamda.expr
  2769. sol = FiniteSet(expr2)
  2770. soln_imageset[expr2] = soln_imagest
  2771. if not isinstance(sol, FiniteSet):
  2772. sol = FiniteSet(sol)
  2773. return sol, soln_imageset
  2774. def _check_exclude(rnew, imgset_yes):
  2775. rnew_ = rnew
  2776. if imgset_yes:
  2777. # replace all dummy variables (Imageset lambda variables)
  2778. # with zero before `checksol`. Considering fundamental soln
  2779. # for `checksol`.
  2780. rnew_copy = rnew.copy()
  2781. dummy_n = imgset_yes[0]
  2782. for key_res, value_res in rnew_copy.items():
  2783. rnew_copy[key_res] = value_res.subs(dummy_n, 0)
  2784. rnew_ = rnew_copy
  2785. # satisfy_exclude == true if it satisfies the expr of `exclude` list.
  2786. try:
  2787. # something like : `Mod(-log(3), 2*I*pi)` can't be
  2788. # simplified right now, so `checksol` returns `TypeError`.
  2789. # when this issue is fixed this try block should be
  2790. # removed. Mod(-log(3), 2*I*pi) == -log(3)
  2791. satisfy_exclude = any(
  2792. checksol(d, rnew_) for d in exclude)
  2793. except TypeError:
  2794. satisfy_exclude = None
  2795. return satisfy_exclude
  2796. def _restore_imgset(rnew, original_imageset, newresult):
  2797. restore_sym = set(rnew.keys()) & \
  2798. set(original_imageset.keys())
  2799. for key_sym in restore_sym:
  2800. img = original_imageset[key_sym]
  2801. rnew[key_sym] = img
  2802. if rnew not in newresult:
  2803. newresult.append(rnew)
  2804. def _append_eq(eq, result, res, delete_soln, n=None):
  2805. u = Dummy('u')
  2806. if n:
  2807. eq = eq.subs(n, 0)
  2808. satisfy = eq if eq in (True, False) else checksol(u, u, eq, minimal=True)
  2809. if satisfy is False:
  2810. delete_soln = True
  2811. res = {}
  2812. else:
  2813. result.append(res)
  2814. return result, res, delete_soln
  2815. def _append_new_soln(rnew, sym, sol, imgset_yes, soln_imageset,
  2816. original_imageset, newresult, eq=None):
  2817. """If `rnew` (A dict <symbol: soln>) contains valid soln
  2818. append it to `newresult` list.
  2819. `imgset_yes` is (base, dummy_var) if there was imageset in previously
  2820. calculated result(otherwise empty tuple). `original_imageset` is dict
  2821. of imageset expr and imageset from this result.
  2822. `soln_imageset` dict of imageset expr and imageset of new soln.
  2823. """
  2824. satisfy_exclude = _check_exclude(rnew, imgset_yes)
  2825. delete_soln = False
  2826. # soln should not satisfy expr present in `exclude` list.
  2827. if not satisfy_exclude:
  2828. local_n = None
  2829. # if it is imageset
  2830. if imgset_yes:
  2831. local_n = imgset_yes[0]
  2832. base = imgset_yes[1]
  2833. if sym and sol:
  2834. # when `sym` and `sol` is `None` means no new
  2835. # soln. In that case we will append rnew directly after
  2836. # substituting original imagesets in rnew values if present
  2837. # (second last line of this function using _restore_imgset)
  2838. dummy_list = list(sol.atoms(Dummy))
  2839. # use one dummy `n` which is in
  2840. # previous imageset
  2841. local_n_list = [
  2842. local_n for i in range(
  2843. 0, len(dummy_list))]
  2844. dummy_zip = zip(dummy_list, local_n_list)
  2845. lam = Lambda(local_n, sol.subs(dummy_zip))
  2846. rnew[sym] = ImageSet(lam, base)
  2847. if eq is not None:
  2848. newresult, rnew, delete_soln = _append_eq(
  2849. eq, newresult, rnew, delete_soln, local_n)
  2850. elif eq is not None:
  2851. newresult, rnew, delete_soln = _append_eq(
  2852. eq, newresult, rnew, delete_soln)
  2853. elif sol in soln_imageset.keys():
  2854. rnew[sym] = soln_imageset[sol]
  2855. # restore original imageset
  2856. _restore_imgset(rnew, original_imageset, newresult)
  2857. else:
  2858. newresult.append(rnew)
  2859. elif satisfy_exclude:
  2860. delete_soln = True
  2861. rnew = {}
  2862. _restore_imgset(rnew, original_imageset, newresult)
  2863. return newresult, delete_soln
  2864. def _new_order_result(result, eq):
  2865. # separate first, second priority. `res` that makes `eq` value equals
  2866. # to zero, should be used first then other result(second priority).
  2867. # If it is not done then we may miss some soln.
  2868. first_priority = []
  2869. second_priority = []
  2870. for res in result:
  2871. if not any(isinstance(val, ImageSet) for val in res.values()):
  2872. if eq.subs(res) == 0:
  2873. first_priority.append(res)
  2874. else:
  2875. second_priority.append(res)
  2876. if first_priority or second_priority:
  2877. return first_priority + second_priority
  2878. return result
  2879. def _solve_using_known_values(result, solver):
  2880. """Solves the system using already known solution
  2881. (result contains the dict <symbol: value>).
  2882. solver is :func:`~.solveset_complex` or :func:`~.solveset_real`.
  2883. """
  2884. # stores imageset <expr: imageset(Lambda(n, expr), base)>.
  2885. soln_imageset = {}
  2886. total_solvest_call = 0
  2887. total_conditionst = 0
  2888. # sort equations so the one with the fewest potential
  2889. # symbols appears first
  2890. for index, eq in enumerate(eqs_in_better_order):
  2891. newresult = []
  2892. # if imageset, expr is used to solve for other symbol
  2893. imgset_yes = False
  2894. for res in result:
  2895. original_imageset = {}
  2896. got_symbol = set() # symbols solved in one iteration
  2897. # find the imageset and use its expr.
  2898. for k, v in res.items():
  2899. if isinstance(v, ImageSet):
  2900. res[k] = v.lamda.expr
  2901. original_imageset[k] = v
  2902. dummy_n = v.lamda.expr.atoms(Dummy).pop()
  2903. (base,) = v.base_sets
  2904. imgset_yes = (dummy_n, base)
  2905. assert not isinstance(v, FiniteSet) # if so, internal error
  2906. # update eq with everything that is known so far
  2907. eq2 = eq.subs(res).expand()
  2908. if imgset_yes and not eq2.has(imgset_yes[0]):
  2909. # The substituted equation simplified in such a way that
  2910. # it's no longer necessary to encapsulate a potential new
  2911. # solution in an ImageSet. (E.g. at the previous step some
  2912. # {n*2*pi} was found as partial solution for one of the
  2913. # unknowns, but its main solution expression n*2*pi has now
  2914. # been substituted in a trigonometric function.)
  2915. imgset_yes = False
  2916. unsolved_syms = _unsolved_syms(eq2, sort=True)
  2917. if not unsolved_syms:
  2918. if res:
  2919. newresult, delete_res = _append_new_soln(
  2920. res, None, None, imgset_yes, soln_imageset,
  2921. original_imageset, newresult, eq2)
  2922. if delete_res:
  2923. # `delete_res` is true, means substituting `res` in
  2924. # eq2 doesn't return `zero` or deleting the `res`
  2925. # (a soln) since it satisfies expr of `exclude`
  2926. # list.
  2927. result.remove(res)
  2928. continue # skip as it's independent of desired symbols
  2929. depen1, depen2 = eq2.as_independent(*unsolved_syms)
  2930. if (depen1.has(Abs) or depen2.has(Abs)) and solver == solveset_complex:
  2931. # Absolute values cannot be inverted in the
  2932. # complex domain
  2933. continue
  2934. soln_imageset = {}
  2935. for sym in unsolved_syms:
  2936. not_solvable = False
  2937. try:
  2938. soln = solver(eq2, sym)
  2939. total_solvest_call += 1
  2940. soln_new = S.EmptySet
  2941. if isinstance(soln, Complement):
  2942. # separate solution and complement
  2943. complements[sym] = soln.args[1]
  2944. soln = soln.args[0]
  2945. # complement will be added at the end
  2946. if isinstance(soln, Intersection):
  2947. # Interval will be at 0th index always
  2948. if soln.args[0] != Interval(-oo, oo):
  2949. # sometimes solveset returns soln
  2950. # with intersection S.Reals, to confirm that
  2951. # soln is in domain=S.Reals
  2952. intersections[sym] = soln.args[0]
  2953. soln_new += soln.args[1]
  2954. soln = soln_new if soln_new else soln
  2955. if index > 0 and solver == solveset_real:
  2956. # one symbol's real soln, another symbol may have
  2957. # corresponding complex soln.
  2958. if not isinstance(soln, (ImageSet, ConditionSet)):
  2959. soln += solveset_complex(eq2, sym) # might give ValueError with Abs
  2960. except (NotImplementedError, ValueError):
  2961. # If solveset is not able to solve equation `eq2`. Next
  2962. # time we may get soln using next equation `eq2`
  2963. continue
  2964. if isinstance(soln, ConditionSet):
  2965. if soln.base_set in (S.Reals, S.Complexes):
  2966. soln = S.EmptySet
  2967. # don't do `continue` we may get soln
  2968. # in terms of other symbol(s)
  2969. not_solvable = True
  2970. total_conditionst += 1
  2971. else:
  2972. soln = soln.base_set
  2973. if soln is not S.EmptySet:
  2974. soln, soln_imageset = _extract_main_soln(
  2975. sym, soln, soln_imageset)
  2976. for sol in soln:
  2977. # sol is not a `Union` since we checked it
  2978. # before this loop
  2979. sol, soln_imageset = _extract_main_soln(
  2980. sym, sol, soln_imageset)
  2981. sol = set(sol).pop() # XXX what if there are more solutions?
  2982. free = sol.free_symbols
  2983. if got_symbol and any(
  2984. ss in free for ss in got_symbol
  2985. ):
  2986. # sol depends on previously solved symbols
  2987. # then continue
  2988. continue
  2989. rnew = res.copy()
  2990. # put each solution in res and append the new result
  2991. # in the new result list (solution for symbol `s`)
  2992. # along with old results.
  2993. for k, v in res.items():
  2994. if isinstance(v, Expr) and isinstance(sol, Expr):
  2995. # if any unsolved symbol is present
  2996. # Then subs known value
  2997. rnew[k] = v.subs(sym, sol)
  2998. # and add this new solution
  2999. if sol in soln_imageset.keys():
  3000. # replace all lambda variables with 0.
  3001. imgst = soln_imageset[sol]
  3002. rnew[sym] = imgst.lamda(
  3003. *[0 for i in range(0, len(
  3004. imgst.lamda.variables))])
  3005. else:
  3006. rnew[sym] = sol
  3007. newresult, delete_res = _append_new_soln(
  3008. rnew, sym, sol, imgset_yes, soln_imageset,
  3009. original_imageset, newresult)
  3010. if delete_res:
  3011. # deleting the `res` (a soln) since it satisfies
  3012. # eq of `exclude` list
  3013. result.remove(res)
  3014. # solution got for sym
  3015. if not not_solvable:
  3016. got_symbol.add(sym)
  3017. # next time use this new soln
  3018. if newresult:
  3019. result = newresult
  3020. return result, total_solvest_call, total_conditionst
  3021. new_result_real, solve_call1, cnd_call1 = _solve_using_known_values(
  3022. old_result, solveset_real)
  3023. new_result_complex, solve_call2, cnd_call2 = _solve_using_known_values(
  3024. old_result, solveset_complex)
  3025. # If total_solveset_call is equal to total_conditionset
  3026. # then solveset failed to solve all of the equations.
  3027. # In this case we return a ConditionSet here.
  3028. total_conditionset += (cnd_call1 + cnd_call2)
  3029. total_solveset_call += (solve_call1 + solve_call2)
  3030. if total_conditionset == total_solveset_call and total_solveset_call != -1:
  3031. return _return_conditionset(eqs_in_better_order, all_symbols)
  3032. # don't keep duplicate solutions
  3033. filtered_complex = []
  3034. for i in list(new_result_complex):
  3035. for j in list(new_result_real):
  3036. if i.keys() != j.keys():
  3037. continue
  3038. if all(a.dummy_eq(b) for a, b in zip(i.values(), j.values()) \
  3039. if not (isinstance(a, int) and isinstance(b, int))):
  3040. break
  3041. else:
  3042. filtered_complex.append(i)
  3043. # overall result
  3044. result = new_result_real + filtered_complex
  3045. result_all_variables = []
  3046. result_infinite = []
  3047. for res in result:
  3048. if not res:
  3049. # means {None : None}
  3050. continue
  3051. # If length < len(all_symbols) means infinite soln.
  3052. # Some or all the soln is dependent on 1 symbol.
  3053. # eg. {x: y+2} then final soln {x: y+2, y: y}
  3054. if len(res) < len(all_symbols):
  3055. solved_symbols = res.keys()
  3056. unsolved = list(filter(
  3057. lambda x: x not in solved_symbols, all_symbols))
  3058. for unsolved_sym in unsolved:
  3059. res[unsolved_sym] = unsolved_sym
  3060. result_infinite.append(res)
  3061. if res not in result_all_variables:
  3062. result_all_variables.append(res)
  3063. if result_infinite:
  3064. # we have general soln
  3065. # eg : [{x: -1, y : 1}, {x : -y, y: y}] then
  3066. # return [{x : -y, y : y}]
  3067. result_all_variables = result_infinite
  3068. if intersections or complements:
  3069. result_all_variables = add_intersection_complement(
  3070. result_all_variables, intersections, complements)
  3071. # convert to ordered tuple
  3072. result = S.EmptySet
  3073. for r in result_all_variables:
  3074. temp = [r[symb] for symb in all_symbols]
  3075. result += FiniteSet(tuple(temp))
  3076. return result
  3077. def _solveset_work(system, symbols):
  3078. soln = solveset(system[0], symbols[0])
  3079. if isinstance(soln, FiniteSet):
  3080. _soln = FiniteSet(*[(s,) for s in soln])
  3081. return _soln
  3082. else:
  3083. return FiniteSet(tuple(FiniteSet(soln)))
  3084. def _handle_positive_dimensional(polys, symbols, denominators):
  3085. from sympy.polys.polytools import groebner
  3086. # substitution method where new system is groebner basis of the system
  3087. _symbols = list(symbols)
  3088. _symbols.sort(key=default_sort_key)
  3089. basis = groebner(polys, _symbols, polys=True)
  3090. new_system = []
  3091. for poly_eq in basis:
  3092. new_system.append(poly_eq.as_expr())
  3093. result = [{}]
  3094. result = substitution(
  3095. new_system, symbols, result, [],
  3096. denominators)
  3097. return result
  3098. def _handle_zero_dimensional(polys, symbols, system):
  3099. # solve 0 dimensional poly system using `solve_poly_system`
  3100. result = solve_poly_system(polys, *symbols)
  3101. # May be some extra soln is added because
  3102. # we used `unrad` in `_separate_poly_nonpoly`, so
  3103. # need to check and remove if it is not a soln.
  3104. result_update = S.EmptySet
  3105. for res in result:
  3106. dict_sym_value = dict(list(zip(symbols, res)))
  3107. if all(checksol(eq, dict_sym_value) for eq in system):
  3108. result_update += FiniteSet(res)
  3109. return result_update
  3110. def _separate_poly_nonpoly(system, symbols):
  3111. polys = []
  3112. polys_expr = []
  3113. nonpolys = []
  3114. # unrad_changed stores a list of expressions containing
  3115. # radicals that were processed using unrad
  3116. # this is useful if solutions need to be checked later.
  3117. unrad_changed = []
  3118. denominators = set()
  3119. poly = None
  3120. for eq in system:
  3121. # Store denom expressions that contain symbols
  3122. denominators.update(_simple_dens(eq, symbols))
  3123. # Convert equality to expression
  3124. if isinstance(eq, Eq):
  3125. eq = eq.lhs - eq.rhs
  3126. # try to remove sqrt and rational power
  3127. without_radicals = unrad(simplify(eq), *symbols)
  3128. if without_radicals:
  3129. unrad_changed.append(eq)
  3130. eq_unrad, cov = without_radicals
  3131. if not cov:
  3132. eq = eq_unrad
  3133. if isinstance(eq, Expr):
  3134. eq = eq.as_numer_denom()[0]
  3135. poly = eq.as_poly(*symbols, extension=True)
  3136. elif simplify(eq).is_number:
  3137. continue
  3138. if poly is not None:
  3139. polys.append(poly)
  3140. polys_expr.append(poly.as_expr())
  3141. else:
  3142. nonpolys.append(eq)
  3143. return polys, polys_expr, nonpolys, denominators, unrad_changed
  3144. def _handle_poly(polys, symbols):
  3145. # _handle_poly(polys, symbols) -> (poly_sol, poly_eqs)
  3146. #
  3147. # We will return possible solution information to nonlinsolve as well as a
  3148. # new system of polynomial equations to be solved if we cannot solve
  3149. # everything directly here. The new system of polynomial equations will be
  3150. # a lex-order Groebner basis for the original system. The lex basis
  3151. # hopefully separate some of the variables and equations and give something
  3152. # easier for substitution to work with.
  3153. # The format for representing solution sets in nonlinsolve and substitution
  3154. # is a list of dicts. These are the special cases:
  3155. no_information = [{}] # No equations solved yet
  3156. no_solutions = [] # The system is inconsistent and has no solutions.
  3157. # If there is no need to attempt further solution of these equations then
  3158. # we return no equations:
  3159. no_equations = []
  3160. inexact = any(not p.domain.is_Exact for p in polys)
  3161. if inexact:
  3162. # The use of Groebner over RR is likely to result incorrectly in an
  3163. # inconsistent Groebner basis. So, convert any float coefficients to
  3164. # Rational before computing the Groebner basis.
  3165. polys = [poly(nsimplify(p, rational=True)) for p in polys]
  3166. # Compute a Groebner basis in grevlex order wrt the ordering given. We will
  3167. # try to convert this to lex order later. Usually it seems to be more
  3168. # efficient to compute a lex order basis by computing a grevlex basis and
  3169. # converting to lex with fglm.
  3170. basis = groebner(polys, symbols, order='grevlex', polys=False)
  3171. #
  3172. # No solutions (inconsistent equations)?
  3173. #
  3174. if 1 in basis:
  3175. # No solutions:
  3176. poly_sol = no_solutions
  3177. poly_eqs = no_equations
  3178. #
  3179. # Finite number of solutions (zero-dimensional case)
  3180. #
  3181. elif basis.is_zero_dimensional:
  3182. # Convert Groebner basis to lex ordering
  3183. basis = basis.fglm('lex')
  3184. # Convert polynomial coefficients back to float before calling
  3185. # solve_poly_system
  3186. if inexact:
  3187. basis = [nfloat(p) for p in basis]
  3188. # Solve the zero-dimensional case using solve_poly_system if possible.
  3189. # If some polynomials have factors that cannot be solved in radicals
  3190. # then this will fail. Using solve_poly_system(..., strict=True)
  3191. # ensures that we either get a complete solution set in radicals or
  3192. # UnsolvableFactorError will be raised.
  3193. try:
  3194. result = solve_poly_system(basis, *symbols, strict=True)
  3195. except UnsolvableFactorError:
  3196. # Failure... not fully solvable in radicals. Return the lex-order
  3197. # basis for substitution to handle.
  3198. poly_sol = no_information
  3199. poly_eqs = list(basis)
  3200. else:
  3201. # Success! We have a finite solution set and solve_poly_system has
  3202. # succeeded in finding all solutions. Return the solutions and also
  3203. # an empty list of remaining equations to be solved.
  3204. poly_sol = [dict(zip(symbols, res)) for res in result]
  3205. poly_eqs = no_equations
  3206. #
  3207. # Infinite families of solutions (positive-dimensional case)
  3208. #
  3209. else:
  3210. # In this case the grevlex basis cannot be converted to lex using the
  3211. # fglm method and also solve_poly_system cannot solve the equations. We
  3212. # would like to return a lex basis but since we can't use fglm we
  3213. # compute the lex basis directly here. The time required to recompute
  3214. # the basis is generally significantly less than the time required by
  3215. # substitution to solve the new system.
  3216. poly_sol = no_information
  3217. poly_eqs = list(groebner(polys, symbols, order='lex', polys=False))
  3218. if inexact:
  3219. poly_eqs = [nfloat(p) for p in poly_eqs]
  3220. return poly_sol, poly_eqs
  3221. def nonlinsolve(system, *symbols):
  3222. r"""
  3223. Solve system of $N$ nonlinear equations with $M$ variables, which means both
  3224. under and overdetermined systems are supported. Positive dimensional
  3225. system is also supported (A system with infinitely many solutions is said
  3226. to be positive-dimensional). In a positive dimensional system the solution will
  3227. be dependent on at least one symbol. Returns both real solution
  3228. and complex solution (if they exist).
  3229. Parameters
  3230. ==========
  3231. system : list of equations
  3232. The target system of equations
  3233. symbols : list of Symbols
  3234. symbols should be given as a sequence eg. list
  3235. Returns
  3236. =======
  3237. A :class:`~.FiniteSet` of ordered tuple of values of `symbols` for which the `system`
  3238. has solution. Order of values in the tuple is same as symbols present in
  3239. the parameter `symbols`.
  3240. Please note that general :class:`~.FiniteSet` is unordered, the solution
  3241. returned here is not simply a :class:`~.FiniteSet` of solutions, rather it
  3242. is a :class:`~.FiniteSet` of ordered tuple, i.e. the first and only
  3243. argument to :class:`~.FiniteSet` is a tuple of solutions, which is
  3244. ordered, and, hence ,the returned solution is ordered.
  3245. Also note that solution could also have been returned as an ordered tuple,
  3246. FiniteSet is just a wrapper ``{}`` around the tuple. It has no other
  3247. significance except for the fact it is just used to maintain a consistent
  3248. output format throughout the solveset.
  3249. For the given set of equations, the respective input types
  3250. are given below:
  3251. .. math:: xy - 1 = 0
  3252. .. math:: 4x^2 + y^2 - 5 = 0
  3253. ::
  3254. system = [x*y - 1, 4*x**2 + y**2 - 5]
  3255. symbols = [x, y]
  3256. Raises
  3257. ======
  3258. ValueError
  3259. The input is not valid.
  3260. The symbols are not given.
  3261. AttributeError
  3262. The input symbols are not `Symbol` type.
  3263. Examples
  3264. ========
  3265. >>> from sympy import symbols, nonlinsolve
  3266. >>> x, y, z = symbols('x, y, z', real=True)
  3267. >>> nonlinsolve([x*y - 1, 4*x**2 + y**2 - 5], [x, y])
  3268. {(-1, -1), (-1/2, -2), (1/2, 2), (1, 1)}
  3269. 1. Positive dimensional system and complements:
  3270. >>> from sympy import pprint
  3271. >>> from sympy.polys.polytools import is_zero_dimensional
  3272. >>> a, b, c, d = symbols('a, b, c, d', extended_real=True)
  3273. >>> eq1 = a + b + c + d
  3274. >>> eq2 = a*b + b*c + c*d + d*a
  3275. >>> eq3 = a*b*c + b*c*d + c*d*a + d*a*b
  3276. >>> eq4 = a*b*c*d - 1
  3277. >>> system = [eq1, eq2, eq3, eq4]
  3278. >>> is_zero_dimensional(system)
  3279. False
  3280. >>> pprint(nonlinsolve(system, [a, b, c, d]), use_unicode=False)
  3281. -1 1 1 -1
  3282. {(---, -d, -, {d} \ {0}), (-, -d, ---, {d} \ {0})}
  3283. d d d d
  3284. >>> nonlinsolve([(x+y)**2 - 4, x + y - 2], [x, y])
  3285. {(2 - y, y)}
  3286. 2. If some of the equations are non-polynomial then `nonlinsolve`
  3287. will call the ``substitution`` function and return real and complex solutions,
  3288. if present.
  3289. >>> from sympy import exp, sin
  3290. >>> nonlinsolve([exp(x) - sin(y), y**2 - 4], [x, y])
  3291. {(ImageSet(Lambda(_n, I*(2*_n*pi + pi) + log(sin(2))), Integers), -2),
  3292. (ImageSet(Lambda(_n, 2*_n*I*pi + log(sin(2))), Integers), 2)}
  3293. 3. If system is non-linear polynomial and zero-dimensional then it
  3294. returns both solution (real and complex solutions, if present) using
  3295. :func:`~.solve_poly_system`:
  3296. >>> from sympy import sqrt
  3297. >>> nonlinsolve([x**2 - 2*y**2 -2, x*y - 2], [x, y])
  3298. {(-2, -1), (2, 1), (-sqrt(2)*I, sqrt(2)*I), (sqrt(2)*I, -sqrt(2)*I)}
  3299. 4. ``nonlinsolve`` can solve some linear (zero or positive dimensional)
  3300. system (because it uses the :func:`sympy.polys.polytools.groebner` function to get the
  3301. groebner basis and then uses the ``substitution`` function basis as the
  3302. new `system`). But it is not recommended to solve linear system using
  3303. ``nonlinsolve``, because :func:`~.linsolve` is better for general linear systems.
  3304. >>> nonlinsolve([x + 2*y -z - 3, x - y - 4*z + 9, y + z - 4], [x, y, z])
  3305. {(3*z - 5, 4 - z, z)}
  3306. 5. System having polynomial equations and only real solution is
  3307. solved using :func:`~.solve_poly_system`:
  3308. >>> e1 = sqrt(x**2 + y**2) - 10
  3309. >>> e2 = sqrt(y**2 + (-x + 10)**2) - 3
  3310. >>> nonlinsolve((e1, e2), (x, y))
  3311. {(191/20, -3*sqrt(391)/20), (191/20, 3*sqrt(391)/20)}
  3312. >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [x, y])
  3313. {(1, 2), (1 - sqrt(5), 2 + sqrt(5)), (1 + sqrt(5), 2 - sqrt(5))}
  3314. >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [y, x])
  3315. {(2, 1), (2 - sqrt(5), 1 + sqrt(5)), (2 + sqrt(5), 1 - sqrt(5))}
  3316. 6. It is better to use symbols instead of trigonometric functions or
  3317. :class:`~.Function`. For example, replace $\sin(x)$ with a symbol, replace
  3318. $f(x)$ with a symbol and so on. Get a solution from ``nonlinsolve`` and then
  3319. use :func:`~.solveset` to get the value of $x$.
  3320. How nonlinsolve is better than old solver ``_solve_system`` :
  3321. =============================================================
  3322. 1. A positive dimensional system solver: nonlinsolve can return
  3323. solution for positive dimensional system. It finds the
  3324. Groebner Basis of the positive dimensional system(calling it as
  3325. basis) then we can start solving equation(having least number of
  3326. variable first in the basis) using solveset and substituting that
  3327. solved solutions into other equation(of basis) to get solution in
  3328. terms of minimum variables. Here the important thing is how we
  3329. are substituting the known values and in which equations.
  3330. 2. Real and complex solutions: nonlinsolve returns both real
  3331. and complex solution. If all the equations in the system are polynomial
  3332. then using :func:`~.solve_poly_system` both real and complex solution is returned.
  3333. If all the equations in the system are not polynomial equation then goes to
  3334. ``substitution`` method with this polynomial and non polynomial equation(s),
  3335. to solve for unsolved variables. Here to solve for particular variable
  3336. solveset_real and solveset_complex is used. For both real and complex
  3337. solution ``_solve_using_known_values`` is used inside ``substitution``
  3338. (``substitution`` will be called when any non-polynomial equation is present).
  3339. If a solution is valid its general solution is added to the final result.
  3340. 3. :class:`~.Complement` and :class:`~.Intersection` will be added:
  3341. nonlinsolve maintains dict for complements and intersections. If solveset
  3342. find complements or/and intersections with any interval or set during the
  3343. execution of ``substitution`` function, then complement or/and
  3344. intersection for that variable is added before returning final solution.
  3345. """
  3346. if not system:
  3347. return S.EmptySet
  3348. if not symbols:
  3349. msg = ('Symbols must be given, for which solution of the '
  3350. 'system is to be found.')
  3351. raise ValueError(filldedent(msg))
  3352. if hasattr(symbols[0], '__iter__'):
  3353. symbols = symbols[0]
  3354. if not is_sequence(symbols) or not symbols:
  3355. msg = ('Symbols must be given, for which solution of the '
  3356. 'system is to be found.')
  3357. raise IndexError(filldedent(msg))
  3358. symbols = list(map(_sympify, symbols))
  3359. system, symbols, swap = recast_to_symbols(system, symbols)
  3360. if swap:
  3361. soln = nonlinsolve(system, symbols)
  3362. return FiniteSet(*[tuple(i.xreplace(swap) for i in s) for s in soln])
  3363. if len(system) == 1 and len(symbols) == 1:
  3364. return _solveset_work(system, symbols)
  3365. # main code of def nonlinsolve() starts from here
  3366. polys, polys_expr, nonpolys, denominators, unrad_changed = \
  3367. _separate_poly_nonpoly(system, symbols)
  3368. poly_eqs = []
  3369. poly_sol = [{}]
  3370. if polys:
  3371. poly_sol, poly_eqs = _handle_poly(polys, symbols)
  3372. if poly_sol and poly_sol[0]:
  3373. poly_syms = set().union(*(eq.free_symbols for eq in polys))
  3374. unrad_syms = set().union(*(eq.free_symbols for eq in unrad_changed))
  3375. if unrad_syms == poly_syms and unrad_changed:
  3376. # if all the symbols have been solved by _handle_poly
  3377. # and unrad has been used then check solutions
  3378. poly_sol = [sol for sol in poly_sol if checksol(unrad_changed, sol)]
  3379. # Collect together the unsolved polynomials with the non-polynomial
  3380. # equations.
  3381. remaining = poly_eqs + nonpolys
  3382. # to_tuple converts a solution dictionary to a tuple containing the
  3383. # value for each symbol
  3384. to_tuple = lambda sol: tuple(sol[s] for s in symbols)
  3385. if not remaining:
  3386. # If there is nothing left to solve then return the solution from
  3387. # solve_poly_system directly.
  3388. return FiniteSet(*map(to_tuple, poly_sol))
  3389. else:
  3390. # Here we handle:
  3391. #
  3392. # 1. The Groebner basis if solve_poly_system failed.
  3393. # 2. The Groebner basis in the positive-dimensional case.
  3394. # 3. Any non-polynomial equations
  3395. #
  3396. # If solve_poly_system did succeed then we pass those solutions in as
  3397. # preliminary results.
  3398. subs_res = substitution(remaining, symbols, result=poly_sol, exclude=denominators)
  3399. if not isinstance(subs_res, FiniteSet):
  3400. return subs_res
  3401. # check solutions produced by substitution. Currently, checking is done for
  3402. # only those solutions which have non-Set variable values.
  3403. if unrad_changed:
  3404. result = [dict(zip(symbols, sol)) for sol in subs_res.args]
  3405. correct_sols = [sol for sol in result if any(isinstance(v, Set) for v in sol)
  3406. or checksol(unrad_changed, sol) != False]
  3407. return FiniteSet(*map(to_tuple, correct_sols))
  3408. else:
  3409. return subs_res