1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490 |
- from typing import Tuple as tTuple
- from sympy.core.add import Add
- from sympy.core.basic import sympify, cacheit
- from sympy.core.expr import Expr
- from sympy.core.function import Function, ArgumentIndexError, PoleError, expand_mul
- from sympy.core.logic import fuzzy_not, fuzzy_or, FuzzyBool, fuzzy_and
- from sympy.core.numbers import igcdex, Rational, pi, Integer
- from sympy.core.relational import Ne
- from sympy.core.singleton import S
- from sympy.core.symbol import Symbol, Dummy
- from sympy.functions.combinatorial.factorials import factorial, RisingFactorial
- from sympy.functions.elementary.exponential import log, exp
- from sympy.functions.elementary.integers import floor
- from sympy.functions.elementary.hyperbolic import (acoth, asinh, atanh, cosh,
- coth, HyperbolicFunction, sinh, tanh)
- from sympy.functions.elementary.miscellaneous import sqrt, Min, Max
- from sympy.functions.elementary.piecewise import Piecewise
- from sympy.sets.setexpr import SetExpr
- from sympy.sets.sets import FiniteSet
- from sympy.utilities.iterables import numbered_symbols
- ###############################################################################
- ########################## TRIGONOMETRIC FUNCTIONS ############################
- ###############################################################################
- class TrigonometricFunction(Function):
- """Base class for trigonometric functions. """
- unbranched = True
- _singularities = (S.ComplexInfinity,)
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if s.args[0].is_rational and fuzzy_not(s.args[0].is_zero):
- return False
- else:
- return s.is_rational
- def _eval_is_algebraic(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if fuzzy_not(self.args[0].is_zero) and self.args[0].is_algebraic:
- return False
- pi_coeff = _pi_coeff(self.args[0])
- if pi_coeff is not None and pi_coeff.is_rational:
- return True
- else:
- return s.is_algebraic
- def _eval_expand_complex(self, deep=True, **hints):
- re_part, im_part = self.as_real_imag(deep=deep, **hints)
- return re_part + im_part*S.ImaginaryUnit
- def _as_real_imag(self, deep=True, **hints):
- if self.args[0].is_extended_real:
- if deep:
- hints['complex'] = False
- return (self.args[0].expand(deep, **hints), S.Zero)
- else:
- return (self.args[0], S.Zero)
- if deep:
- re, im = self.args[0].expand(deep, **hints).as_real_imag()
- else:
- re, im = self.args[0].as_real_imag()
- return (re, im)
- def _period(self, general_period, symbol=None):
- f = expand_mul(self.args[0])
- if symbol is None:
- symbol = tuple(f.free_symbols)[0]
- if not f.has(symbol):
- return S.Zero
- if f == symbol:
- return general_period
- if symbol in f.free_symbols:
- if f.is_Mul:
- g, h = f.as_independent(symbol)
- if h == symbol:
- return general_period/abs(g)
- if f.is_Add:
- a, h = f.as_independent(symbol)
- g, h = h.as_independent(symbol, as_Add=False)
- if h == symbol:
- return general_period/abs(g)
- raise NotImplementedError("Use the periodicity function instead.")
- def _peeloff_pi(arg):
- r"""
- Split ARG into two parts, a "rest" and a multiple of $\pi$.
- This assumes ARG to be an Add.
- The multiple of $\pi$ returned in the second position is always a Rational.
- Examples
- ========
- >>> from sympy.functions.elementary.trigonometric import _peeloff_pi
- >>> from sympy import pi
- >>> from sympy.abc import x, y
- >>> _peeloff_pi(x + pi/2)
- (x, 1/2)
- >>> _peeloff_pi(x + 2*pi/3 + pi*y)
- (x + pi*y + pi/6, 1/2)
- """
- pi_coeff = S.Zero
- rest_terms = []
- for a in Add.make_args(arg):
- K = a.coeff(S.Pi)
- if K and K.is_rational:
- pi_coeff += K
- else:
- rest_terms.append(a)
- if pi_coeff is S.Zero:
- return arg, S.Zero
- m1 = (pi_coeff % S.Half)
- m2 = pi_coeff - m1
- if m2.is_integer or ((2*m2).is_integer and m2.is_even is False):
- return Add(*(rest_terms + [m1*pi])), m2
- return arg, S.Zero
- def _pi_coeff(arg, cycles=1):
- r"""
- When arg is a Number times $\pi$ (e.g. $3\pi/2$) then return the Number
- normalized to be in the range $[0, 2]$, else `None`.
- When an even multiple of $\pi$ is encountered, if it is multiplying
- something with known parity then the multiple is returned as 0 otherwise
- as 2.
- Examples
- ========
- >>> from sympy.functions.elementary.trigonometric import _pi_coeff
- >>> from sympy import pi, Dummy
- >>> from sympy.abc import x
- >>> _pi_coeff(3*x*pi)
- 3*x
- >>> _pi_coeff(11*pi/7)
- 11/7
- >>> _pi_coeff(-11*pi/7)
- 3/7
- >>> _pi_coeff(4*pi)
- 0
- >>> _pi_coeff(5*pi)
- 1
- >>> _pi_coeff(5.0*pi)
- 1
- >>> _pi_coeff(5.5*pi)
- 3/2
- >>> _pi_coeff(2 + pi)
- >>> _pi_coeff(2*Dummy(integer=True)*pi)
- 2
- >>> _pi_coeff(2*Dummy(even=True)*pi)
- 0
- """
- arg = sympify(arg)
- if arg is S.Pi:
- return S.One
- elif not arg:
- return S.Zero
- elif arg.is_Mul:
- cx = arg.coeff(S.Pi)
- if cx:
- c, x = cx.as_coeff_Mul() # pi is not included as coeff
- if c.is_Float:
- # recast exact binary fractions to Rationals
- f = abs(c) % 1
- if f != 0:
- p = -int(round(log(f, 2).evalf()))
- m = 2**p
- cm = c*m
- i = int(cm)
- if i == cm:
- c = Rational(i, m)
- cx = c*x
- else:
- c = Rational(int(c))
- cx = c*x
- if x.is_integer:
- c2 = c % 2
- if c2 == 1:
- return x
- elif not c2:
- if x.is_even is not None: # known parity
- return S.Zero
- return Integer(2)
- else:
- return c2*x
- return cx
- elif arg.is_zero:
- return S.Zero
- class sin(TrigonometricFunction):
- r"""
- The sine function.
- Returns the sine of x (measured in radians).
- Explanation
- ===========
- This function will evaluate automatically in the
- case $x/\pi$ is some rational number [4]_. For example,
- if $x$ is a multiple of $\pi$, $\pi/2$, $\pi/3$, $\pi/4$, and $\pi/6$.
- Examples
- ========
- >>> from sympy import sin, pi
- >>> from sympy.abc import x
- >>> sin(x**2).diff(x)
- 2*x*cos(x**2)
- >>> sin(1).diff(x)
- 0
- >>> sin(pi)
- 0
- >>> sin(pi/2)
- 1
- >>> sin(pi/6)
- 1/2
- >>> sin(pi/12)
- -sqrt(2)/4 + sqrt(6)/4
- See Also
- ========
- csc, cos, sec, tan, cot
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Sin
- .. [4] http://mathworld.wolfram.com/TrigonometryAngles.html
- """
- def period(self, symbol=None):
- return self._period(2*pi, symbol)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return cos(self.args[0])
- else:
- raise ArgumentIndexError(self, argindex)
- @classmethod
- def eval(cls, arg):
- from sympy.calculus.accumulationbounds import AccumBounds
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg.is_zero:
- return S.Zero
- elif arg in (S.Infinity, S.NegativeInfinity):
- return AccumBounds(-1, 1)
- if arg is S.ComplexInfinity:
- return S.NaN
- if isinstance(arg, AccumBounds):
- min, max = arg.min, arg.max
- d = floor(min/(2*S.Pi))
- if min is not S.NegativeInfinity:
- min = min - d*2*S.Pi
- if max is not S.Infinity:
- max = max - d*2*S.Pi
- if AccumBounds(min, max).intersection(FiniteSet(S.Pi/2, S.Pi*Rational(5, 2))) \
- is not S.EmptySet and \
- AccumBounds(min, max).intersection(FiniteSet(S.Pi*Rational(3, 2),
- S.Pi*Rational(7, 2))) is not S.EmptySet:
- return AccumBounds(-1, 1)
- elif AccumBounds(min, max).intersection(FiniteSet(S.Pi/2, S.Pi*Rational(5, 2))) \
- is not S.EmptySet:
- return AccumBounds(Min(sin(min), sin(max)), 1)
- elif AccumBounds(min, max).intersection(FiniteSet(S.Pi*Rational(3, 2), S.Pi*Rational(8, 2))) \
- is not S.EmptySet:
- return AccumBounds(-1, Max(sin(min), sin(max)))
- else:
- return AccumBounds(Min(sin(min), sin(max)),
- Max(sin(min), sin(max)))
- elif isinstance(arg, SetExpr):
- return arg._eval_func(cls)
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return S.ImaginaryUnit*sinh(i_coeff)
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is not None:
- if pi_coeff.is_integer:
- return S.Zero
- if (2*pi_coeff).is_integer:
- # is_even-case handled above as then pi_coeff.is_integer,
- # so check if known to be not even
- if pi_coeff.is_even is False:
- return S.NegativeOne**(pi_coeff - S.Half)
- if not pi_coeff.is_Rational:
- narg = pi_coeff*S.Pi
- if narg != arg:
- return cls(narg)
- return None
- # https://github.com/sympy/sympy/issues/6048
- # transform a sine to a cosine, to avoid redundant code
- if pi_coeff.is_Rational:
- x = pi_coeff % 2
- if x > 1:
- return -cls((x % 1)*S.Pi)
- if 2*x > 1:
- return cls((1 - x)*S.Pi)
- narg = ((pi_coeff + Rational(3, 2)) % 2)*S.Pi
- result = cos(narg)
- if not isinstance(result, cos):
- return result
- if pi_coeff*S.Pi != arg:
- return cls(pi_coeff*S.Pi)
- return None
- if arg.is_Add:
- x, m = _peeloff_pi(arg)
- if m:
- m = m*S.Pi
- return sin(m)*cos(x) + cos(m)*sin(x)
- if arg.is_zero:
- return S.Zero
- if isinstance(arg, asin):
- return arg.args[0]
- if isinstance(arg, atan):
- x = arg.args[0]
- return x/sqrt(1 + x**2)
- if isinstance(arg, atan2):
- y, x = arg.args
- return y/sqrt(x**2 + y**2)
- if isinstance(arg, acos):
- x = arg.args[0]
- return sqrt(1 - x**2)
- if isinstance(arg, acot):
- x = arg.args[0]
- return 1/(sqrt(1 + 1/x**2)*x)
- if isinstance(arg, acsc):
- x = arg.args[0]
- return 1/x
- if isinstance(arg, asec):
- x = arg.args[0]
- return sqrt(1 - 1/x**2)
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- if len(previous_terms) > 2:
- p = previous_terms[-2]
- return -p*x**2/(n*(n - 1))
- else:
- return S.NegativeOne**(n//2)*x**n/factorial(n)
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg = self.args[0]
- if logx is not None:
- arg = arg.subs(log(x), logx)
- if arg.subs(x, 0).has(S.NaN, S.ComplexInfinity):
- raise PoleError("Cannot expand %s around 0" % (self))
- return Function._eval_nseries(self, x, n=n, logx=logx, cdir=cdir)
- def _eval_rewrite_as_exp(self, arg, **kwargs):
- I = S.ImaginaryUnit
- if isinstance(arg, (TrigonometricFunction, HyperbolicFunction)):
- arg = arg.func(arg.args[0]).rewrite(exp)
- return (exp(arg*I) - exp(-arg*I))/(2*I)
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- if isinstance(arg, log):
- I = S.ImaginaryUnit
- x = arg.args[0]
- return I*x**-I/2 - I*x**I /2
- def _eval_rewrite_as_cos(self, arg, **kwargs):
- return cos(arg - S.Pi/2, evaluate=False)
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- tan_half = tan(S.Half*arg)
- return 2*tan_half/(1 + tan_half**2)
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return sin(arg)*cos(arg)/cos(arg)
- def _eval_rewrite_as_cot(self, arg, **kwargs):
- cot_half = cot(S.Half*arg)
- return 2*cot_half/(1 + cot_half**2)
- def _eval_rewrite_as_pow(self, arg, **kwargs):
- return self.rewrite(cos).rewrite(pow)
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- return self.rewrite(cos).rewrite(sqrt)
- def _eval_rewrite_as_csc(self, arg, **kwargs):
- return 1/csc(arg)
- def _eval_rewrite_as_sec(self, arg, **kwargs):
- return 1/sec(arg - S.Pi/2, evaluate=False)
- def _eval_rewrite_as_sinc(self, arg, **kwargs):
- return arg*sinc(arg)
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate())
- def as_real_imag(self, deep=True, **hints):
- re, im = self._as_real_imag(deep=deep, **hints)
- return (sin(re)*cosh(im), cos(re)*sinh(im))
- def _eval_expand_trig(self, **hints):
- from sympy.functions.special.polynomials import chebyshevt, chebyshevu
- arg = self.args[0]
- x = None
- if arg.is_Add: # TODO, implement more if deep stuff here
- # TODO: Do this more efficiently for more than two terms
- x, y = arg.as_two_terms()
- sx = sin(x, evaluate=False)._eval_expand_trig()
- sy = sin(y, evaluate=False)._eval_expand_trig()
- cx = cos(x, evaluate=False)._eval_expand_trig()
- cy = cos(y, evaluate=False)._eval_expand_trig()
- return sx*cy + sy*cx
- elif arg.is_Mul:
- n, x = arg.as_coeff_Mul(rational=True)
- if n.is_Integer: # n will be positive because of .eval
- # canonicalization
- # See http://mathworld.wolfram.com/Multiple-AngleFormulas.html
- if n.is_odd:
- return S.NegativeOne**((n - 1)/2)*chebyshevt(n, sin(x))
- else:
- return expand_mul(S.NegativeOne**(n/2 - 1)*cos(x)*
- chebyshevu(n - 1, sin(x)), deep=False)
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is not None:
- if pi_coeff.is_Rational:
- return self.rewrite(sqrt)
- return sin(arg)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import re
- from sympy.calculus.accumulationbounds import AccumBounds
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- n = x0/S.Pi
- if n.is_integer:
- lt = (arg - n*S.Pi).as_leading_term(x)
- return (S.NegativeOne**n)*lt
- if x0 is S.ComplexInfinity:
- x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
- if x0 in [S.Infinity, S.NegativeInfinity]:
- return AccumBounds(-1, 1)
- return self.func(x0) if x0.is_finite else self
- def _eval_is_extended_real(self):
- if self.args[0].is_extended_real:
- return True
- def _eval_is_finite(self):
- arg = self.args[0]
- if arg.is_extended_real:
- return True
- def _eval_is_zero(self):
- rest, pi_mult = _peeloff_pi(self.args[0])
- if rest.is_zero:
- return pi_mult.is_integer
- def _eval_is_complex(self):
- if self.args[0].is_extended_real \
- or self.args[0].is_complex:
- return True
- class cos(TrigonometricFunction):
- """
- The cosine function.
- Returns the cosine of x (measured in radians).
- Explanation
- ===========
- See :func:`sin` for notes about automatic evaluation.
- Examples
- ========
- >>> from sympy import cos, pi
- >>> from sympy.abc import x
- >>> cos(x**2).diff(x)
- -2*x*sin(x**2)
- >>> cos(1).diff(x)
- 0
- >>> cos(pi)
- -1
- >>> cos(pi/2)
- 0
- >>> cos(2*pi/3)
- -1/2
- >>> cos(pi/12)
- sqrt(2)/4 + sqrt(6)/4
- See Also
- ========
- sin, csc, sec, tan, cot
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Cos
- """
- def period(self, symbol=None):
- return self._period(2*pi, symbol)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return -sin(self.args[0])
- else:
- raise ArgumentIndexError(self, argindex)
- @classmethod
- def eval(cls, arg):
- from sympy.functions.special.polynomials import chebyshevt
- from sympy.calculus.accumulationbounds import AccumBounds
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg.is_zero:
- return S.One
- elif arg in (S.Infinity, S.NegativeInfinity):
- # In this case it is better to return AccumBounds(-1, 1)
- # rather than returning S.NaN, since AccumBounds(-1, 1)
- # preserves the information that sin(oo) is between
- # -1 and 1, where S.NaN does not do that.
- return AccumBounds(-1, 1)
- if arg is S.ComplexInfinity:
- return S.NaN
- if isinstance(arg, AccumBounds):
- return sin(arg + S.Pi/2)
- elif isinstance(arg, SetExpr):
- return arg._eval_func(cls)
- if arg.is_extended_real and arg.is_finite is False:
- return AccumBounds(-1, 1)
- if arg.could_extract_minus_sign():
- return cls(-arg)
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return cosh(i_coeff)
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is not None:
- if pi_coeff.is_integer:
- return (S.NegativeOne)**pi_coeff
- if (2*pi_coeff).is_integer:
- # is_even-case handled above as then pi_coeff.is_integer,
- # so check if known to be not even
- if pi_coeff.is_even is False:
- return S.Zero
- if not pi_coeff.is_Rational:
- narg = pi_coeff*S.Pi
- if narg != arg:
- return cls(narg)
- return None
- # cosine formula #####################
- # https://github.com/sympy/sympy/issues/6048
- # explicit calculations are performed for
- # cos(k pi/n) for n = 8,10,12,15,20,24,30,40,60,120
- # Some other exact values like cos(k pi/240) can be
- # calculated using a partial-fraction decomposition
- # by calling cos( X ).rewrite(sqrt)
- cst_table_some = {
- 3: S.Half,
- 5: (sqrt(5) + 1)/4,
- }
- if pi_coeff.is_Rational:
- q = pi_coeff.q
- p = pi_coeff.p % (2*q)
- if p > q:
- narg = (pi_coeff - 1)*S.Pi
- return -cls(narg)
- if 2*p > q:
- narg = (1 - pi_coeff)*S.Pi
- return -cls(narg)
- # If nested sqrt's are worse than un-evaluation
- # you can require q to be in (1, 2, 3, 4, 6, 12)
- # q <= 12, q=15, q=20, q=24, q=30, q=40, q=60, q=120 return
- # expressions with 2 or fewer sqrt nestings.
- table2 = {
- 12: (3, 4),
- 20: (4, 5),
- 30: (5, 6),
- 15: (6, 10),
- 24: (6, 8),
- 40: (8, 10),
- 60: (20, 30),
- 120: (40, 60)
- }
- if q in table2:
- a, b = p*S.Pi/table2[q][0], p*S.Pi/table2[q][1]
- nvala, nvalb = cls(a), cls(b)
- if None in (nvala, nvalb):
- return None
- return nvala*nvalb + cls(S.Pi/2 - a)*cls(S.Pi/2 - b)
- if q > 12:
- return None
- if q in cst_table_some:
- cts = cst_table_some[pi_coeff.q]
- return chebyshevt(pi_coeff.p, cts).expand()
- if 0 == q % 2:
- narg = (pi_coeff*2)*S.Pi
- nval = cls(narg)
- if None == nval:
- return None
- x = (2*pi_coeff + 1)/2
- sign_cos = (-1)**((-1 if x < 0 else 1)*int(abs(x)))
- return sign_cos*sqrt( (1 + nval)/2 )
- return None
- if arg.is_Add:
- x, m = _peeloff_pi(arg)
- if m:
- m = m*S.Pi
- return cos(m)*cos(x) - sin(m)*sin(x)
- if arg.is_zero:
- return S.One
- if isinstance(arg, acos):
- return arg.args[0]
- if isinstance(arg, atan):
- x = arg.args[0]
- return 1/sqrt(1 + x**2)
- if isinstance(arg, atan2):
- y, x = arg.args
- return x/sqrt(x**2 + y**2)
- if isinstance(arg, asin):
- x = arg.args[0]
- return sqrt(1 - x ** 2)
- if isinstance(arg, acot):
- x = arg.args[0]
- return 1/sqrt(1 + 1/x**2)
- if isinstance(arg, acsc):
- x = arg.args[0]
- return sqrt(1 - 1/x**2)
- if isinstance(arg, asec):
- x = arg.args[0]
- return 1/x
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n < 0 or n % 2 == 1:
- return S.Zero
- else:
- x = sympify(x)
- if len(previous_terms) > 2:
- p = previous_terms[-2]
- return -p*x**2/(n*(n - 1))
- else:
- return S.NegativeOne**(n//2)*x**n/factorial(n)
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg = self.args[0]
- if logx is not None:
- arg = arg.subs(log(x), logx)
- if arg.subs(x, 0).has(S.NaN, S.ComplexInfinity):
- raise PoleError("Cannot expand %s around 0" % (self))
- return Function._eval_nseries(self, x, n=n, logx=logx, cdir=cdir)
- def _eval_rewrite_as_exp(self, arg, **kwargs):
- I = S.ImaginaryUnit
- if isinstance(arg, (TrigonometricFunction, HyperbolicFunction)):
- arg = arg.func(arg.args[0]).rewrite(exp)
- return (exp(arg*I) + exp(-arg*I))/2
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- if isinstance(arg, log):
- I = S.ImaginaryUnit
- x = arg.args[0]
- return x**I/2 + x**-I/2
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- return sin(arg + S.Pi/2, evaluate=False)
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- tan_half = tan(S.Half*arg)**2
- return (1 - tan_half)/(1 + tan_half)
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return sin(arg)*cos(arg)/sin(arg)
- def _eval_rewrite_as_cot(self, arg, **kwargs):
- cot_half = cot(S.Half*arg)**2
- return (cot_half - 1)/(cot_half + 1)
- def _eval_rewrite_as_pow(self, arg, **kwargs):
- return self._eval_rewrite_as_sqrt(arg)
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- from sympy.functions.special.polynomials import chebyshevt
- def migcdex(x):
- # recursive calcuation of gcd and linear combination
- # for a sequence of integers.
- # Given (x1, x2, x3)
- # Returns (y1, y1, y3, g)
- # such that g is the gcd and x1*y1+x2*y2+x3*y3 - g = 0
- # Note, that this is only one such linear combination.
- if len(x) == 1:
- return (1, x[0])
- if len(x) == 2:
- return igcdex(x[0], x[-1])
- g = migcdex(x[1:])
- u, v, h = igcdex(x[0], g[-1])
- return tuple([u] + [v*i for i in g[0:-1] ] + [h])
- def ipartfrac(r, factors=None):
- from sympy.ntheory import factorint
- if isinstance(r, int):
- return r
- if not isinstance(r, Rational):
- raise TypeError("r is not rational")
- n = r.q
- if 2 > r.q*r.q:
- return r.q
- if None == factors:
- a = [n//x**y for x, y in factorint(r.q).items()]
- else:
- a = [n//x for x in factors]
- if len(a) == 1:
- return [ r ]
- h = migcdex(a)
- ans = [ r.p*Rational(i*j, r.q) for i, j in zip(h[:-1], a) ]
- assert r == sum(ans)
- return ans
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is None:
- return None
- if pi_coeff.is_integer:
- # it was unevaluated
- return self.func(pi_coeff*S.Pi)
- if not pi_coeff.is_Rational:
- return None
- def _cospi257():
- """ Express cos(pi/257) explicitly as a function of radicals
- Based upon the equations in
- http://math.stackexchange.com/questions/516142/how-does-cos2-pi-257-look-like-in-real-radicals
- See also http://www.susqu.edu/brakke/constructions/257-gon.m.txt
- """
- def f1(a, b):
- return (a + sqrt(a**2 + b))/2, (a - sqrt(a**2 + b))/2
- def f2(a, b):
- return (a - sqrt(a**2 + b))/2
- t1, t2 = f1(-1, 256)
- z1, z3 = f1(t1, 64)
- z2, z4 = f1(t2, 64)
- y1, y5 = f1(z1, 4*(5 + t1 + 2*z1))
- y6, y2 = f1(z2, 4*(5 + t2 + 2*z2))
- y3, y7 = f1(z3, 4*(5 + t1 + 2*z3))
- y8, y4 = f1(z4, 4*(5 + t2 + 2*z4))
- x1, x9 = f1(y1, -4*(t1 + y1 + y3 + 2*y6))
- x2, x10 = f1(y2, -4*(t2 + y2 + y4 + 2*y7))
- x3, x11 = f1(y3, -4*(t1 + y3 + y5 + 2*y8))
- x4, x12 = f1(y4, -4*(t2 + y4 + y6 + 2*y1))
- x5, x13 = f1(y5, -4*(t1 + y5 + y7 + 2*y2))
- x6, x14 = f1(y6, -4*(t2 + y6 + y8 + 2*y3))
- x15, x7 = f1(y7, -4*(t1 + y7 + y1 + 2*y4))
- x8, x16 = f1(y8, -4*(t2 + y8 + y2 + 2*y5))
- v1 = f2(x1, -4*(x1 + x2 + x3 + x6))
- v2 = f2(x2, -4*(x2 + x3 + x4 + x7))
- v3 = f2(x8, -4*(x8 + x9 + x10 + x13))
- v4 = f2(x9, -4*(x9 + x10 + x11 + x14))
- v5 = f2(x10, -4*(x10 + x11 + x12 + x15))
- v6 = f2(x16, -4*(x16 + x1 + x2 + x5))
- u1 = -f2(-v1, -4*(v2 + v3))
- u2 = -f2(-v4, -4*(v5 + v6))
- w1 = -2*f2(-u1, -4*u2)
- return sqrt(sqrt(2)*sqrt(w1 + 4)/8 + S.Half)
- cst_table_some = {
- 3: S.Half,
- 5: (sqrt(5) + 1)/4,
- 17: sqrt((15 + sqrt(17))/32 + sqrt(2)*(sqrt(17 - sqrt(17)) +
- sqrt(sqrt(2)*(-8*sqrt(17 + sqrt(17)) - (1 - sqrt(17))
- *sqrt(17 - sqrt(17))) + 6*sqrt(17) + 34))/32),
- 257: _cospi257()
- # 65537 is the only other known Fermat prime and the very
- # large expression is intentionally omitted from SymPy; see
- # http://www.susqu.edu/brakke/constructions/65537-gon.m.txt
- }
- def _fermatCoords(n):
- # if n can be factored in terms of Fermat primes with
- # multiplicity of each being 1, return those primes, else
- # False
- primes = []
- for p_i in cst_table_some:
- quotient, remainder = divmod(n, p_i)
- if remainder == 0:
- n = quotient
- primes.append(p_i)
- if n == 1:
- return tuple(primes)
- return False
- if pi_coeff.q in cst_table_some:
- rv = chebyshevt(pi_coeff.p, cst_table_some[pi_coeff.q])
- if pi_coeff.q < 257:
- rv = rv.expand()
- return rv
- if not pi_coeff.q % 2: # recursively remove factors of 2
- pico2 = pi_coeff*2
- nval = cos(pico2*S.Pi).rewrite(sqrt)
- x = (pico2 + 1)/2
- sign_cos = -1 if int(x) % 2 else 1
- return sign_cos*sqrt( (1 + nval)/2 )
- FC = _fermatCoords(pi_coeff.q)
- if FC:
- decomp = ipartfrac(pi_coeff, FC)
- X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))]
- pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X)
- return pcls.rewrite(sqrt)
- else:
- decomp = ipartfrac(pi_coeff)
- X = [(x[1], x[0]*S.Pi) for x in zip(decomp, numbered_symbols('z'))]
- pcls = cos(sum([x[0] for x in X]))._eval_expand_trig().subs(X)
- return pcls
- def _eval_rewrite_as_sec(self, arg, **kwargs):
- return 1/sec(arg)
- def _eval_rewrite_as_csc(self, arg, **kwargs):
- return 1/sec(arg).rewrite(csc)
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate())
- def as_real_imag(self, deep=True, **hints):
- re, im = self._as_real_imag(deep=deep, **hints)
- return (cos(re)*cosh(im), -sin(re)*sinh(im))
- def _eval_expand_trig(self, **hints):
- from sympy.functions.special.polynomials import chebyshevt
- arg = self.args[0]
- x = None
- if arg.is_Add: # TODO: Do this more efficiently for more than two terms
- x, y = arg.as_two_terms()
- sx = sin(x, evaluate=False)._eval_expand_trig()
- sy = sin(y, evaluate=False)._eval_expand_trig()
- cx = cos(x, evaluate=False)._eval_expand_trig()
- cy = cos(y, evaluate=False)._eval_expand_trig()
- return cx*cy - sx*sy
- elif arg.is_Mul:
- coeff, terms = arg.as_coeff_Mul(rational=True)
- if coeff.is_Integer:
- return chebyshevt(coeff, cos(terms))
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is not None:
- if pi_coeff.is_Rational:
- return self.rewrite(sqrt)
- return cos(arg)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import re
- from sympy.calculus.accumulationbounds import AccumBounds
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- n = (x0 + S.Pi/2)/S.Pi
- if n.is_integer:
- lt = (arg - n*S.Pi + S.Pi/2).as_leading_term(x)
- return (S.NegativeOne**n)*lt
- if x0 is S.ComplexInfinity:
- x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
- if x0 in [S.Infinity, S.NegativeInfinity]:
- return AccumBounds(-1, 1)
- return self.func(x0) if x0.is_finite else self
- def _eval_is_extended_real(self):
- if self.args[0].is_extended_real:
- return True
- def _eval_is_finite(self):
- arg = self.args[0]
- if arg.is_extended_real:
- return True
- def _eval_is_complex(self):
- if self.args[0].is_extended_real \
- or self.args[0].is_complex:
- return True
- def _eval_is_zero(self):
- rest, pi_mult = _peeloff_pi(self.args[0])
- if pi_mult:
- return fuzzy_and([(pi_mult - S.Half).is_integer, rest.is_zero])
- else:
- return rest.is_zero
- class tan(TrigonometricFunction):
- """
- The tangent function.
- Returns the tangent of x (measured in radians).
- Explanation
- ===========
- See :class:`sin` for notes about automatic evaluation.
- Examples
- ========
- >>> from sympy import tan, pi
- >>> from sympy.abc import x
- >>> tan(x**2).diff(x)
- 2*x*(tan(x**2)**2 + 1)
- >>> tan(1).diff(x)
- 0
- >>> tan(pi/8).expand()
- -1 + sqrt(2)
- See Also
- ========
- sin, csc, cos, sec, cot
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Tan
- """
- def period(self, symbol=None):
- return self._period(pi, symbol)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return S.One + self**2
- else:
- raise ArgumentIndexError(self, argindex)
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return atan
- @classmethod
- def eval(cls, arg):
- from sympy.calculus.accumulationbounds import AccumBounds
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg.is_zero:
- return S.Zero
- elif arg in (S.Infinity, S.NegativeInfinity):
- return AccumBounds(S.NegativeInfinity, S.Infinity)
- if arg is S.ComplexInfinity:
- return S.NaN
- if isinstance(arg, AccumBounds):
- min, max = arg.min, arg.max
- d = floor(min/S.Pi)
- if min is not S.NegativeInfinity:
- min = min - d*S.Pi
- if max is not S.Infinity:
- max = max - d*S.Pi
- if AccumBounds(min, max).intersection(FiniteSet(S.Pi/2, S.Pi*Rational(3, 2))):
- return AccumBounds(S.NegativeInfinity, S.Infinity)
- else:
- return AccumBounds(tan(min), tan(max))
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return S.ImaginaryUnit*tanh(i_coeff)
- pi_coeff = _pi_coeff(arg, 2)
- if pi_coeff is not None:
- if pi_coeff.is_integer:
- return S.Zero
- if not pi_coeff.is_Rational:
- narg = pi_coeff*S.Pi
- if narg != arg:
- return cls(narg)
- return None
- if pi_coeff.is_Rational:
- q = pi_coeff.q
- p = pi_coeff.p % q
- # ensure simplified results are returned for n*pi/5, n*pi/10
- table10 = {
- 1: sqrt(1 - 2*sqrt(5)/5),
- 2: sqrt(5 - 2*sqrt(5)),
- 3: sqrt(1 + 2*sqrt(5)/5),
- 4: sqrt(5 + 2*sqrt(5))
- }
- if q in (5, 10):
- n = 10*p/q
- if n > 5:
- n = 10 - n
- return -table10[n]
- else:
- return table10[n]
- if not pi_coeff.q % 2:
- narg = pi_coeff*S.Pi*2
- cresult, sresult = cos(narg), cos(narg - S.Pi/2)
- if not isinstance(cresult, cos) \
- and not isinstance(sresult, cos):
- if sresult == 0:
- return S.ComplexInfinity
- return 1/sresult - cresult/sresult
- table2 = {
- 12: (3, 4),
- 20: (4, 5),
- 30: (5, 6),
- 15: (6, 10),
- 24: (6, 8),
- 40: (8, 10),
- 60: (20, 30),
- 120: (40, 60)
- }
- if q in table2:
- nvala, nvalb = cls(p*S.Pi/table2[q][0]), cls(p*S.Pi/table2[q][1])
- if None in (nvala, nvalb):
- return None
- return (nvala - nvalb)/(1 + nvala*nvalb)
- narg = ((pi_coeff + S.Half) % 1 - S.Half)*S.Pi
- # see cos() to specify which expressions should be
- # expanded automatically in terms of radicals
- cresult, sresult = cos(narg), cos(narg - S.Pi/2)
- if not isinstance(cresult, cos) \
- and not isinstance(sresult, cos):
- if cresult == 0:
- return S.ComplexInfinity
- return (sresult/cresult)
- if narg != arg:
- return cls(narg)
- if arg.is_Add:
- x, m = _peeloff_pi(arg)
- if m:
- tanm = tan(m*S.Pi)
- if tanm is S.ComplexInfinity:
- return -cot(x)
- else: # tanm == 0
- return tan(x)
- if arg.is_zero:
- return S.Zero
- if isinstance(arg, atan):
- return arg.args[0]
- if isinstance(arg, atan2):
- y, x = arg.args
- return y/x
- if isinstance(arg, asin):
- x = arg.args[0]
- return x/sqrt(1 - x**2)
- if isinstance(arg, acos):
- x = arg.args[0]
- return sqrt(1 - x**2)/x
- if isinstance(arg, acot):
- x = arg.args[0]
- return 1/x
- if isinstance(arg, acsc):
- x = arg.args[0]
- return 1/(sqrt(1 - 1/x**2)*x)
- if isinstance(arg, asec):
- x = arg.args[0]
- return sqrt(1 - 1/x**2)*x
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- from sympy.functions.combinatorial.numbers import bernoulli
- if n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- a, b = ((n - 1)//2), 2**(n + 1)
- B = bernoulli(n + 1)
- F = factorial(n + 1)
- return S.NegativeOne**a*b*(b - 1)*B/F*x**n
- def _eval_nseries(self, x, n, logx, cdir=0):
- i = self.args[0].limit(x, 0)*2/S.Pi
- if i and i.is_Integer:
- return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx)
- return Function._eval_nseries(self, x, n=n, logx=logx)
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- if isinstance(arg, log):
- I = S.ImaginaryUnit
- x = arg.args[0]
- return I*(x**-I - x**I)/(x**-I + x**I)
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate())
- def as_real_imag(self, deep=True, **hints):
- re, im = self._as_real_imag(deep=deep, **hints)
- if im:
- denom = cos(2*re) + cosh(2*im)
- return (sin(2*re)/denom, sinh(2*im)/denom)
- else:
- return (self.func(re), S.Zero)
- def _eval_expand_trig(self, **hints):
- from sympy.functions.elementary.complexes import (im, re)
- arg = self.args[0]
- x = None
- if arg.is_Add:
- from sympy.polys.specialpolys import symmetric_poly
- n = len(arg.args)
- TX = []
- for x in arg.args:
- tx = tan(x, evaluate=False)._eval_expand_trig()
- TX.append(tx)
- Yg = numbered_symbols('Y')
- Y = [ next(Yg) for i in range(n) ]
- p = [0, 0]
- for i in range(n + 1):
- p[1 - i % 2] += symmetric_poly(i, Y)*(-1)**((i % 4)//2)
- return (p[0]/p[1]).subs(list(zip(Y, TX)))
- elif arg.is_Mul:
- coeff, terms = arg.as_coeff_Mul(rational=True)
- if coeff.is_Integer and coeff > 1:
- I = S.ImaginaryUnit
- z = Symbol('dummy', real=True)
- P = ((1 + I*z)**coeff).expand()
- return (im(P)/re(P)).subs([(z, tan(terms))])
- return tan(arg)
- def _eval_rewrite_as_exp(self, arg, **kwargs):
- I = S.ImaginaryUnit
- if isinstance(arg, (TrigonometricFunction, HyperbolicFunction)):
- arg = arg.func(arg.args[0]).rewrite(exp)
- neg_exp, pos_exp = exp(-arg*I), exp(arg*I)
- return I*(neg_exp - pos_exp)/(neg_exp + pos_exp)
- def _eval_rewrite_as_sin(self, x, **kwargs):
- return 2*sin(x)**2/sin(2*x)
- def _eval_rewrite_as_cos(self, x, **kwargs):
- return cos(x - S.Pi/2, evaluate=False)/cos(x)
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return sin(arg)/cos(arg)
- def _eval_rewrite_as_cot(self, arg, **kwargs):
- return 1/cot(arg)
- def _eval_rewrite_as_sec(self, arg, **kwargs):
- sin_in_sec_form = sin(arg).rewrite(sec)
- cos_in_sec_form = cos(arg).rewrite(sec)
- return sin_in_sec_form/cos_in_sec_form
- def _eval_rewrite_as_csc(self, arg, **kwargs):
- sin_in_csc_form = sin(arg).rewrite(csc)
- cos_in_csc_form = cos(arg).rewrite(csc)
- return sin_in_csc_form/cos_in_csc_form
- def _eval_rewrite_as_pow(self, arg, **kwargs):
- y = self.rewrite(cos).rewrite(pow)
- if y.has(cos):
- return None
- return y
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- y = self.rewrite(cos).rewrite(sqrt)
- if y.has(cos):
- return None
- return y
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- n = 2*x0/S.Pi
- if n.is_integer:
- lt = (arg - n*S.Pi/2).as_leading_term(x)
- return lt if n.is_even else -1/lt
- return self.func(x0) if x0.is_finite else self
- def _eval_is_extended_real(self):
- # FIXME: currently tan(pi/2) return zoo
- return self.args[0].is_extended_real
- def _eval_is_real(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi - S.Half).is_integer is False:
- return True
- def _eval_is_finite(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi - S.Half).is_integer is False:
- return True
- if arg.is_imaginary:
- return True
- def _eval_is_zero(self):
- rest, pi_mult = _peeloff_pi(self.args[0])
- if rest.is_zero:
- return pi_mult.is_integer
- def _eval_is_complex(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi - S.Half).is_integer is False:
- return True
- class cot(TrigonometricFunction):
- """
- The cotangent function.
- Returns the cotangent of x (measured in radians).
- Explanation
- ===========
- See :class:`sin` for notes about automatic evaluation.
- Examples
- ========
- >>> from sympy import cot, pi
- >>> from sympy.abc import x
- >>> cot(x**2).diff(x)
- 2*x*(-cot(x**2)**2 - 1)
- >>> cot(1).diff(x)
- 0
- >>> cot(pi/12)
- sqrt(3) + 2
- See Also
- ========
- sin, csc, cos, sec, tan
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Cot
- """
- def period(self, symbol=None):
- return self._period(pi, symbol)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return S.NegativeOne - self**2
- else:
- raise ArgumentIndexError(self, argindex)
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return acot
- @classmethod
- def eval(cls, arg):
- from sympy.calculus.accumulationbounds import AccumBounds
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- if arg.is_zero:
- return S.ComplexInfinity
- if arg is S.ComplexInfinity:
- return S.NaN
- if isinstance(arg, AccumBounds):
- return -tan(arg + S.Pi/2)
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return -S.ImaginaryUnit*coth(i_coeff)
- pi_coeff = _pi_coeff(arg, 2)
- if pi_coeff is not None:
- if pi_coeff.is_integer:
- return S.ComplexInfinity
- if not pi_coeff.is_Rational:
- narg = pi_coeff*S.Pi
- if narg != arg:
- return cls(narg)
- return None
- if pi_coeff.is_Rational:
- if pi_coeff.q in (5, 10):
- return tan(S.Pi/2 - arg)
- if pi_coeff.q > 2 and not pi_coeff.q % 2:
- narg = pi_coeff*S.Pi*2
- cresult, sresult = cos(narg), cos(narg - S.Pi/2)
- if not isinstance(cresult, cos) \
- and not isinstance(sresult, cos):
- return 1/sresult + cresult/sresult
- table2 = {
- 12: (3, 4),
- 20: (4, 5),
- 30: (5, 6),
- 15: (6, 10),
- 24: (6, 8),
- 40: (8, 10),
- 60: (20, 30),
- 120: (40, 60)
- }
- q = pi_coeff.q
- p = pi_coeff.p % q
- if q in table2:
- nvala, nvalb = cls(p*S.Pi/table2[q][0]), cls(p*S.Pi/table2[q][1])
- if None in (nvala, nvalb):
- return None
- return (1 + nvala*nvalb)/(nvalb - nvala)
- narg = (((pi_coeff + S.Half) % 1) - S.Half)*S.Pi
- # see cos() to specify which expressions should be
- # expanded automatically in terms of radicals
- cresult, sresult = cos(narg), cos(narg - S.Pi/2)
- if not isinstance(cresult, cos) \
- and not isinstance(sresult, cos):
- if sresult == 0:
- return S.ComplexInfinity
- return cresult/sresult
- if narg != arg:
- return cls(narg)
- if arg.is_Add:
- x, m = _peeloff_pi(arg)
- if m:
- cotm = cot(m*S.Pi)
- if cotm is S.ComplexInfinity:
- return cot(x)
- else: # cotm == 0
- return -tan(x)
- if arg.is_zero:
- return S.ComplexInfinity
- if isinstance(arg, acot):
- return arg.args[0]
- if isinstance(arg, atan):
- x = arg.args[0]
- return 1/x
- if isinstance(arg, atan2):
- y, x = arg.args
- return x/y
- if isinstance(arg, asin):
- x = arg.args[0]
- return sqrt(1 - x**2)/x
- if isinstance(arg, acos):
- x = arg.args[0]
- return x/sqrt(1 - x**2)
- if isinstance(arg, acsc):
- x = arg.args[0]
- return sqrt(1 - 1/x**2)*x
- if isinstance(arg, asec):
- x = arg.args[0]
- return 1/(sqrt(1 - 1/x**2)*x)
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- from sympy.functions.combinatorial.numbers import bernoulli
- if n == 0:
- return 1/sympify(x)
- elif n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- B = bernoulli(n + 1)
- F = factorial(n + 1)
- return S.NegativeOne**((n + 1)//2)*2**(n + 1)*B/F*x**n
- def _eval_nseries(self, x, n, logx, cdir=0):
- i = self.args[0].limit(x, 0)/S.Pi
- if i and i.is_Integer:
- return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx)
- return self.rewrite(tan)._eval_nseries(x, n=n, logx=logx)
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate())
- def as_real_imag(self, deep=True, **hints):
- re, im = self._as_real_imag(deep=deep, **hints)
- if im:
- denom = cos(2*re) - cosh(2*im)
- return (-sin(2*re)/denom, sinh(2*im)/denom)
- else:
- return (self.func(re), S.Zero)
- def _eval_rewrite_as_exp(self, arg, **kwargs):
- I = S.ImaginaryUnit
- if isinstance(arg, (TrigonometricFunction, HyperbolicFunction)):
- arg = arg.func(arg.args[0]).rewrite(exp)
- neg_exp, pos_exp = exp(-arg*I), exp(arg*I)
- return I*(pos_exp + neg_exp)/(pos_exp - neg_exp)
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- if isinstance(arg, log):
- I = S.ImaginaryUnit
- x = arg.args[0]
- return -I*(x**-I + x**I)/(x**-I - x**I)
- def _eval_rewrite_as_sin(self, x, **kwargs):
- return sin(2*x)/(2*(sin(x)**2))
- def _eval_rewrite_as_cos(self, x, **kwargs):
- return cos(x)/cos(x - S.Pi/2, evaluate=False)
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return cos(arg)/sin(arg)
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- return 1/tan(arg)
- def _eval_rewrite_as_sec(self, arg, **kwargs):
- cos_in_sec_form = cos(arg).rewrite(sec)
- sin_in_sec_form = sin(arg).rewrite(sec)
- return cos_in_sec_form/sin_in_sec_form
- def _eval_rewrite_as_csc(self, arg, **kwargs):
- cos_in_csc_form = cos(arg).rewrite(csc)
- sin_in_csc_form = sin(arg).rewrite(csc)
- return cos_in_csc_form/sin_in_csc_form
- def _eval_rewrite_as_pow(self, arg, **kwargs):
- y = self.rewrite(cos).rewrite(pow)
- if y.has(cos):
- return None
- return y
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- y = self.rewrite(cos).rewrite(sqrt)
- if y.has(cos):
- return None
- return y
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- n = 2*x0/S.Pi
- if n.is_integer:
- lt = (arg - n*S.Pi/2).as_leading_term(x)
- return 1/lt if n.is_even else -lt
- return self.func(x0) if x0.is_finite else self
- def _eval_is_extended_real(self):
- return self.args[0].is_extended_real
- def _eval_expand_trig(self, **hints):
- from sympy.functions.elementary.complexes import (im, re)
- arg = self.args[0]
- x = None
- if arg.is_Add:
- from sympy.polys.specialpolys import symmetric_poly
- n = len(arg.args)
- CX = []
- for x in arg.args:
- cx = cot(x, evaluate=False)._eval_expand_trig()
- CX.append(cx)
- Yg = numbered_symbols('Y')
- Y = [ next(Yg) for i in range(n) ]
- p = [0, 0]
- for i in range(n, -1, -1):
- p[(n - i) % 2] += symmetric_poly(i, Y)*(-1)**(((n - i) % 4)//2)
- return (p[0]/p[1]).subs(list(zip(Y, CX)))
- elif arg.is_Mul:
- coeff, terms = arg.as_coeff_Mul(rational=True)
- if coeff.is_Integer and coeff > 1:
- I = S.ImaginaryUnit
- z = Symbol('dummy', real=True)
- P = ((z + I)**coeff).expand()
- return (re(P)/im(P)).subs([(z, cot(terms))])
- return cot(arg) # XXX sec and csc return 1/cos and 1/sin
- def _eval_is_finite(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi).is_integer is False:
- return True
- if arg.is_imaginary:
- return True
- def _eval_is_real(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi).is_integer is False:
- return True
- def _eval_is_complex(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi).is_integer is False:
- return True
- def _eval_is_zero(self):
- rest, pimult = _peeloff_pi(self.args[0])
- if pimult and rest.is_zero:
- return (pimult - S.Half).is_integer
- def _eval_subs(self, old, new):
- arg = self.args[0]
- argnew = arg.subs(old, new)
- if arg != argnew and (argnew/S.Pi).is_integer:
- return S.ComplexInfinity
- return cot(argnew)
- class ReciprocalTrigonometricFunction(TrigonometricFunction):
- """Base class for reciprocal functions of trigonometric functions. """
- _reciprocal_of = None # mandatory, to be defined in subclass
- _singularities = (S.ComplexInfinity,)
- # _is_even and _is_odd are used for correct evaluation of csc(-x), sec(-x)
- # TODO refactor into TrigonometricFunction common parts of
- # trigonometric functions eval() like even/odd, func(x+2*k*pi), etc.
- # optional, to be defined in subclasses:
- _is_even = None # type: FuzzyBool
- _is_odd = None # type: FuzzyBool
- @classmethod
- def eval(cls, arg):
- if arg.could_extract_minus_sign():
- if cls._is_even:
- return cls(-arg)
- if cls._is_odd:
- return -cls(-arg)
- pi_coeff = _pi_coeff(arg)
- if (pi_coeff is not None
- and not (2*pi_coeff).is_integer
- and pi_coeff.is_Rational):
- q = pi_coeff.q
- p = pi_coeff.p % (2*q)
- if p > q:
- narg = (pi_coeff - 1)*S.Pi
- return -cls(narg)
- if 2*p > q:
- narg = (1 - pi_coeff)*S.Pi
- if cls._is_odd:
- return cls(narg)
- elif cls._is_even:
- return -cls(narg)
- if hasattr(arg, 'inverse') and arg.inverse() == cls:
- return arg.args[0]
- t = cls._reciprocal_of.eval(arg)
- if t is None:
- return t
- elif any(isinstance(i, cos) for i in (t, -t)):
- return (1/t).rewrite(sec)
- elif any(isinstance(i, sin) for i in (t, -t)):
- return (1/t).rewrite(csc)
- else:
- return 1/t
- def _call_reciprocal(self, method_name, *args, **kwargs):
- # Calls method_name on _reciprocal_of
- o = self._reciprocal_of(self.args[0])
- return getattr(o, method_name)(*args, **kwargs)
- def _calculate_reciprocal(self, method_name, *args, **kwargs):
- # If calling method_name on _reciprocal_of returns a value != None
- # then return the reciprocal of that value
- t = self._call_reciprocal(method_name, *args, **kwargs)
- return 1/t if t is not None else t
- def _rewrite_reciprocal(self, method_name, arg):
- # Special handling for rewrite functions. If reciprocal rewrite returns
- # unmodified expression, then return None
- t = self._call_reciprocal(method_name, arg)
- if t is not None and t != self._reciprocal_of(arg):
- return 1/t
- def _period(self, symbol):
- f = expand_mul(self.args[0])
- return self._reciprocal_of(f).period(symbol)
- def fdiff(self, argindex=1):
- return -self._calculate_reciprocal("fdiff", argindex)/self**2
- def _eval_rewrite_as_exp(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_exp", arg)
- def _eval_rewrite_as_Pow(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_Pow", arg)
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_sin", arg)
- def _eval_rewrite_as_cos(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_cos", arg)
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_tan", arg)
- def _eval_rewrite_as_pow(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_pow", arg)
- def _eval_rewrite_as_sqrt(self, arg, **kwargs):
- return self._rewrite_reciprocal("_eval_rewrite_as_sqrt", arg)
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate())
- def as_real_imag(self, deep=True, **hints):
- return (1/self._reciprocal_of(self.args[0])).as_real_imag(deep,
- **hints)
- def _eval_expand_trig(self, **hints):
- return self._calculate_reciprocal("_eval_expand_trig", **hints)
- def _eval_is_extended_real(self):
- return self._reciprocal_of(self.args[0])._eval_is_extended_real()
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- return (1/self._reciprocal_of(self.args[0]))._eval_as_leading_term(x)
- def _eval_is_finite(self):
- return (1/self._reciprocal_of(self.args[0])).is_finite
- def _eval_nseries(self, x, n, logx, cdir=0):
- return (1/self._reciprocal_of(self.args[0]))._eval_nseries(x, n, logx)
- class sec(ReciprocalTrigonometricFunction):
- """
- The secant function.
- Returns the secant of x (measured in radians).
- Explanation
- ===========
- See :class:`sin` for notes about automatic evaluation.
- Examples
- ========
- >>> from sympy import sec
- >>> from sympy.abc import x
- >>> sec(x**2).diff(x)
- 2*x*tan(x**2)*sec(x**2)
- >>> sec(1).diff(x)
- 0
- See Also
- ========
- sin, csc, cos, tan, cot
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Sec
- """
- _reciprocal_of = cos
- _is_even = True
- def period(self, symbol=None):
- return self._period(symbol)
- def _eval_rewrite_as_cot(self, arg, **kwargs):
- cot_half_sq = cot(arg/2)**2
- return (cot_half_sq + 1)/(cot_half_sq - 1)
- def _eval_rewrite_as_cos(self, arg, **kwargs):
- return (1/cos(arg))
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return sin(arg)/(cos(arg)*sin(arg))
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- return (1/cos(arg).rewrite(sin))
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- return (1/cos(arg).rewrite(tan))
- def _eval_rewrite_as_csc(self, arg, **kwargs):
- return csc(pi/2 - arg, evaluate=False)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return tan(self.args[0])*sec(self.args[0])
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_complex(self):
- arg = self.args[0]
- if arg.is_complex and (arg/pi - S.Half).is_integer is False:
- return True
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- # Reference Formula:
- # http://functions.wolfram.com/ElementaryFunctions/Sec/06/01/02/01/
- from sympy.functions.combinatorial.numbers import euler
- if n < 0 or n % 2 == 1:
- return S.Zero
- else:
- x = sympify(x)
- k = n//2
- return S.NegativeOne**k*euler(2*k)/factorial(2*k)*x**(2*k)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- n = (x0 + S.Pi/2)/S.Pi
- if n.is_integer:
- lt = (arg - n*S.Pi + S.Pi/2).as_leading_term(x)
- return (S.NegativeOne**n)/lt
- return self.func(x0)
- class csc(ReciprocalTrigonometricFunction):
- """
- The cosecant function.
- Returns the cosecant of x (measured in radians).
- Explanation
- ===========
- See :func:`sin` for notes about automatic evaluation.
- Examples
- ========
- >>> from sympy import csc
- >>> from sympy.abc import x
- >>> csc(x**2).diff(x)
- -2*x*cot(x**2)*csc(x**2)
- >>> csc(1).diff(x)
- 0
- See Also
- ========
- sin, cos, sec, tan, cot
- asin, acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.14
- .. [3] http://functions.wolfram.com/ElementaryFunctions/Csc
- """
- _reciprocal_of = sin
- _is_odd = True
- def period(self, symbol=None):
- return self._period(symbol)
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- return (1/sin(arg))
- def _eval_rewrite_as_sincos(self, arg, **kwargs):
- return cos(arg)/(sin(arg)*cos(arg))
- def _eval_rewrite_as_cot(self, arg, **kwargs):
- cot_half = cot(arg/2)
- return (1 + cot_half**2)/(2*cot_half)
- def _eval_rewrite_as_cos(self, arg, **kwargs):
- return 1/sin(arg).rewrite(cos)
- def _eval_rewrite_as_sec(self, arg, **kwargs):
- return sec(pi/2 - arg, evaluate=False)
- def _eval_rewrite_as_tan(self, arg, **kwargs):
- return (1/sin(arg).rewrite(tan))
- def fdiff(self, argindex=1):
- if argindex == 1:
- return -cot(self.args[0])*csc(self.args[0])
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_complex(self):
- arg = self.args[0]
- if arg.is_real and (arg/pi).is_integer is False:
- return True
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- from sympy.functions.combinatorial.numbers import bernoulli
- if n == 0:
- return 1/sympify(x)
- elif n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- k = n//2 + 1
- return (S.NegativeOne**(k - 1)*2*(2**(2*k - 1) - 1)*
- bernoulli(2*k)*x**(2*k - 1)/factorial(2*k))
- class sinc(Function):
- r"""
- Represents an unnormalized sinc function:
- .. math::
- \operatorname{sinc}(x) =
- \begin{cases}
- \frac{\sin x}{x} & \qquad x \neq 0 \\
- 1 & \qquad x = 0
- \end{cases}
- Examples
- ========
- >>> from sympy import sinc, oo, jn
- >>> from sympy.abc import x
- >>> sinc(x)
- sinc(x)
- * Automated Evaluation
- >>> sinc(0)
- 1
- >>> sinc(oo)
- 0
- * Differentiation
- >>> sinc(x).diff()
- cos(x)/x - sin(x)/x**2
- * Series Expansion
- >>> sinc(x).series()
- 1 - x**2/6 + x**4/120 + O(x**6)
- * As zero'th order spherical Bessel Function
- >>> sinc(x).rewrite(jn)
- jn(0, x)
- See also
- ========
- sin
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Sinc_function
- """
- _singularities = (S.ComplexInfinity,)
- def fdiff(self, argindex=1):
- x = self.args[0]
- if argindex == 1:
- # We would like to return the Piecewise here, but Piecewise.diff
- # currently can't handle removable singularities, meaning things
- # like sinc(x).diff(x, 2) give the wrong answer at x = 0. See
- # https://github.com/sympy/sympy/issues/11402.
- #
- # return Piecewise(((x*cos(x) - sin(x))/x**2, Ne(x, S.Zero)), (S.Zero, S.true))
- return cos(x)/x - sin(x)/x**2
- else:
- raise ArgumentIndexError(self, argindex)
- @classmethod
- def eval(cls, arg):
- if arg.is_zero:
- return S.One
- if arg.is_Number:
- if arg in [S.Infinity, S.NegativeInfinity]:
- return S.Zero
- elif arg is S.NaN:
- return S.NaN
- if arg is S.ComplexInfinity:
- return S.NaN
- if arg.could_extract_minus_sign():
- return cls(-arg)
- pi_coeff = _pi_coeff(arg)
- if pi_coeff is not None:
- if pi_coeff.is_integer:
- if fuzzy_not(arg.is_zero):
- return S.Zero
- elif (2*pi_coeff).is_integer:
- return S.NegativeOne**(pi_coeff - S.Half)/arg
- def _eval_nseries(self, x, n, logx, cdir=0):
- x = self.args[0]
- return (sin(x)/x)._eval_nseries(x, n, logx)
- def _eval_rewrite_as_jn(self, arg, **kwargs):
- from sympy.functions.special.bessel import jn
- return jn(0, arg)
- def _eval_rewrite_as_sin(self, arg, **kwargs):
- return Piecewise((sin(arg)/arg, Ne(arg, S.Zero)), (S.One, S.true))
- def _eval_is_zero(self):
- if self.args[0].is_infinite:
- return True
- rest, pi_mult = _peeloff_pi(self.args[0])
- if rest.is_zero:
- return fuzzy_and([pi_mult.is_integer, pi_mult.is_nonzero])
- if rest.is_Number and pi_mult.is_integer:
- return False
- def _eval_is_real(self):
- if self.args[0].is_extended_real or self.args[0].is_imaginary:
- return True
- _eval_is_finite = _eval_is_real
- ###############################################################################
- ########################### TRIGONOMETRIC INVERSES ############################
- ###############################################################################
- class InverseTrigonometricFunction(Function):
- """Base class for inverse trigonometric functions."""
- _singularities = (S.One, S.NegativeOne, S.Zero, S.ComplexInfinity) # type: tTuple[Expr, ...]
- @staticmethod
- def _asin_table():
- # Only keys with could_extract_minus_sign() == False
- # are actually needed.
- return {
- sqrt(3)/2: S.Pi/3,
- sqrt(2)/2: S.Pi/4,
- 1/sqrt(2): S.Pi/4,
- sqrt((5 - sqrt(5))/8): S.Pi/5,
- sqrt(2)*sqrt(5 - sqrt(5))/4: S.Pi/5,
- sqrt((5 + sqrt(5))/8): S.Pi*Rational(2, 5),
- sqrt(2)*sqrt(5 + sqrt(5))/4: S.Pi*Rational(2, 5),
- S.Half: S.Pi/6,
- sqrt(2 - sqrt(2))/2: S.Pi/8,
- sqrt(S.Half - sqrt(2)/4): S.Pi/8,
- sqrt(2 + sqrt(2))/2: S.Pi*Rational(3, 8),
- sqrt(S.Half + sqrt(2)/4): S.Pi*Rational(3, 8),
- (sqrt(5) - 1)/4: S.Pi/10,
- (1 - sqrt(5))/4: -S.Pi/10,
- (sqrt(5) + 1)/4: S.Pi*Rational(3, 10),
- sqrt(6)/4 - sqrt(2)/4: S.Pi/12,
- -sqrt(6)/4 + sqrt(2)/4: -S.Pi/12,
- (sqrt(3) - 1)/sqrt(8): S.Pi/12,
- (1 - sqrt(3))/sqrt(8): -S.Pi/12,
- sqrt(6)/4 + sqrt(2)/4: S.Pi*Rational(5, 12),
- (1 + sqrt(3))/sqrt(8): S.Pi*Rational(5, 12)
- }
- @staticmethod
- def _atan_table():
- # Only keys with could_extract_minus_sign() == False
- # are actually needed.
- return {
- sqrt(3)/3: S.Pi/6,
- 1/sqrt(3): S.Pi/6,
- sqrt(3): S.Pi/3,
- sqrt(2) - 1: S.Pi/8,
- 1 - sqrt(2): -S.Pi/8,
- 1 + sqrt(2): S.Pi*Rational(3, 8),
- sqrt(5 - 2*sqrt(5)): S.Pi/5,
- sqrt(5 + 2*sqrt(5)): S.Pi*Rational(2, 5),
- sqrt(1 - 2*sqrt(5)/5): S.Pi/10,
- sqrt(1 + 2*sqrt(5)/5): S.Pi*Rational(3, 10),
- 2 - sqrt(3): S.Pi/12,
- -2 + sqrt(3): -S.Pi/12,
- 2 + sqrt(3): S.Pi*Rational(5, 12)
- }
- @staticmethod
- def _acsc_table():
- # Keys for which could_extract_minus_sign()
- # will obviously return True are omitted.
- return {
- 2*sqrt(3)/3: S.Pi/3,
- sqrt(2): S.Pi/4,
- sqrt(2 + 2*sqrt(5)/5): S.Pi/5,
- 1/sqrt(Rational(5, 8) - sqrt(5)/8): S.Pi/5,
- sqrt(2 - 2*sqrt(5)/5): S.Pi*Rational(2, 5),
- 1/sqrt(Rational(5, 8) + sqrt(5)/8): S.Pi*Rational(2, 5),
- 2: S.Pi/6,
- sqrt(4 + 2*sqrt(2)): S.Pi/8,
- 2/sqrt(2 - sqrt(2)): S.Pi/8,
- sqrt(4 - 2*sqrt(2)): S.Pi*Rational(3, 8),
- 2/sqrt(2 + sqrt(2)): S.Pi*Rational(3, 8),
- 1 + sqrt(5): S.Pi/10,
- sqrt(5) - 1: S.Pi*Rational(3, 10),
- -(sqrt(5) - 1): S.Pi*Rational(-3, 10),
- sqrt(6) + sqrt(2): S.Pi/12,
- sqrt(6) - sqrt(2): S.Pi*Rational(5, 12),
- -(sqrt(6) - sqrt(2)): S.Pi*Rational(-5, 12)
- }
- class asin(InverseTrigonometricFunction):
- r"""
- The inverse sine function.
- Returns the arcsine of x in radians.
- Explanation
- ===========
- ``asin(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, 0, 1, -1\}$ and for some instances when the
- result is a rational multiple of $\pi$ (see the ``eval`` class method).
- A purely imaginary argument will lead to an asinh expression.
- Examples
- ========
- >>> from sympy import asin, oo
- >>> asin(1)
- pi/2
- >>> asin(-1)
- -pi/2
- >>> asin(-oo)
- oo*I
- >>> asin(oo)
- -oo*I
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- acsc, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.23
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcSin
- """
- def fdiff(self, argindex=1):
- if argindex == 1:
- return 1/sqrt(1 - self.args[0]**2)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if s.args[0].is_rational:
- return False
- else:
- return s.is_rational
- def _eval_is_positive(self):
- return self._eval_is_extended_real() and self.args[0].is_positive
- def _eval_is_negative(self):
- return self._eval_is_extended_real() and self.args[0].is_negative
- @classmethod
- def eval(cls, arg):
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.Infinity:
- return S.NegativeInfinity*S.ImaginaryUnit
- elif arg is S.NegativeInfinity:
- return S.Infinity*S.ImaginaryUnit
- elif arg.is_zero:
- return S.Zero
- elif arg is S.One:
- return S.Pi/2
- elif arg is S.NegativeOne:
- return -S.Pi/2
- if arg is S.ComplexInfinity:
- return S.ComplexInfinity
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- if arg.is_number:
- asin_table = cls._asin_table()
- if arg in asin_table:
- return asin_table[arg]
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return S.ImaginaryUnit*asinh(i_coeff)
- if arg.is_zero:
- return S.Zero
- if isinstance(arg, sin):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= 2*pi # restrict to [0,2*pi)
- if ang > pi: # restrict to (-pi,pi]
- ang = pi - ang
- # restrict to [-pi/2,pi/2]
- if ang > pi/2:
- ang = pi - ang
- if ang < -pi/2:
- ang = -pi - ang
- return ang
- if isinstance(arg, cos): # acos(x) + asin(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- return pi/2 - acos(arg)
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- if len(previous_terms) >= 2 and n > 2:
- p = previous_terms[-2]
- return p*(n - 2)**2/(n*(n - 1))*x**2
- else:
- k = (n - 1) // 2
- R = RisingFactorial(S.Half, k)
- F = factorial(k)
- return R/F*x**n/n
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import im
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0.is_zero:
- return arg.as_leading_term(x)
- if x0 is S.ComplexInfinity:
- return S.ImaginaryUnit*log(arg.as_leading_term(x))
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if im(cdir) < 0 and x0.is_real and x0 < S.NegativeOne:
- return -S.Pi - self.func(x0)
- elif im(cdir) > 0 and x0.is_real and x0 > S.One:
- return S.Pi - self.func(x0)
- return self.func(x0)
- def _eval_nseries(self, x, n, logx, cdir=0): # asin
- from sympy.functions.elementary.complexes import im
- from sympy.series.order import O
- arg0 = self.args[0].subs(x, 0)
- if arg0 is S.One:
- t = Dummy('t', positive=True)
- ser = asin(S.One - t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.One - self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- if not g.is_meromorphic(x, 0): # cannot be expanded
- return O(1) if n == 0 else S.Pi/2 + O(sqrt(x))
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- if arg0 is S.NegativeOne:
- t = Dummy('t', positive=True)
- ser = asin(S.NegativeOne + t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.One + self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- if not g.is_meromorphic(x, 0): # cannot be expanded
- return O(1) if n == 0 else -S.Pi/2 + O(sqrt(x))
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if arg0 is S.ComplexInfinity:
- return res
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if im(cdir) < 0 and arg0.is_real and arg0 < S.NegativeOne:
- return -S.Pi - res
- elif im(cdir) > 0 and arg0.is_real and arg0 > S.One:
- return S.Pi - res
- return res
- def _eval_rewrite_as_acos(self, x, **kwargs):
- return S.Pi/2 - acos(x)
- def _eval_rewrite_as_atan(self, x, **kwargs):
- return 2*atan(x/(1 + sqrt(1 - x**2)))
- def _eval_rewrite_as_log(self, x, **kwargs):
- return -S.ImaginaryUnit*log(S.ImaginaryUnit*x + sqrt(1 - x**2))
- def _eval_rewrite_as_acot(self, arg, **kwargs):
- return 2*acot((1 + sqrt(1 - arg**2))/arg)
- def _eval_rewrite_as_asec(self, arg, **kwargs):
- return S.Pi/2 - asec(1/arg)
- def _eval_rewrite_as_acsc(self, arg, **kwargs):
- return acsc(1/arg)
- def _eval_is_extended_real(self):
- x = self.args[0]
- return x.is_extended_real and (1 - abs(x)).is_nonnegative
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return sin
- class acos(InverseTrigonometricFunction):
- r"""
- The inverse cosine function.
- Returns the arc cosine of x (measured in radians).
- Examples
- ========
- ``acos(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, 0, 1, -1\}$ and for some instances when
- the result is a rational multiple of $\pi$ (see the eval class method).
- ``acos(zoo)`` evaluates to ``zoo``
- (see note in :class:`sympy.functions.elementary.trigonometric.asec`)
- A purely imaginary argument will be rewritten to asinh.
- Examples
- ========
- >>> from sympy import acos, oo
- >>> acos(1)
- 0
- >>> acos(0)
- pi/2
- >>> acos(oo)
- oo*I
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acsc, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.23
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcCos
- """
- def fdiff(self, argindex=1):
- if argindex == 1:
- return -1/sqrt(1 - self.args[0]**2)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if s.args[0].is_rational:
- return False
- else:
- return s.is_rational
- @classmethod
- def eval(cls, arg):
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.Infinity:
- return S.Infinity*S.ImaginaryUnit
- elif arg is S.NegativeInfinity:
- return S.NegativeInfinity*S.ImaginaryUnit
- elif arg.is_zero:
- return S.Pi/2
- elif arg is S.One:
- return S.Zero
- elif arg is S.NegativeOne:
- return S.Pi
- if arg is S.ComplexInfinity:
- return S.ComplexInfinity
- if arg.is_number:
- asin_table = cls._asin_table()
- if arg in asin_table:
- return pi/2 - asin_table[arg]
- elif -arg in asin_table:
- return pi/2 + asin_table[-arg]
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return pi/2 - asin(arg)
- if isinstance(arg, cos):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= 2*pi # restrict to [0,2*pi)
- if ang > pi: # restrict to [0,pi]
- ang = 2*pi - ang
- return ang
- if isinstance(arg, sin): # acos(x) + asin(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- return pi/2 - asin(arg)
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n == 0:
- return S.Pi/2
- elif n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- if len(previous_terms) >= 2 and n > 2:
- p = previous_terms[-2]
- return p*(n - 2)**2/(n*(n - 1))*x**2
- else:
- k = (n - 1) // 2
- R = RisingFactorial(S.Half, k)
- F = factorial(k)
- return -R/F*x**n/n
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import im
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0 == 1:
- return sqrt(2)*sqrt((S.One - arg).as_leading_term(x))
- if x0 is S.ComplexInfinity:
- return S.ImaginaryUnit*log(arg.as_leading_term(x))
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if im(cdir) < 0 and x0.is_real and x0 < S.NegativeOne:
- return 2*S.Pi - self.func(x0)
- elif im(cdir) > 0 and x0.is_real and x0 > S.One:
- return -self.func(x0)
- return self.func(x0)
- def _eval_is_extended_real(self):
- x = self.args[0]
- return x.is_extended_real and (1 - abs(x)).is_nonnegative
- def _eval_is_nonnegative(self):
- return self._eval_is_extended_real()
- def _eval_nseries(self, x, n, logx, cdir=0): # acos
- from sympy.functions.elementary.complexes import im
- from sympy.series.order import O
- arg0 = self.args[0].subs(x, 0)
- if arg0 is S.One:
- t = Dummy('t', positive=True)
- ser = acos(S.One - t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.One - self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- if not g.is_meromorphic(x, 0): # cannot be expanded
- return O(1) if n == 0 else O(sqrt(x))
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- if arg0 is S.NegativeOne:
- t = Dummy('t', positive=True)
- ser = acos(S.NegativeOne + t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.One + self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- if not g.is_meromorphic(x, 0): # cannot be expanded
- return O(1) if n == 0 else S.Pi + O(sqrt(x))
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if arg0 is S.ComplexInfinity:
- return res
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if im(cdir) < 0 and arg0.is_real and arg0 < S.NegativeOne:
- return 2*S.Pi - res
- elif im(cdir) > 0 and arg0.is_real and arg0 > S.One:
- return -res
- return res
- def _eval_rewrite_as_log(self, x, **kwargs):
- return S.Pi/2 + S.ImaginaryUnit*\
- log(S.ImaginaryUnit*x + sqrt(1 - x**2))
- def _eval_rewrite_as_asin(self, x, **kwargs):
- return S.Pi/2 - asin(x)
- def _eval_rewrite_as_atan(self, x, **kwargs):
- return atan(sqrt(1 - x**2)/x) + (S.Pi/2)*(1 - x*sqrt(1/x**2))
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return cos
- def _eval_rewrite_as_acot(self, arg, **kwargs):
- return S.Pi/2 - 2*acot((1 + sqrt(1 - arg**2))/arg)
- def _eval_rewrite_as_asec(self, arg, **kwargs):
- return asec(1/arg)
- def _eval_rewrite_as_acsc(self, arg, **kwargs):
- return S.Pi/2 - acsc(1/arg)
- def _eval_conjugate(self):
- z = self.args[0]
- r = self.func(self.args[0].conjugate())
- if z.is_extended_real is False:
- return r
- elif z.is_extended_real and (z + 1).is_nonnegative and (z - 1).is_nonpositive:
- return r
- class atan(InverseTrigonometricFunction):
- r"""
- The inverse tangent function.
- Returns the arc tangent of x (measured in radians).
- Explanation
- ===========
- ``atan(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, 0, 1, -1\}$ and for some instances when the
- result is a rational multiple of $\pi$ (see the eval class method).
- Examples
- ========
- >>> from sympy import atan, oo
- >>> atan(0)
- 0
- >>> atan(1)
- pi/4
- >>> atan(oo)
- pi/2
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acsc, acos, asec, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.23
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcTan
- """
- args: tTuple[Expr]
- _singularities = (S.ImaginaryUnit, -S.ImaginaryUnit)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return 1/(1 + self.args[0]**2)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if s.args[0].is_rational:
- return False
- else:
- return s.is_rational
- def _eval_is_positive(self):
- return self.args[0].is_extended_positive
- def _eval_is_nonnegative(self):
- return self.args[0].is_extended_nonnegative
- def _eval_is_zero(self):
- return self.args[0].is_zero
- def _eval_is_real(self):
- return self.args[0].is_extended_real
- @classmethod
- def eval(cls, arg):
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.Infinity:
- return S.Pi/2
- elif arg is S.NegativeInfinity:
- return -S.Pi/2
- elif arg.is_zero:
- return S.Zero
- elif arg is S.One:
- return S.Pi/4
- elif arg is S.NegativeOne:
- return -S.Pi/4
- if arg is S.ComplexInfinity:
- from sympy.calculus.accumulationbounds import AccumBounds
- return AccumBounds(-S.Pi/2, S.Pi/2)
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- if arg.is_number:
- atan_table = cls._atan_table()
- if arg in atan_table:
- return atan_table[arg]
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return S.ImaginaryUnit*atanh(i_coeff)
- if arg.is_zero:
- return S.Zero
- if isinstance(arg, tan):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= pi # restrict to [0,pi)
- if ang > pi/2: # restrict to [-pi/2,pi/2]
- ang -= pi
- return ang
- if isinstance(arg, cot): # atan(x) + acot(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- ang = pi/2 - acot(arg)
- if ang > pi/2: # restrict to [-pi/2,pi/2]
- ang -= pi
- return ang
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- return S.NegativeOne**((n - 1)//2)*x**n/n
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import (im, re)
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0.is_zero:
- return arg.as_leading_term(x)
- if x0 is S.ComplexInfinity:
- return acot(1/arg)._eval_as_leading_term(x, cdir=cdir)
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if re(cdir) < 0 and re(x0).is_zero and im(x0) > S.One:
- return self.func(x0) - S.Pi
- elif re(cdir) > 0 and re(x0).is_zero and im(x0) < S.NegativeOne:
- return self.func(x0) + S.Pi
- return self.func(x0)
- def _eval_nseries(self, x, n, logx, cdir=0): # atan
- from sympy.functions.elementary.complexes import (im, re)
- arg0 = self.args[0].subs(x, 0)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if arg0 is S.ComplexInfinity:
- if re(cdir) > 0:
- return res - S.Pi
- return res
- if re(cdir) < 0 and re(arg0).is_zero and im(arg0) > S.One:
- return res - S.Pi
- elif re(cdir) > 0 and re(arg0).is_zero and im(arg0) < S.NegativeOne:
- return res + S.Pi
- return res
- def _eval_rewrite_as_log(self, x, **kwargs):
- return S.ImaginaryUnit/2*(log(S.One - S.ImaginaryUnit*x)
- - log(S.One + S.ImaginaryUnit*x))
- def _eval_aseries(self, n, args0, x, logx):
- if args0[0] is S.Infinity:
- return (S.Pi/2 - atan(1/self.args[0]))._eval_nseries(x, n, logx)
- elif args0[0] is S.NegativeInfinity:
- return (-S.Pi/2 - atan(1/self.args[0]))._eval_nseries(x, n, logx)
- else:
- return super()._eval_aseries(n, args0, x, logx)
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return tan
- def _eval_rewrite_as_asin(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(S.Pi/2 - asin(1/sqrt(1 + arg**2)))
- def _eval_rewrite_as_acos(self, arg, **kwargs):
- return sqrt(arg**2)/arg*acos(1/sqrt(1 + arg**2))
- def _eval_rewrite_as_acot(self, arg, **kwargs):
- return acot(1/arg)
- def _eval_rewrite_as_asec(self, arg, **kwargs):
- return sqrt(arg**2)/arg*asec(sqrt(1 + arg**2))
- def _eval_rewrite_as_acsc(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(S.Pi/2 - acsc(sqrt(1 + arg**2)))
- class acot(InverseTrigonometricFunction):
- r"""
- The inverse cotangent function.
- Returns the arc cotangent of x (measured in radians).
- Explanation
- ===========
- ``acot(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, \tilde{\infty}, 0, 1, -1\}$
- and for some instances when the result is a rational multiple of $\pi$
- (see the eval class method).
- A purely imaginary argument will lead to an ``acoth`` expression.
- ``acot(x)`` has a branch cut along $(-i, i)$, hence it is discontinuous
- at 0. Its range for real $x$ is $(-\frac{\pi}{2}, \frac{\pi}{2}]$.
- Examples
- ========
- >>> from sympy import acot, sqrt
- >>> acot(0)
- pi/2
- >>> acot(1)
- pi/4
- >>> acot(sqrt(3) - 2)
- -5*pi/12
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acsc, acos, asec, atan, atan2
- References
- ==========
- .. [1] http://dlmf.nist.gov/4.23
- .. [2] http://functions.wolfram.com/ElementaryFunctions/ArcCot
- """
- _singularities = (S.ImaginaryUnit, -S.ImaginaryUnit)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return -1/(1 + self.args[0]**2)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_is_rational(self):
- s = self.func(*self.args)
- if s.func == self.func:
- if s.args[0].is_rational:
- return False
- else:
- return s.is_rational
- def _eval_is_positive(self):
- return self.args[0].is_nonnegative
- def _eval_is_negative(self):
- return self.args[0].is_negative
- def _eval_is_extended_real(self):
- return self.args[0].is_extended_real
- @classmethod
- def eval(cls, arg):
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.Infinity:
- return S.Zero
- elif arg is S.NegativeInfinity:
- return S.Zero
- elif arg.is_zero:
- return S.Pi/ 2
- elif arg is S.One:
- return S.Pi/4
- elif arg is S.NegativeOne:
- return -S.Pi/4
- if arg is S.ComplexInfinity:
- return S.Zero
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- if arg.is_number:
- atan_table = cls._atan_table()
- if arg in atan_table:
- ang = pi/2 - atan_table[arg]
- if ang > pi/2: # restrict to (-pi/2,pi/2]
- ang -= pi
- return ang
- i_coeff = arg.as_coefficient(S.ImaginaryUnit)
- if i_coeff is not None:
- return -S.ImaginaryUnit*acoth(i_coeff)
- if arg.is_zero:
- return S.Pi*S.Half
- if isinstance(arg, cot):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= pi # restrict to [0,pi)
- if ang > pi/2: # restrict to (-pi/2,pi/2]
- ang -= pi;
- return ang
- if isinstance(arg, tan): # atan(x) + acot(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- ang = pi/2 - atan(arg)
- if ang > pi/2: # restrict to (-pi/2,pi/2]
- ang -= pi
- return ang
- @staticmethod
- @cacheit
- def taylor_term(n, x, *previous_terms):
- if n == 0:
- return S.Pi/2 # FIX THIS
- elif n < 0 or n % 2 == 0:
- return S.Zero
- else:
- x = sympify(x)
- return S.NegativeOne**((n + 1)//2)*x**n/n
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import (im, re)
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0 is S.ComplexInfinity:
- return (1/arg).as_leading_term(x)
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if x0.is_zero:
- if re(cdir) < 0:
- return self.func(x0) - S.Pi
- return self.func(x0)
- if re(cdir) > 0 and re(x0).is_zero and im(x0) > S.Zero and im(x0) < S.One:
- return self.func(x0) + S.Pi
- if re(cdir) < 0 and re(x0).is_zero and im(x0) < S.Zero and im(x0) > S.NegativeOne:
- return self.func(x0) - S.Pi
- return self.func(x0)
- def _eval_nseries(self, x, n, logx, cdir=0): # acot
- from sympy.functions.elementary.complexes import (im, re)
- arg0 = self.args[0].subs(x, 0)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if arg0 is S.ComplexInfinity:
- return res
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if arg0.is_zero:
- if re(cdir) < 0:
- return res - S.Pi
- return res
- if re(cdir) > 0 and re(arg0).is_zero and im(arg0) > S.Zero and im(arg0) < S.One:
- return res + S.Pi
- if re(cdir) < 0 and re(arg0).is_zero and im(arg0) < S.Zero and im(arg0) > S.NegativeOne:
- return res - S.Pi
- return res
- def _eval_aseries(self, n, args0, x, logx):
- if args0[0] is S.Infinity:
- return (S.Pi/2 - acot(1/self.args[0]))._eval_nseries(x, n, logx)
- elif args0[0] is S.NegativeInfinity:
- return (S.Pi*Rational(3, 2) - acot(1/self.args[0]))._eval_nseries(x, n, logx)
- else:
- return super(atan, self)._eval_aseries(n, args0, x, logx)
- def _eval_rewrite_as_log(self, x, **kwargs):
- return S.ImaginaryUnit/2*(log(1 - S.ImaginaryUnit/x)
- - log(1 + S.ImaginaryUnit/x))
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return cot
- def _eval_rewrite_as_asin(self, arg, **kwargs):
- return (arg*sqrt(1/arg**2)*
- (S.Pi/2 - asin(sqrt(-arg**2)/sqrt(-arg**2 - 1))))
- def _eval_rewrite_as_acos(self, arg, **kwargs):
- return arg*sqrt(1/arg**2)*acos(sqrt(-arg**2)/sqrt(-arg**2 - 1))
- def _eval_rewrite_as_atan(self, arg, **kwargs):
- return atan(1/arg)
- def _eval_rewrite_as_asec(self, arg, **kwargs):
- return arg*sqrt(1/arg**2)*asec(sqrt((1 + arg**2)/arg**2))
- def _eval_rewrite_as_acsc(self, arg, **kwargs):
- return arg*sqrt(1/arg**2)*(S.Pi/2 - acsc(sqrt((1 + arg**2)/arg**2)))
- class asec(InverseTrigonometricFunction):
- r"""
- The inverse secant function.
- Returns the arc secant of x (measured in radians).
- Explanation
- ===========
- ``asec(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, 0, 1, -1\}$ and for some instances when the
- result is a rational multiple of $\pi$ (see the eval class method).
- ``asec(x)`` has branch cut in the interval $[-1, 1]$. For complex arguments,
- it can be defined [4]_ as
- .. math::
- \operatorname{sec^{-1}}(z) = -i\frac{\log\left(\sqrt{1 - z^2} + 1\right)}{z}
- At ``x = 0``, for positive branch cut, the limit evaluates to ``zoo``. For
- negative branch cut, the limit
- .. math::
- \lim_{z \to 0}-i\frac{\log\left(-\sqrt{1 - z^2} + 1\right)}{z}
- simplifies to :math:`-i\log\left(z/2 + O\left(z^3\right)\right)` which
- ultimately evaluates to ``zoo``.
- As ``acos(x) = asec(1/x)``, a similar argument can be given for
- ``acos(x)``.
- Examples
- ========
- >>> from sympy import asec, oo
- >>> asec(1)
- 0
- >>> asec(-1)
- pi
- >>> asec(0)
- zoo
- >>> asec(-oo)
- pi/2
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acsc, acos, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.23
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcSec
- .. [4] http://reference.wolfram.com/language/ref/ArcSec.html
- """
- @classmethod
- def eval(cls, arg):
- if arg.is_zero:
- return S.ComplexInfinity
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.One:
- return S.Zero
- elif arg is S.NegativeOne:
- return S.Pi
- if arg in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]:
- return S.Pi/2
- if arg.is_number:
- acsc_table = cls._acsc_table()
- if arg in acsc_table:
- return pi/2 - acsc_table[arg]
- elif -arg in acsc_table:
- return pi/2 + acsc_table[-arg]
- if isinstance(arg, sec):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= 2*pi # restrict to [0,2*pi)
- if ang > pi: # restrict to [0,pi]
- ang = 2*pi - ang
- return ang
- if isinstance(arg, csc): # asec(x) + acsc(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- return pi/2 - acsc(arg)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return 1/(self.args[0]**2*sqrt(1 - 1/self.args[0]**2))
- else:
- raise ArgumentIndexError(self, argindex)
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return sec
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import im
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0 == 1:
- return sqrt(2)*sqrt((arg - S.One).as_leading_term(x))
- if x0.is_zero:
- return S.ImaginaryUnit*log(arg.as_leading_term(x))
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if im(cdir) < 0 and x0.is_real and x0 > S.Zero and x0 < S.One:
- return -self.func(x0)
- elif im(cdir) > 0 and x0.is_real and x0 < S.Zero and x0 > S.NegativeOne:
- return 2*S.Pi - self.func(x0)
- return self.func(x0)
- def _eval_nseries(self, x, n, logx, cdir=0): # asec
- from sympy.functions.elementary.complexes import im
- from sympy.series.order import O
- arg0 = self.args[0].subs(x, 0)
- if arg0 is S.One:
- t = Dummy('t', positive=True)
- ser = asec(S.One + t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.NegativeOne + self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- if arg0 is S.NegativeOne:
- t = Dummy('t', positive=True)
- ser = asec(S.NegativeOne - t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.NegativeOne - self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if arg0 is S.ComplexInfinity:
- return res
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if im(cdir) < 0 and arg0.is_real and arg0 > S.Zero and arg0 < S.One:
- return -res
- elif im(cdir) > 0 and arg0.is_real and arg0 < S.Zero and arg0 > S.NegativeOne:
- return 2*S.Pi - res
- return res
- def _eval_is_extended_real(self):
- x = self.args[0]
- if x.is_extended_real is False:
- return False
- return fuzzy_or(((x - 1).is_nonnegative, (-x - 1).is_nonnegative))
- def _eval_rewrite_as_log(self, arg, **kwargs):
- return S.Pi/2 + S.ImaginaryUnit*log(S.ImaginaryUnit/arg + sqrt(1 - 1/arg**2))
- def _eval_rewrite_as_asin(self, arg, **kwargs):
- return S.Pi/2 - asin(1/arg)
- def _eval_rewrite_as_acos(self, arg, **kwargs):
- return acos(1/arg)
- def _eval_rewrite_as_atan(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(-S.Pi/2 + 2*atan(arg + sqrt(arg**2 - 1)))
- def _eval_rewrite_as_acot(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(-S.Pi/2 + 2*acot(arg - sqrt(arg**2 - 1)))
- def _eval_rewrite_as_acsc(self, arg, **kwargs):
- return S.Pi/2 - acsc(arg)
- class acsc(InverseTrigonometricFunction):
- r"""
- The inverse cosecant function.
- Returns the arc cosecant of x (measured in radians).
- Explanation
- ===========
- ``acsc(x)`` will evaluate automatically in the cases
- $x \in \{\infty, -\infty, 0, 1, -1\}$` and for some instances when the
- result is a rational multiple of $\pi$ (see the ``eval`` class method).
- Examples
- ========
- >>> from sympy import acsc, oo
- >>> acsc(1)
- pi/2
- >>> acsc(-1)
- -pi/2
- >>> acsc(oo)
- 0
- >>> acsc(-oo) == acsc(oo)
- True
- >>> acsc(0)
- zoo
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acos, asec, atan, acot, atan2
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] http://dlmf.nist.gov/4.23
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcCsc
- """
- @classmethod
- def eval(cls, arg):
- if arg.is_zero:
- return S.ComplexInfinity
- if arg.is_Number:
- if arg is S.NaN:
- return S.NaN
- elif arg is S.One:
- return S.Pi/2
- elif arg is S.NegativeOne:
- return -S.Pi/2
- if arg in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]:
- return S.Zero
- if arg.could_extract_minus_sign():
- return -cls(-arg)
- if arg.is_number:
- acsc_table = cls._acsc_table()
- if arg in acsc_table:
- return acsc_table[arg]
- if isinstance(arg, csc):
- ang = arg.args[0]
- if ang.is_comparable:
- ang %= 2*pi # restrict to [0,2*pi)
- if ang > pi: # restrict to (-pi,pi]
- ang = pi - ang
- # restrict to [-pi/2,pi/2]
- if ang > pi/2:
- ang = pi - ang
- if ang < -pi/2:
- ang = -pi - ang
- return ang
- if isinstance(arg, sec): # asec(x) + acsc(x) = pi/2
- ang = arg.args[0]
- if ang.is_comparable:
- return pi/2 - asec(arg)
- def fdiff(self, argindex=1):
- if argindex == 1:
- return -1/(self.args[0]**2*sqrt(1 - 1/self.args[0]**2))
- else:
- raise ArgumentIndexError(self, argindex)
- def inverse(self, argindex=1):
- """
- Returns the inverse of this function.
- """
- return csc
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- from sympy.functions.elementary.complexes import im
- arg = self.args[0]
- x0 = arg.subs(x, 0).cancel()
- if x0.is_zero:
- return S.ImaginaryUnit*log(arg.as_leading_term(x))
- if x0 is S.ComplexInfinity:
- return arg.as_leading_term(x)
- if cdir != 0:
- cdir = arg.dir(x, cdir)
- if im(cdir) < 0 and x0.is_real and x0 > S.Zero and x0 < S.One:
- return S.Pi - self.func(x0)
- elif im(cdir) > 0 and x0.is_real and x0 < S.Zero and x0 > S.NegativeOne:
- return -S.Pi - self.func(x0)
- return self.func(x0)
- def _eval_nseries(self, x, n, logx, cdir=0): # acsc
- from sympy.functions.elementary.complexes import im
- from sympy.series.order import O
- arg0 = self.args[0].subs(x, 0)
- if arg0 is S.One:
- t = Dummy('t', positive=True)
- ser = acsc(S.One + t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.NegativeOne + self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- if arg0 is S.NegativeOne:
- t = Dummy('t', positive=True)
- ser = acsc(S.NegativeOne - t**2).rewrite(log).nseries(t, 0, 2*n)
- arg1 = S.NegativeOne - self.args[0]
- f = arg1.as_leading_term(x)
- g = (arg1 - f)/ f
- res1 = sqrt(S.One + g)._eval_nseries(x, n=n, logx=logx)
- res = (res1.removeO()*sqrt(f)).expand()
- return ser.removeO().subs(t, res).expand().powsimp() + O(x**n, x)
- res = Function._eval_nseries(self, x, n=n, logx=logx)
- if arg0 is S.ComplexInfinity:
- return res
- if cdir != 0:
- cdir = self.args[0].dir(x, cdir)
- if im(cdir) < 0 and arg0.is_real and arg0 > S.Zero and arg0 < S.One:
- return S.Pi - res
- elif im(cdir) > 0 and arg0.is_real and arg0 < S.Zero and arg0 > S.NegativeOne:
- return -S.Pi - res
- return res
- def _eval_rewrite_as_log(self, arg, **kwargs):
- return -S.ImaginaryUnit*log(S.ImaginaryUnit/arg + sqrt(1 - 1/arg**2))
- def _eval_rewrite_as_asin(self, arg, **kwargs):
- return asin(1/arg)
- def _eval_rewrite_as_acos(self, arg, **kwargs):
- return S.Pi/2 - acos(1/arg)
- def _eval_rewrite_as_atan(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(S.Pi/2 - atan(sqrt(arg**2 - 1)))
- def _eval_rewrite_as_acot(self, arg, **kwargs):
- return sqrt(arg**2)/arg*(S.Pi/2 - acot(1/sqrt(arg**2 - 1)))
- def _eval_rewrite_as_asec(self, arg, **kwargs):
- return S.Pi/2 - asec(arg)
- class atan2(InverseTrigonometricFunction):
- r"""
- The function ``atan2(y, x)`` computes `\operatorname{atan}(y/x)` taking
- two arguments `y` and `x`. Signs of both `y` and `x` are considered to
- determine the appropriate quadrant of `\operatorname{atan}(y/x)`.
- The range is `(-\pi, \pi]`. The complete definition reads as follows:
- .. math::
- \operatorname{atan2}(y, x) =
- \begin{cases}
- \arctan\left(\frac y x\right) & \qquad x > 0 \\
- \arctan\left(\frac y x\right) + \pi& \qquad y \ge 0, x < 0 \\
- \arctan\left(\frac y x\right) - \pi& \qquad y < 0, x < 0 \\
- +\frac{\pi}{2} & \qquad y > 0, x = 0 \\
- -\frac{\pi}{2} & \qquad y < 0, x = 0 \\
- \text{undefined} & \qquad y = 0, x = 0
- \end{cases}
- Attention: Note the role reversal of both arguments. The `y`-coordinate
- is the first argument and the `x`-coordinate the second.
- If either `x` or `y` is complex:
- .. math::
- \operatorname{atan2}(y, x) =
- -i\log\left(\frac{x + iy}{\sqrt{x^2 + y^2}}\right)
- Examples
- ========
- Going counter-clock wise around the origin we find the
- following angles:
- >>> from sympy import atan2
- >>> atan2(0, 1)
- 0
- >>> atan2(1, 1)
- pi/4
- >>> atan2(1, 0)
- pi/2
- >>> atan2(1, -1)
- 3*pi/4
- >>> atan2(0, -1)
- pi
- >>> atan2(-1, -1)
- -3*pi/4
- >>> atan2(-1, 0)
- -pi/2
- >>> atan2(-1, 1)
- -pi/4
- which are all correct. Compare this to the results of the ordinary
- `\operatorname{atan}` function for the point `(x, y) = (-1, 1)`
- >>> from sympy import atan, S
- >>> atan(S(1)/-1)
- -pi/4
- >>> atan2(1, -1)
- 3*pi/4
- where only the `\operatorname{atan2}` function reurns what we expect.
- We can differentiate the function with respect to both arguments:
- >>> from sympy import diff
- >>> from sympy.abc import x, y
- >>> diff(atan2(y, x), x)
- -y/(x**2 + y**2)
- >>> diff(atan2(y, x), y)
- x/(x**2 + y**2)
- We can express the `\operatorname{atan2}` function in terms of
- complex logarithms:
- >>> from sympy import log
- >>> atan2(y, x).rewrite(log)
- -I*log((x + I*y)/sqrt(x**2 + y**2))
- and in terms of `\operatorname(atan)`:
- >>> from sympy import atan
- >>> atan2(y, x).rewrite(atan)
- Piecewise((2*atan(y/(x + sqrt(x**2 + y**2))), Ne(y, 0)), (pi, re(x) < 0), (0, Ne(x, 0)), (nan, True))
- but note that this form is undefined on the negative real axis.
- See Also
- ========
- sin, csc, cos, sec, tan, cot
- asin, acsc, acos, asec, atan, acot
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- .. [2] https://en.wikipedia.org/wiki/Atan2
- .. [3] http://functions.wolfram.com/ElementaryFunctions/ArcTan2
- """
- @classmethod
- def eval(cls, y, x):
- from sympy.functions.elementary.complexes import (im, re)
- from sympy.functions.special.delta_functions import Heaviside
- if x is S.NegativeInfinity:
- if y.is_zero:
- # Special case y = 0 because we define Heaviside(0) = 1/2
- return S.Pi
- return 2*S.Pi*(Heaviside(re(y))) - S.Pi
- elif x is S.Infinity:
- return S.Zero
- elif x.is_imaginary and y.is_imaginary and x.is_number and y.is_number:
- x = im(x)
- y = im(y)
- if x.is_extended_real and y.is_extended_real:
- if x.is_positive:
- return atan(y/x)
- elif x.is_negative:
- if y.is_negative:
- return atan(y/x) - S.Pi
- elif y.is_nonnegative:
- return atan(y/x) + S.Pi
- elif x.is_zero:
- if y.is_positive:
- return S.Pi/2
- elif y.is_negative:
- return -S.Pi/2
- elif y.is_zero:
- return S.NaN
- if y.is_zero:
- if x.is_extended_nonzero:
- return S.Pi*(S.One - Heaviside(x))
- if x.is_number:
- return Piecewise((S.Pi, re(x) < 0),
- (0, Ne(x, 0)),
- (S.NaN, True))
- if x.is_number and y.is_number:
- return -S.ImaginaryUnit*log(
- (x + S.ImaginaryUnit*y)/sqrt(x**2 + y**2))
- def _eval_rewrite_as_log(self, y, x, **kwargs):
- return -S.ImaginaryUnit*log((x + S.ImaginaryUnit*y)/sqrt(x**2 + y**2))
- def _eval_rewrite_as_atan(self, y, x, **kwargs):
- from sympy.functions.elementary.complexes import re
- return Piecewise((2*atan(y/(x + sqrt(x**2 + y**2))), Ne(y, 0)),
- (pi, re(x) < 0),
- (0, Ne(x, 0)),
- (S.NaN, True))
- def _eval_rewrite_as_arg(self, y, x, **kwargs):
- from sympy.functions.elementary.complexes import arg
- if x.is_extended_real and y.is_extended_real:
- return arg(x + y*S.ImaginaryUnit)
- n = x + S.ImaginaryUnit*y
- d = x**2 + y**2
- return arg(n/sqrt(d)) - S.ImaginaryUnit*log(abs(n)/sqrt(abs(d)))
- def _eval_is_extended_real(self):
- return self.args[0].is_extended_real and self.args[1].is_extended_real
- def _eval_conjugate(self):
- return self.func(self.args[0].conjugate(), self.args[1].conjugate())
- def fdiff(self, argindex):
- y, x = self.args
- if argindex == 1:
- # Diff wrt y
- return x/(x**2 + y**2)
- elif argindex == 2:
- # Diff wrt x
- return -y/(x**2 + y**2)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_evalf(self, prec):
- y, x = self.args
- if x.is_extended_real and y.is_extended_real:
- return super()._eval_evalf(prec)
|