1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385 |
- """
- There are three types of functions implemented in SymPy:
- 1) defined functions (in the sense that they can be evaluated) like
- exp or sin; they have a name and a body:
- f = exp
- 2) undefined function which have a name but no body. Undefined
- functions can be defined using a Function class as follows:
- f = Function('f')
- (the result will be a Function instance)
- 3) anonymous function (or lambda function) which have a body (defined
- with dummy variables) but have no name:
- f = Lambda(x, exp(x)*x)
- f = Lambda((x, y), exp(x)*y)
- The fourth type of functions are composites, like (sin + cos)(x); these work in
- SymPy core, but are not yet part of SymPy.
- Examples
- ========
- >>> import sympy
- >>> f = sympy.Function("f")
- >>> from sympy.abc import x
- >>> f(x)
- f(x)
- >>> print(sympy.srepr(f(x).func))
- Function('f')
- >>> f(x).args
- (x,)
- """
- from __future__ import annotations
- from typing import Any
- from collections.abc import Iterable
- from .add import Add
- from .basic import Basic, _atomic
- from .cache import cacheit
- from .containers import Tuple, Dict
- from .decorators import _sympifyit
- from .evalf import pure_complex
- from .expr import Expr, AtomicExpr
- from .logic import fuzzy_and, fuzzy_or, fuzzy_not, FuzzyBool
- from .mul import Mul
- from .numbers import Rational, Float, Integer
- from .operations import LatticeOp
- from .parameters import global_parameters
- from .rules import Transform
- from .singleton import S
- from .sympify import sympify, _sympify
- from .sorting import default_sort_key, ordered
- from sympy.utilities.exceptions import (sympy_deprecation_warning,
- SymPyDeprecationWarning, ignore_warnings)
- from sympy.utilities.iterables import (has_dups, sift, iterable,
- is_sequence, uniq, topological_sort)
- from sympy.utilities.lambdify import MPMATH_TRANSLATIONS
- from sympy.utilities.misc import as_int, filldedent, func_name
- import mpmath
- from mpmath.libmp.libmpf import prec_to_dps
- import inspect
- from collections import Counter
- def _coeff_isneg(a):
- """Return True if the leading Number is negative.
- Examples
- ========
- >>> from sympy.core.function import _coeff_isneg
- >>> from sympy import S, Symbol, oo, pi
- >>> _coeff_isneg(-3*pi)
- True
- >>> _coeff_isneg(S(3))
- False
- >>> _coeff_isneg(-oo)
- True
- >>> _coeff_isneg(Symbol('n', negative=True)) # coeff is 1
- False
- For matrix expressions:
- >>> from sympy import MatrixSymbol, sqrt
- >>> A = MatrixSymbol("A", 3, 3)
- >>> _coeff_isneg(-sqrt(2)*A)
- True
- >>> _coeff_isneg(sqrt(2)*A)
- False
- """
- if a.is_MatMul:
- a = a.args[0]
- if a.is_Mul:
- a = a.args[0]
- return a.is_Number and a.is_extended_negative
- class PoleError(Exception):
- pass
- class ArgumentIndexError(ValueError):
- def __str__(self):
- return ("Invalid operation with argument number %s for Function %s" %
- (self.args[1], self.args[0]))
- class BadSignatureError(TypeError):
- '''Raised when a Lambda is created with an invalid signature'''
- pass
- class BadArgumentsError(TypeError):
- '''Raised when a Lambda is called with an incorrect number of arguments'''
- pass
- # Python 3 version that does not raise a Deprecation warning
- def arity(cls):
- """Return the arity of the function if it is known, else None.
- Explanation
- ===========
- When default values are specified for some arguments, they are
- optional and the arity is reported as a tuple of possible values.
- Examples
- ========
- >>> from sympy import arity, log
- >>> arity(lambda x: x)
- 1
- >>> arity(log)
- (1, 2)
- >>> arity(lambda *x: sum(x)) is None
- True
- """
- eval_ = getattr(cls, 'eval', cls)
- parameters = inspect.signature(eval_).parameters.items()
- if [p for _, p in parameters if p.kind == p.VAR_POSITIONAL]:
- return
- p_or_k = [p for _, p in parameters if p.kind == p.POSITIONAL_OR_KEYWORD]
- # how many have no default and how many have a default value
- no, yes = map(len, sift(p_or_k,
- lambda p:p.default == p.empty, binary=True))
- return no if not yes else tuple(range(no, no + yes + 1))
- class FunctionClass(type):
- """
- Base class for function classes. FunctionClass is a subclass of type.
- Use Function('<function name>' [ , signature ]) to create
- undefined function classes.
- """
- _new = type.__new__
- def __init__(cls, *args, **kwargs):
- # honor kwarg value or class-defined value before using
- # the number of arguments in the eval function (if present)
- nargs = kwargs.pop('nargs', cls.__dict__.get('nargs', arity(cls)))
- if nargs is None and 'nargs' not in cls.__dict__:
- for supcls in cls.__mro__:
- if hasattr(supcls, '_nargs'):
- nargs = supcls._nargs
- break
- else:
- continue
- # Canonicalize nargs here; change to set in nargs.
- if is_sequence(nargs):
- if not nargs:
- raise ValueError(filldedent('''
- Incorrectly specified nargs as %s:
- if there are no arguments, it should be
- `nargs = 0`;
- if there are any number of arguments,
- it should be
- `nargs = None`''' % str(nargs)))
- nargs = tuple(ordered(set(nargs)))
- elif nargs is not None:
- nargs = (as_int(nargs),)
- cls._nargs = nargs
- # When __init__ is called from UndefinedFunction it is called with
- # just one arg but when it is called from subclassing Function it is
- # called with the usual (name, bases, namespace) type() signature.
- if len(args) == 3:
- namespace = args[2]
- if 'eval' in namespace and not isinstance(namespace['eval'], classmethod):
- raise TypeError("eval on Function subclasses should be a class method (defined with @classmethod)")
- @property
- def __signature__(self):
- """
- Allow Python 3's inspect.signature to give a useful signature for
- Function subclasses.
- """
- # Python 3 only, but backports (like the one in IPython) still might
- # call this.
- try:
- from inspect import signature
- except ImportError:
- return None
- # TODO: Look at nargs
- return signature(self.eval)
- @property
- def free_symbols(self):
- return set()
- @property
- def xreplace(self):
- # Function needs args so we define a property that returns
- # a function that takes args...and then use that function
- # to return the right value
- return lambda rule, **_: rule.get(self, self)
- @property
- def nargs(self):
- """Return a set of the allowed number of arguments for the function.
- Examples
- ========
- >>> from sympy import Function
- >>> f = Function('f')
- If the function can take any number of arguments, the set of whole
- numbers is returned:
- >>> Function('f').nargs
- Naturals0
- If the function was initialized to accept one or more arguments, a
- corresponding set will be returned:
- >>> Function('f', nargs=1).nargs
- {1}
- >>> Function('f', nargs=(2, 1)).nargs
- {1, 2}
- The undefined function, after application, also has the nargs
- attribute; the actual number of arguments is always available by
- checking the ``args`` attribute:
- >>> f = Function('f')
- >>> f(1).nargs
- Naturals0
- >>> len(f(1).args)
- 1
- """
- from sympy.sets.sets import FiniteSet
- # XXX it would be nice to handle this in __init__ but there are import
- # problems with trying to import FiniteSet there
- return FiniteSet(*self._nargs) if self._nargs else S.Naturals0
- def _valid_nargs(self, n : int) -> bool:
- """ Return True if the specified integer is a valid number of arguments
- The number of arguments n is guaranteed to be an integer and positive
- """
- if self._nargs:
- return n in self._nargs
- nargs = self.nargs
- return nargs is S.Naturals0 or n in nargs
- def __repr__(cls):
- return cls.__name__
- class Application(Basic, metaclass=FunctionClass):
- """
- Base class for applied functions.
- Explanation
- ===========
- Instances of Application represent the result of applying an application of
- any type to any object.
- """
- is_Function = True
- @cacheit
- def __new__(cls, *args, **options):
- from sympy.sets.fancysets import Naturals0
- from sympy.sets.sets import FiniteSet
- args = list(map(sympify, args))
- evaluate = options.pop('evaluate', global_parameters.evaluate)
- # WildFunction (and anything else like it) may have nargs defined
- # and we throw that value away here
- options.pop('nargs', None)
- if options:
- raise ValueError("Unknown options: %s" % options)
- if evaluate:
- evaluated = cls.eval(*args)
- if evaluated is not None:
- return evaluated
- obj = super().__new__(cls, *args, **options)
- # make nargs uniform here
- sentinel = object()
- objnargs = getattr(obj, "nargs", sentinel)
- if objnargs is not sentinel:
- # things passing through here:
- # - functions subclassed from Function (e.g. myfunc(1).nargs)
- # - functions like cos(1).nargs
- # - AppliedUndef with given nargs like Function('f', nargs=1)(1).nargs
- # Canonicalize nargs here
- if is_sequence(objnargs):
- nargs = tuple(ordered(set(objnargs)))
- elif objnargs is not None:
- nargs = (as_int(objnargs),)
- else:
- nargs = None
- else:
- # things passing through here:
- # - WildFunction('f').nargs
- # - AppliedUndef with no nargs like Function('f')(1).nargs
- nargs = obj._nargs # note the underscore here
- # convert to FiniteSet
- obj.nargs = FiniteSet(*nargs) if nargs else Naturals0()
- return obj
- @classmethod
- def eval(cls, *args):
- """
- Returns a canonical form of cls applied to arguments args.
- Explanation
- ===========
- The ``eval()`` method is called when the class ``cls`` is about to be
- instantiated and it should return either some simplified instance
- (possible of some other class), or if the class ``cls`` should be
- unmodified, return None.
- Examples of ``eval()`` for the function "sign"
- .. code-block:: python
- @classmethod
- def eval(cls, arg):
- if arg is S.NaN:
- return S.NaN
- if arg.is_zero: return S.Zero
- if arg.is_positive: return S.One
- if arg.is_negative: return S.NegativeOne
- if isinstance(arg, Mul):
- coeff, terms = arg.as_coeff_Mul(rational=True)
- if coeff is not S.One:
- return cls(coeff) * cls(terms)
- """
- return
- @property
- def func(self):
- return self.__class__
- def _eval_subs(self, old, new):
- if (old.is_Function and new.is_Function and
- callable(old) and callable(new) and
- old == self.func and len(self.args) in new.nargs):
- return new(*[i._subs(old, new) for i in self.args])
- class Function(Application, Expr):
- r"""
- Base class for applied mathematical functions.
- It also serves as a constructor for undefined function classes.
- See the :ref:`custom-functions` guide for details on how to subclass
- ``Function`` and what methods can be defined.
- Examples
- ========
- **Undefined Functions**
- To create an undefined function, pass a string of the function name to
- ``Function``.
- >>> from sympy import Function, Symbol
- >>> x = Symbol('x')
- >>> f = Function('f')
- >>> g = Function('g')(x)
- >>> f
- f
- >>> f(x)
- f(x)
- >>> g
- g(x)
- >>> f(x).diff(x)
- Derivative(f(x), x)
- >>> g.diff(x)
- Derivative(g(x), x)
- Assumptions can be passed to ``Function`` the same as with a
- :class:`~.Symbol`. Alternatively, you can use a ``Symbol`` with
- assumptions for the function name and the function will inherit the name
- and assumptions associated with the ``Symbol``:
- >>> f_real = Function('f', real=True)
- >>> f_real(x).is_real
- True
- >>> f_real_inherit = Function(Symbol('f', real=True))
- >>> f_real_inherit(x).is_real
- True
- Note that assumptions on a function are unrelated to the assumptions on
- the variables it is called on. If you want to add a relationship, subclass
- ``Function`` and define custom assumptions handler methods. See the
- :ref:`custom-functions-assumptions` section of the :ref:`custom-functions`
- guide for more details.
- **Custom Function Subclasses**
- The :ref:`custom-functions` guide has several
- :ref:`custom-functions-complete-examples` of how to subclass ``Function``
- to create a custom function.
- """
- @property
- def _diff_wrt(self):
- return False
- @cacheit
- def __new__(cls, *args, **options):
- # Handle calls like Function('f')
- if cls is Function:
- return UndefinedFunction(*args, **options)
- n = len(args)
- if not cls._valid_nargs(n):
- # XXX: exception message must be in exactly this format to
- # make it work with NumPy's functions like vectorize(). See,
- # for example, https://github.com/numpy/numpy/issues/1697.
- # The ideal solution would be just to attach metadata to
- # the exception and change NumPy to take advantage of this.
- temp = ('%(name)s takes %(qual)s %(args)s '
- 'argument%(plural)s (%(given)s given)')
- raise TypeError(temp % {
- 'name': cls,
- 'qual': 'exactly' if len(cls.nargs) == 1 else 'at least',
- 'args': min(cls.nargs),
- 'plural': 's'*(min(cls.nargs) != 1),
- 'given': n})
- evaluate = options.get('evaluate', global_parameters.evaluate)
- result = super().__new__(cls, *args, **options)
- if evaluate and isinstance(result, cls) and result.args:
- _should_evalf = [cls._should_evalf(a) for a in result.args]
- pr2 = min(_should_evalf)
- if pr2 > 0:
- pr = max(_should_evalf)
- result = result.evalf(prec_to_dps(pr))
- return _sympify(result)
- @classmethod
- def _should_evalf(cls, arg):
- """
- Decide if the function should automatically evalf().
- Explanation
- ===========
- By default (in this implementation), this happens if (and only if) the
- ARG is a floating point number (including complex numbers).
- This function is used by __new__.
- Returns the precision to evalf to, or -1 if it should not evalf.
- """
- if arg.is_Float:
- return arg._prec
- if not arg.is_Add:
- return -1
- m = pure_complex(arg)
- if m is None:
- return -1
- # the elements of m are of type Number, so have a _prec
- return max(m[0]._prec, m[1]._prec)
- @classmethod
- def class_key(cls):
- from sympy.sets.fancysets import Naturals0
- funcs = {
- 'exp': 10,
- 'log': 11,
- 'sin': 20,
- 'cos': 21,
- 'tan': 22,
- 'cot': 23,
- 'sinh': 30,
- 'cosh': 31,
- 'tanh': 32,
- 'coth': 33,
- 'conjugate': 40,
- 're': 41,
- 'im': 42,
- 'arg': 43,
- }
- name = cls.__name__
- try:
- i = funcs[name]
- except KeyError:
- i = 0 if isinstance(cls.nargs, Naturals0) else 10000
- return 4, i, name
- def _eval_evalf(self, prec):
- def _get_mpmath_func(fname):
- """Lookup mpmath function based on name"""
- if isinstance(self, AppliedUndef):
- # Shouldn't lookup in mpmath but might have ._imp_
- return None
- if not hasattr(mpmath, fname):
- fname = MPMATH_TRANSLATIONS.get(fname, None)
- if fname is None:
- return None
- return getattr(mpmath, fname)
- _eval_mpmath = getattr(self, '_eval_mpmath', None)
- if _eval_mpmath is None:
- func = _get_mpmath_func(self.func.__name__)
- args = self.args
- else:
- func, args = _eval_mpmath()
- # Fall-back evaluation
- if func is None:
- imp = getattr(self, '_imp_', None)
- if imp is None:
- return None
- try:
- return Float(imp(*[i.evalf(prec) for i in self.args]), prec)
- except (TypeError, ValueError):
- return None
- # Convert all args to mpf or mpc
- # Convert the arguments to *higher* precision than requested for the
- # final result.
- # XXX + 5 is a guess, it is similar to what is used in evalf.py. Should
- # we be more intelligent about it?
- try:
- args = [arg._to_mpmath(prec + 5) for arg in args]
- def bad(m):
- from mpmath import mpf, mpc
- # the precision of an mpf value is the last element
- # if that is 1 (and m[1] is not 1 which would indicate a
- # power of 2), then the eval failed; so check that none of
- # the arguments failed to compute to a finite precision.
- # Note: An mpc value has two parts, the re and imag tuple;
- # check each of those parts, too. Anything else is allowed to
- # pass
- if isinstance(m, mpf):
- m = m._mpf_
- return m[1] !=1 and m[-1] == 1
- elif isinstance(m, mpc):
- m, n = m._mpc_
- return m[1] !=1 and m[-1] == 1 and \
- n[1] !=1 and n[-1] == 1
- else:
- return False
- if any(bad(a) for a in args):
- raise ValueError # one or more args failed to compute with significance
- except ValueError:
- return
- with mpmath.workprec(prec):
- v = func(*args)
- return Expr._from_mpmath(v, prec)
- def _eval_derivative(self, s):
- # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s)
- i = 0
- l = []
- for a in self.args:
- i += 1
- da = a.diff(s)
- if da.is_zero:
- continue
- try:
- df = self.fdiff(i)
- except ArgumentIndexError:
- df = Function.fdiff(self, i)
- l.append(df * da)
- return Add(*l)
- def _eval_is_commutative(self):
- return fuzzy_and(a.is_commutative for a in self.args)
- def _eval_is_meromorphic(self, x, a):
- if not self.args:
- return True
- if any(arg.has(x) for arg in self.args[1:]):
- return False
- arg = self.args[0]
- if not arg._eval_is_meromorphic(x, a):
- return None
- return fuzzy_not(type(self).is_singular(arg.subs(x, a)))
- _singularities: FuzzyBool | tuple[Expr, ...] = None
- @classmethod
- def is_singular(cls, a):
- """
- Tests whether the argument is an essential singularity
- or a branch point, or the functions is non-holomorphic.
- """
- ss = cls._singularities
- if ss in (True, None, False):
- return ss
- return fuzzy_or(a.is_infinite if s is S.ComplexInfinity
- else (a - s).is_zero for s in ss)
- def as_base_exp(self):
- """
- Returns the method as the 2-tuple (base, exponent).
- """
- return self, S.One
- def _eval_aseries(self, n, args0, x, logx):
- """
- Compute an asymptotic expansion around args0, in terms of self.args.
- This function is only used internally by _eval_nseries and should not
- be called directly; derived classes can overwrite this to implement
- asymptotic expansions.
- """
- raise PoleError(filldedent('''
- Asymptotic expansion of %s around %s is
- not implemented.''' % (type(self), args0)))
- def _eval_nseries(self, x, n, logx, cdir=0):
- """
- This function does compute series for multivariate functions,
- but the expansion is always in terms of *one* variable.
- Examples
- ========
- >>> from sympy import atan2
- >>> from sympy.abc import x, y
- >>> atan2(x, y).series(x, n=2)
- atan2(0, y) + x/y + O(x**2)
- >>> atan2(x, y).series(y, n=2)
- -y/x + atan2(x, 0) + O(y**2)
- This function also computes asymptotic expansions, if necessary
- and possible:
- >>> from sympy import loggamma
- >>> loggamma(1/x)._eval_nseries(x,0,None)
- -1/x - log(x)/x + log(x)/2 + O(1)
- """
- from .symbol import uniquely_named_symbol
- from sympy.series.order import Order
- from sympy.sets.sets import FiniteSet
- args = self.args
- args0 = [t.limit(x, 0) for t in args]
- if any(t.is_finite is False for t in args0):
- from .numbers import oo, zoo, nan
- a = [t.as_leading_term(x, logx=logx) for t in args]
- a0 = [t.limit(x, 0) for t in a]
- if any(t.has(oo, -oo, zoo, nan) for t in a0):
- return self._eval_aseries(n, args0, x, logx)
- # Careful: the argument goes to oo, but only logarithmically so. We
- # are supposed to do a power series expansion "around the
- # logarithmic term". e.g.
- # f(1+x+log(x))
- # -> f(1+logx) + x*f'(1+logx) + O(x**2)
- # where 'logx' is given in the argument
- a = [t._eval_nseries(x, n, logx) for t in args]
- z = [r - r0 for (r, r0) in zip(a, a0)]
- p = [Dummy() for _ in z]
- q = []
- v = None
- for ai, zi, pi in zip(a0, z, p):
- if zi.has(x):
- if v is not None:
- raise NotImplementedError
- q.append(ai + pi)
- v = pi
- else:
- q.append(ai)
- e1 = self.func(*q)
- if v is None:
- return e1
- s = e1._eval_nseries(v, n, logx)
- o = s.getO()
- s = s.removeO()
- s = s.subs(v, zi).expand() + Order(o.expr.subs(v, zi), x)
- return s
- if (self.func.nargs is S.Naturals0
- or (self.func.nargs == FiniteSet(1) and args0[0])
- or any(c > 1 for c in self.func.nargs)):
- e = self
- e1 = e.expand()
- if e == e1:
- #for example when e = sin(x+1) or e = sin(cos(x))
- #let's try the general algorithm
- if len(e.args) == 1:
- # issue 14411
- e = e.func(e.args[0].cancel())
- term = e.subs(x, S.Zero)
- if term.is_finite is False or term is S.NaN:
- raise PoleError("Cannot expand %s around 0" % (self))
- series = term
- fact = S.One
- _x = uniquely_named_symbol('xi', self)
- e = e.subs(x, _x)
- for i in range(1, n):
- fact *= Rational(i)
- e = e.diff(_x)
- subs = e.subs(_x, S.Zero)
- if subs is S.NaN:
- # try to evaluate a limit if we have to
- subs = e.limit(_x, S.Zero)
- if subs.is_finite is False:
- raise PoleError("Cannot expand %s around 0" % (self))
- term = subs*(x**i)/fact
- term = term.expand()
- series += term
- return series + Order(x**n, x)
- return e1.nseries(x, n=n, logx=logx)
- arg = self.args[0]
- l = []
- g = None
- # try to predict a number of terms needed
- nterms = n + 2
- cf = Order(arg.as_leading_term(x), x).getn()
- if cf != 0:
- nterms = (n/cf).ceiling()
- for i in range(nterms):
- g = self.taylor_term(i, arg, g)
- g = g.nseries(x, n=n, logx=logx)
- l.append(g)
- return Add(*l) + Order(x**n, x)
- def fdiff(self, argindex=1):
- """
- Returns the first derivative of the function.
- """
- if not (1 <= argindex <= len(self.args)):
- raise ArgumentIndexError(self, argindex)
- ix = argindex - 1
- A = self.args[ix]
- if A._diff_wrt:
- if len(self.args) == 1 or not A.is_Symbol:
- return _derivative_dispatch(self, A)
- for i, v in enumerate(self.args):
- if i != ix and A in v.free_symbols:
- # it can't be in any other argument's free symbols
- # issue 8510
- break
- else:
- return _derivative_dispatch(self, A)
- # See issue 4624 and issue 4719, 5600 and 8510
- D = Dummy('xi_%i' % argindex, dummy_index=hash(A))
- args = self.args[:ix] + (D,) + self.args[ix + 1:]
- return Subs(Derivative(self.func(*args), D), D, A)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- """Stub that should be overridden by new Functions to return
- the first non-zero term in a series if ever an x-dependent
- argument whose leading term vanishes as x -> 0 might be encountered.
- See, for example, cos._eval_as_leading_term.
- """
- from sympy.series.order import Order
- args = [a.as_leading_term(x, logx=logx) for a in self.args]
- o = Order(1, x)
- if any(x in a.free_symbols and o.contains(a) for a in args):
- # Whereas x and any finite number are contained in O(1, x),
- # expressions like 1/x are not. If any arg simplified to a
- # vanishing expression as x -> 0 (like x or x**2, but not
- # 3, 1/x, etc...) then the _eval_as_leading_term is needed
- # to supply the first non-zero term of the series,
- #
- # e.g. expression leading term
- # ---------- ------------
- # cos(1/x) cos(1/x)
- # cos(cos(x)) cos(1)
- # cos(x) 1 <- _eval_as_leading_term needed
- # sin(x) x <- _eval_as_leading_term needed
- #
- raise NotImplementedError(
- '%s has no _eval_as_leading_term routine' % self.func)
- else:
- return self.func(*args)
- class AppliedUndef(Function):
- """
- Base class for expressions resulting from the application of an undefined
- function.
- """
- is_number = False
- def __new__(cls, *args, **options):
- args = list(map(sympify, args))
- u = [a.name for a in args if isinstance(a, UndefinedFunction)]
- if u:
- raise TypeError('Invalid argument: expecting an expression, not UndefinedFunction%s: %s' % (
- 's'*(len(u) > 1), ', '.join(u)))
- obj = super().__new__(cls, *args, **options)
- return obj
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- return self
- @property
- def _diff_wrt(self):
- """
- Allow derivatives wrt to undefined functions.
- Examples
- ========
- >>> from sympy import Function, Symbol
- >>> f = Function('f')
- >>> x = Symbol('x')
- >>> f(x)._diff_wrt
- True
- >>> f(x).diff(x)
- Derivative(f(x), x)
- """
- return True
- class UndefSageHelper:
- """
- Helper to facilitate Sage conversion.
- """
- def __get__(self, ins, typ):
- import sage.all as sage
- if ins is None:
- return lambda: sage.function(typ.__name__)
- else:
- args = [arg._sage_() for arg in ins.args]
- return lambda : sage.function(ins.__class__.__name__)(*args)
- _undef_sage_helper = UndefSageHelper()
- class UndefinedFunction(FunctionClass):
- """
- The (meta)class of undefined functions.
- """
- def __new__(mcl, name, bases=(AppliedUndef,), __dict__=None, **kwargs):
- from .symbol import _filter_assumptions
- # Allow Function('f', real=True)
- # and/or Function(Symbol('f', real=True))
- assumptions, kwargs = _filter_assumptions(kwargs)
- if isinstance(name, Symbol):
- assumptions = name._merge(assumptions)
- name = name.name
- elif not isinstance(name, str):
- raise TypeError('expecting string or Symbol for name')
- else:
- commutative = assumptions.get('commutative', None)
- assumptions = Symbol(name, **assumptions).assumptions0
- if commutative is None:
- assumptions.pop('commutative')
- __dict__ = __dict__ or {}
- # put the `is_*` for into __dict__
- __dict__.update({'is_%s' % k: v for k, v in assumptions.items()})
- # You can add other attributes, although they do have to be hashable
- # (but seriously, if you want to add anything other than assumptions,
- # just subclass Function)
- __dict__.update(kwargs)
- # add back the sanitized assumptions without the is_ prefix
- kwargs.update(assumptions)
- # Save these for __eq__
- __dict__.update({'_kwargs': kwargs})
- # do this for pickling
- __dict__['__module__'] = None
- obj = super().__new__(mcl, name, bases, __dict__)
- obj.name = name
- obj._sage_ = _undef_sage_helper
- return obj
- def __instancecheck__(cls, instance):
- return cls in type(instance).__mro__
- _kwargs: dict[str, bool | None] = {}
- def __hash__(self):
- return hash((self.class_key(), frozenset(self._kwargs.items())))
- def __eq__(self, other):
- return (isinstance(other, self.__class__) and
- self.class_key() == other.class_key() and
- self._kwargs == other._kwargs)
- def __ne__(self, other):
- return not self == other
- @property
- def _diff_wrt(self):
- return False
- # XXX: The type: ignore on WildFunction is because mypy complains:
- #
- # sympy/core/function.py:939: error: Cannot determine type of 'sort_key' in
- # base class 'Expr'
- #
- # Somehow this is because of the @cacheit decorator but it is not clear how to
- # fix it.
- class WildFunction(Function, AtomicExpr): # type: ignore
- """
- A WildFunction function matches any function (with its arguments).
- Examples
- ========
- >>> from sympy import WildFunction, Function, cos
- >>> from sympy.abc import x, y
- >>> F = WildFunction('F')
- >>> f = Function('f')
- >>> F.nargs
- Naturals0
- >>> x.match(F)
- >>> F.match(F)
- {F_: F_}
- >>> f(x).match(F)
- {F_: f(x)}
- >>> cos(x).match(F)
- {F_: cos(x)}
- >>> f(x, y).match(F)
- {F_: f(x, y)}
- To match functions with a given number of arguments, set ``nargs`` to the
- desired value at instantiation:
- >>> F = WildFunction('F', nargs=2)
- >>> F.nargs
- {2}
- >>> f(x).match(F)
- >>> f(x, y).match(F)
- {F_: f(x, y)}
- To match functions with a range of arguments, set ``nargs`` to a tuple
- containing the desired number of arguments, e.g. if ``nargs = (1, 2)``
- then functions with 1 or 2 arguments will be matched.
- >>> F = WildFunction('F', nargs=(1, 2))
- >>> F.nargs
- {1, 2}
- >>> f(x).match(F)
- {F_: f(x)}
- >>> f(x, y).match(F)
- {F_: f(x, y)}
- >>> f(x, y, 1).match(F)
- """
- # XXX: What is this class attribute used for?
- include: set[Any] = set()
- def __init__(cls, name, **assumptions):
- from sympy.sets.sets import Set, FiniteSet
- cls.name = name
- nargs = assumptions.pop('nargs', S.Naturals0)
- if not isinstance(nargs, Set):
- # Canonicalize nargs here. See also FunctionClass.
- if is_sequence(nargs):
- nargs = tuple(ordered(set(nargs)))
- elif nargs is not None:
- nargs = (as_int(nargs),)
- nargs = FiniteSet(*nargs)
- cls.nargs = nargs
- def matches(self, expr, repl_dict=None, old=False):
- if not isinstance(expr, (AppliedUndef, Function)):
- return None
- if len(expr.args) not in self.nargs:
- return None
- if repl_dict is None:
- repl_dict = {}
- else:
- repl_dict = repl_dict.copy()
- repl_dict[self] = expr
- return repl_dict
- class Derivative(Expr):
- """
- Carries out differentiation of the given expression with respect to symbols.
- Examples
- ========
- >>> from sympy import Derivative, Function, symbols, Subs
- >>> from sympy.abc import x, y
- >>> f, g = symbols('f g', cls=Function)
- >>> Derivative(x**2, x, evaluate=True)
- 2*x
- Denesting of derivatives retains the ordering of variables:
- >>> Derivative(Derivative(f(x, y), y), x)
- Derivative(f(x, y), y, x)
- Contiguously identical symbols are merged into a tuple giving
- the symbol and the count:
- >>> Derivative(f(x), x, x, y, x)
- Derivative(f(x), (x, 2), y, x)
- If the derivative cannot be performed, and evaluate is True, the
- order of the variables of differentiation will be made canonical:
- >>> Derivative(f(x, y), y, x, evaluate=True)
- Derivative(f(x, y), x, y)
- Derivatives with respect to undefined functions can be calculated:
- >>> Derivative(f(x)**2, f(x), evaluate=True)
- 2*f(x)
- Such derivatives will show up when the chain rule is used to
- evalulate a derivative:
- >>> f(g(x)).diff(x)
- Derivative(f(g(x)), g(x))*Derivative(g(x), x)
- Substitution is used to represent derivatives of functions with
- arguments that are not symbols or functions:
- >>> f(2*x + 3).diff(x) == 2*Subs(f(y).diff(y), y, 2*x + 3)
- True
- Notes
- =====
- Simplification of high-order derivatives:
- Because there can be a significant amount of simplification that can be
- done when multiple differentiations are performed, results will be
- automatically simplified in a fairly conservative fashion unless the
- keyword ``simplify`` is set to False.
- >>> from sympy import sqrt, diff, Function, symbols
- >>> from sympy.abc import x, y, z
- >>> f, g = symbols('f,g', cls=Function)
- >>> e = sqrt((x + 1)**2 + x)
- >>> diff(e, (x, 5), simplify=False).count_ops()
- 136
- >>> diff(e, (x, 5)).count_ops()
- 30
- Ordering of variables:
- If evaluate is set to True and the expression cannot be evaluated, the
- list of differentiation symbols will be sorted, that is, the expression is
- assumed to have continuous derivatives up to the order asked.
- Derivative wrt non-Symbols:
- For the most part, one may not differentiate wrt non-symbols.
- For example, we do not allow differentiation wrt `x*y` because
- there are multiple ways of structurally defining where x*y appears
- in an expression: a very strict definition would make
- (x*y*z).diff(x*y) == 0. Derivatives wrt defined functions (like
- cos(x)) are not allowed, either:
- >>> (x*y*z).diff(x*y)
- Traceback (most recent call last):
- ...
- ValueError: Can't calculate derivative wrt x*y.
- To make it easier to work with variational calculus, however,
- derivatives wrt AppliedUndef and Derivatives are allowed.
- For example, in the Euler-Lagrange method one may write
- F(t, u, v) where u = f(t) and v = f'(t). These variables can be
- written explicitly as functions of time::
- >>> from sympy.abc import t
- >>> F = Function('F')
- >>> U = f(t)
- >>> V = U.diff(t)
- The derivative wrt f(t) can be obtained directly:
- >>> direct = F(t, U, V).diff(U)
- When differentiation wrt a non-Symbol is attempted, the non-Symbol
- is temporarily converted to a Symbol while the differentiation
- is performed and the same answer is obtained:
- >>> indirect = F(t, U, V).subs(U, x).diff(x).subs(x, U)
- >>> assert direct == indirect
- The implication of this non-symbol replacement is that all
- functions are treated as independent of other functions and the
- symbols are independent of the functions that contain them::
- >>> x.diff(f(x))
- 0
- >>> g(x).diff(f(x))
- 0
- It also means that derivatives are assumed to depend only
- on the variables of differentiation, not on anything contained
- within the expression being differentiated::
- >>> F = f(x)
- >>> Fx = F.diff(x)
- >>> Fx.diff(F) # derivative depends on x, not F
- 0
- >>> Fxx = Fx.diff(x)
- >>> Fxx.diff(Fx) # derivative depends on x, not Fx
- 0
- The last example can be made explicit by showing the replacement
- of Fx in Fxx with y:
- >>> Fxx.subs(Fx, y)
- Derivative(y, x)
- Since that in itself will evaluate to zero, differentiating
- wrt Fx will also be zero:
- >>> _.doit()
- 0
- Replacing undefined functions with concrete expressions
- One must be careful to replace undefined functions with expressions
- that contain variables consistent with the function definition and
- the variables of differentiation or else insconsistent result will
- be obtained. Consider the following example:
- >>> eq = f(x)*g(y)
- >>> eq.subs(f(x), x*y).diff(x, y).doit()
- y*Derivative(g(y), y) + g(y)
- >>> eq.diff(x, y).subs(f(x), x*y).doit()
- y*Derivative(g(y), y)
- The results differ because `f(x)` was replaced with an expression
- that involved both variables of differentiation. In the abstract
- case, differentiation of `f(x)` by `y` is 0; in the concrete case,
- the presence of `y` made that derivative nonvanishing and produced
- the extra `g(y)` term.
- Defining differentiation for an object
- An object must define ._eval_derivative(symbol) method that returns
- the differentiation result. This function only needs to consider the
- non-trivial case where expr contains symbol and it should call the diff()
- method internally (not _eval_derivative); Derivative should be the only
- one to call _eval_derivative.
- Any class can allow derivatives to be taken with respect to
- itself (while indicating its scalar nature). See the
- docstring of Expr._diff_wrt.
- See Also
- ========
- _sort_variable_count
- """
- is_Derivative = True
- @property
- def _diff_wrt(self):
- """An expression may be differentiated wrt a Derivative if
- it is in elementary form.
- Examples
- ========
- >>> from sympy import Function, Derivative, cos
- >>> from sympy.abc import x
- >>> f = Function('f')
- >>> Derivative(f(x), x)._diff_wrt
- True
- >>> Derivative(cos(x), x)._diff_wrt
- False
- >>> Derivative(x + 1, x)._diff_wrt
- False
- A Derivative might be an unevaluated form of what will not be
- a valid variable of differentiation if evaluated. For example,
- >>> Derivative(f(f(x)), x).doit()
- Derivative(f(x), x)*Derivative(f(f(x)), f(x))
- Such an expression will present the same ambiguities as arise
- when dealing with any other product, like ``2*x``, so ``_diff_wrt``
- is False:
- >>> Derivative(f(f(x)), x)._diff_wrt
- False
- """
- return self.expr._diff_wrt and isinstance(self.doit(), Derivative)
- def __new__(cls, expr, *variables, **kwargs):
- expr = sympify(expr)
- symbols_or_none = getattr(expr, "free_symbols", None)
- has_symbol_set = isinstance(symbols_or_none, set)
- if not has_symbol_set:
- raise ValueError(filldedent('''
- Since there are no variables in the expression %s,
- it cannot be differentiated.''' % expr))
- # determine value for variables if it wasn't given
- if not variables:
- variables = expr.free_symbols
- if len(variables) != 1:
- if expr.is_number:
- return S.Zero
- if len(variables) == 0:
- raise ValueError(filldedent('''
- Since there are no variables in the expression,
- the variable(s) of differentiation must be supplied
- to differentiate %s''' % expr))
- else:
- raise ValueError(filldedent('''
- Since there is more than one variable in the
- expression, the variable(s) of differentiation
- must be supplied to differentiate %s''' % expr))
- # Split the list of variables into a list of the variables we are diff
- # wrt, where each element of the list has the form (s, count) where
- # s is the entity to diff wrt and count is the order of the
- # derivative.
- variable_count = []
- array_likes = (tuple, list, Tuple)
- from sympy.tensor.array import Array, NDimArray
- for i, v in enumerate(variables):
- if isinstance(v, UndefinedFunction):
- raise TypeError(
- "cannot differentiate wrt "
- "UndefinedFunction: %s" % v)
- if isinstance(v, array_likes):
- if len(v) == 0:
- # Ignore empty tuples: Derivative(expr, ... , (), ... )
- continue
- if isinstance(v[0], array_likes):
- # Derive by array: Derivative(expr, ... , [[x, y, z]], ... )
- if len(v) == 1:
- v = Array(v[0])
- count = 1
- else:
- v, count = v
- v = Array(v)
- else:
- v, count = v
- if count == 0:
- continue
- variable_count.append(Tuple(v, count))
- continue
- v = sympify(v)
- if isinstance(v, Integer):
- if i == 0:
- raise ValueError("First variable cannot be a number: %i" % v)
- count = v
- prev, prevcount = variable_count[-1]
- if prevcount != 1:
- raise TypeError("tuple {} followed by number {}".format((prev, prevcount), v))
- if count == 0:
- variable_count.pop()
- else:
- variable_count[-1] = Tuple(prev, count)
- else:
- count = 1
- variable_count.append(Tuple(v, count))
- # light evaluation of contiguous, identical
- # items: (x, 1), (x, 1) -> (x, 2)
- merged = []
- for t in variable_count:
- v, c = t
- if c.is_negative:
- raise ValueError(
- 'order of differentiation must be nonnegative')
- if merged and merged[-1][0] == v:
- c += merged[-1][1]
- if not c:
- merged.pop()
- else:
- merged[-1] = Tuple(v, c)
- else:
- merged.append(t)
- variable_count = merged
- # sanity check of variables of differentation; we waited
- # until the counts were computed since some variables may
- # have been removed because the count was 0
- for v, c in variable_count:
- # v must have _diff_wrt True
- if not v._diff_wrt:
- __ = '' # filler to make error message neater
- raise ValueError(filldedent('''
- Can't calculate derivative wrt %s.%s''' % (v,
- __)))
- # We make a special case for 0th derivative, because there is no
- # good way to unambiguously print this.
- if len(variable_count) == 0:
- return expr
- evaluate = kwargs.get('evaluate', False)
- if evaluate:
- if isinstance(expr, Derivative):
- expr = expr.canonical
- variable_count = [
- (v.canonical if isinstance(v, Derivative) else v, c)
- for v, c in variable_count]
- # Look for a quick exit if there are symbols that don't appear in
- # expression at all. Note, this cannot check non-symbols like
- # Derivatives as those can be created by intermediate
- # derivatives.
- zero = False
- free = expr.free_symbols
- from sympy.matrices.expressions.matexpr import MatrixExpr
- for v, c in variable_count:
- vfree = v.free_symbols
- if c.is_positive and vfree:
- if isinstance(v, AppliedUndef):
- # these match exactly since
- # x.diff(f(x)) == g(x).diff(f(x)) == 0
- # and are not created by differentiation
- D = Dummy()
- if not expr.xreplace({v: D}).has(D):
- zero = True
- break
- elif isinstance(v, MatrixExpr):
- zero = False
- break
- elif isinstance(v, Symbol) and v not in free:
- zero = True
- break
- else:
- if not free & vfree:
- # e.g. v is IndexedBase or Matrix
- zero = True
- break
- if zero:
- return cls._get_zero_with_shape_like(expr)
- # make the order of symbols canonical
- #TODO: check if assumption of discontinuous derivatives exist
- variable_count = cls._sort_variable_count(variable_count)
- # denest
- if isinstance(expr, Derivative):
- variable_count = list(expr.variable_count) + variable_count
- expr = expr.expr
- return _derivative_dispatch(expr, *variable_count, **kwargs)
- # we return here if evaluate is False or if there is no
- # _eval_derivative method
- if not evaluate or not hasattr(expr, '_eval_derivative'):
- # return an unevaluated Derivative
- if evaluate and variable_count == [(expr, 1)] and expr.is_scalar:
- # special hack providing evaluation for classes
- # that have defined is_scalar=True but have no
- # _eval_derivative defined
- return S.One
- return Expr.__new__(cls, expr, *variable_count)
- # evaluate the derivative by calling _eval_derivative method
- # of expr for each variable
- # -------------------------------------------------------------
- nderivs = 0 # how many derivatives were performed
- unhandled = []
- from sympy.matrices.common import MatrixCommon
- for i, (v, count) in enumerate(variable_count):
- old_expr = expr
- old_v = None
- is_symbol = v.is_symbol or isinstance(v,
- (Iterable, Tuple, MatrixCommon, NDimArray))
- if not is_symbol:
- old_v = v
- v = Dummy('xi')
- expr = expr.xreplace({old_v: v})
- # Derivatives and UndefinedFunctions are independent
- # of all others
- clashing = not (isinstance(old_v, Derivative) or \
- isinstance(old_v, AppliedUndef))
- if v not in expr.free_symbols and not clashing:
- return expr.diff(v) # expr's version of 0
- if not old_v.is_scalar and not hasattr(
- old_v, '_eval_derivative'):
- # special hack providing evaluation for classes
- # that have defined is_scalar=True but have no
- # _eval_derivative defined
- expr *= old_v.diff(old_v)
- obj = cls._dispatch_eval_derivative_n_times(expr, v, count)
- if obj is not None and obj.is_zero:
- return obj
- nderivs += count
- if old_v is not None:
- if obj is not None:
- # remove the dummy that was used
- obj = obj.subs(v, old_v)
- # restore expr
- expr = old_expr
- if obj is None:
- # we've already checked for quick-exit conditions
- # that give 0 so the remaining variables
- # are contained in the expression but the expression
- # did not compute a derivative so we stop taking
- # derivatives
- unhandled = variable_count[i:]
- break
- expr = obj
- # what we have so far can be made canonical
- expr = expr.replace(
- lambda x: isinstance(x, Derivative),
- lambda x: x.canonical)
- if unhandled:
- if isinstance(expr, Derivative):
- unhandled = list(expr.variable_count) + unhandled
- expr = expr.expr
- expr = Expr.__new__(cls, expr, *unhandled)
- if (nderivs > 1) == True and kwargs.get('simplify', True):
- from .exprtools import factor_terms
- from sympy.simplify.simplify import signsimp
- expr = factor_terms(signsimp(expr))
- return expr
- @property
- def canonical(cls):
- return cls.func(cls.expr,
- *Derivative._sort_variable_count(cls.variable_count))
- @classmethod
- def _sort_variable_count(cls, vc):
- """
- Sort (variable, count) pairs into canonical order while
- retaining order of variables that do not commute during
- differentiation:
- * symbols and functions commute with each other
- * derivatives commute with each other
- * a derivative does not commute with anything it contains
- * any other object is not allowed to commute if it has
- free symbols in common with another object
- Examples
- ========
- >>> from sympy import Derivative, Function, symbols
- >>> vsort = Derivative._sort_variable_count
- >>> x, y, z = symbols('x y z')
- >>> f, g, h = symbols('f g h', cls=Function)
- Contiguous items are collapsed into one pair:
- >>> vsort([(x, 1), (x, 1)])
- [(x, 2)]
- >>> vsort([(y, 1), (f(x), 1), (y, 1), (f(x), 1)])
- [(y, 2), (f(x), 2)]
- Ordering is canonical.
- >>> def vsort0(*v):
- ... # docstring helper to
- ... # change vi -> (vi, 0), sort, and return vi vals
- ... return [i[0] for i in vsort([(i, 0) for i in v])]
- >>> vsort0(y, x)
- [x, y]
- >>> vsort0(g(y), g(x), f(y))
- [f(y), g(x), g(y)]
- Symbols are sorted as far to the left as possible but never
- move to the left of a derivative having the same symbol in
- its variables; the same applies to AppliedUndef which are
- always sorted after Symbols:
- >>> dfx = f(x).diff(x)
- >>> assert vsort0(dfx, y) == [y, dfx]
- >>> assert vsort0(dfx, x) == [dfx, x]
- """
- if not vc:
- return []
- vc = list(vc)
- if len(vc) == 1:
- return [Tuple(*vc[0])]
- V = list(range(len(vc)))
- E = []
- v = lambda i: vc[i][0]
- D = Dummy()
- def _block(d, v, wrt=False):
- # return True if v should not come before d else False
- if d == v:
- return wrt
- if d.is_Symbol:
- return False
- if isinstance(d, Derivative):
- # a derivative blocks if any of it's variables contain
- # v; the wrt flag will return True for an exact match
- # and will cause an AppliedUndef to block if v is in
- # the arguments
- if any(_block(k, v, wrt=True)
- for k in d._wrt_variables):
- return True
- return False
- if not wrt and isinstance(d, AppliedUndef):
- return False
- if v.is_Symbol:
- return v in d.free_symbols
- if isinstance(v, AppliedUndef):
- return _block(d.xreplace({v: D}), D)
- return d.free_symbols & v.free_symbols
- for i in range(len(vc)):
- for j in range(i):
- if _block(v(j), v(i)):
- E.append((j,i))
- # this is the default ordering to use in case of ties
- O = dict(zip(ordered(uniq([i for i, c in vc])), range(len(vc))))
- ix = topological_sort((V, E), key=lambda i: O[v(i)])
- # merge counts of contiguously identical items
- merged = []
- for v, c in [vc[i] for i in ix]:
- if merged and merged[-1][0] == v:
- merged[-1][1] += c
- else:
- merged.append([v, c])
- return [Tuple(*i) for i in merged]
- def _eval_is_commutative(self):
- return self.expr.is_commutative
- def _eval_derivative(self, v):
- # If v (the variable of differentiation) is not in
- # self.variables, we might be able to take the derivative.
- if v not in self._wrt_variables:
- dedv = self.expr.diff(v)
- if isinstance(dedv, Derivative):
- return dedv.func(dedv.expr, *(self.variable_count + dedv.variable_count))
- # dedv (d(self.expr)/dv) could have simplified things such that the
- # derivative wrt things in self.variables can now be done. Thus,
- # we set evaluate=True to see if there are any other derivatives
- # that can be done. The most common case is when dedv is a simple
- # number so that the derivative wrt anything else will vanish.
- return self.func(dedv, *self.variables, evaluate=True)
- # In this case v was in self.variables so the derivative wrt v has
- # already been attempted and was not computed, either because it
- # couldn't be or evaluate=False originally.
- variable_count = list(self.variable_count)
- variable_count.append((v, 1))
- return self.func(self.expr, *variable_count, evaluate=False)
- def doit(self, **hints):
- expr = self.expr
- if hints.get('deep', True):
- expr = expr.doit(**hints)
- hints['evaluate'] = True
- rv = self.func(expr, *self.variable_count, **hints)
- if rv!= self and rv.has(Derivative):
- rv = rv.doit(**hints)
- return rv
- @_sympifyit('z0', NotImplementedError)
- def doit_numerically(self, z0):
- """
- Evaluate the derivative at z numerically.
- When we can represent derivatives at a point, this should be folded
- into the normal evalf. For now, we need a special method.
- """
- if len(self.free_symbols) != 1 or len(self.variables) != 1:
- raise NotImplementedError('partials and higher order derivatives')
- z = list(self.free_symbols)[0]
- def eval(x):
- f0 = self.expr.subs(z, Expr._from_mpmath(x, prec=mpmath.mp.prec))
- f0 = f0.evalf(prec_to_dps(mpmath.mp.prec))
- return f0._to_mpmath(mpmath.mp.prec)
- return Expr._from_mpmath(mpmath.diff(eval,
- z0._to_mpmath(mpmath.mp.prec)),
- mpmath.mp.prec)
- @property
- def expr(self):
- return self._args[0]
- @property
- def _wrt_variables(self):
- # return the variables of differentiation without
- # respect to the type of count (int or symbolic)
- return [i[0] for i in self.variable_count]
- @property
- def variables(self):
- # TODO: deprecate? YES, make this 'enumerated_variables' and
- # name _wrt_variables as variables
- # TODO: support for `d^n`?
- rv = []
- for v, count in self.variable_count:
- if not count.is_Integer:
- raise TypeError(filldedent('''
- Cannot give expansion for symbolic count. If you just
- want a list of all variables of differentiation, use
- _wrt_variables.'''))
- rv.extend([v]*count)
- return tuple(rv)
- @property
- def variable_count(self):
- return self._args[1:]
- @property
- def derivative_count(self):
- return sum([count for _, count in self.variable_count], 0)
- @property
- def free_symbols(self):
- ret = self.expr.free_symbols
- # Add symbolic counts to free_symbols
- for _, count in self.variable_count:
- ret.update(count.free_symbols)
- return ret
- @property
- def kind(self):
- return self.args[0].kind
- def _eval_subs(self, old, new):
- # The substitution (old, new) cannot be done inside
- # Derivative(expr, vars) for a variety of reasons
- # as handled below.
- if old in self._wrt_variables:
- # first handle the counts
- expr = self.func(self.expr, *[(v, c.subs(old, new))
- for v, c in self.variable_count])
- if expr != self:
- return expr._eval_subs(old, new)
- # quick exit case
- if not getattr(new, '_diff_wrt', False):
- # case (0): new is not a valid variable of
- # differentiation
- if isinstance(old, Symbol):
- # don't introduce a new symbol if the old will do
- return Subs(self, old, new)
- else:
- xi = Dummy('xi')
- return Subs(self.xreplace({old: xi}), xi, new)
- # If both are Derivatives with the same expr, check if old is
- # equivalent to self or if old is a subderivative of self.
- if old.is_Derivative and old.expr == self.expr:
- if self.canonical == old.canonical:
- return new
- # collections.Counter doesn't have __le__
- def _subset(a, b):
- return all((a[i] <= b[i]) == True for i in a)
- old_vars = Counter(dict(reversed(old.variable_count)))
- self_vars = Counter(dict(reversed(self.variable_count)))
- if _subset(old_vars, self_vars):
- return _derivative_dispatch(new, *(self_vars - old_vars).items()).canonical
- args = list(self.args)
- newargs = [x._subs(old, new) for x in args]
- if args[0] == old:
- # complete replacement of self.expr
- # we already checked that the new is valid so we know
- # it won't be a problem should it appear in variables
- return _derivative_dispatch(*newargs)
- if newargs[0] != args[0]:
- # case (1) can't change expr by introducing something that is in
- # the _wrt_variables if it was already in the expr
- # e.g.
- # for Derivative(f(x, g(y)), y), x cannot be replaced with
- # anything that has y in it; for f(g(x), g(y)).diff(g(y))
- # g(x) cannot be replaced with anything that has g(y)
- syms = {vi: Dummy() for vi in self._wrt_variables
- if not vi.is_Symbol}
- wrt = {syms.get(vi, vi) for vi in self._wrt_variables}
- forbidden = args[0].xreplace(syms).free_symbols & wrt
- nfree = new.xreplace(syms).free_symbols
- ofree = old.xreplace(syms).free_symbols
- if (nfree - ofree) & forbidden:
- return Subs(self, old, new)
- viter = ((i, j) for ((i, _), (j, _)) in zip(newargs[1:], args[1:]))
- if any(i != j for i, j in viter): # a wrt-variable change
- # case (2) can't change vars by introducing a variable
- # that is contained in expr, e.g.
- # for Derivative(f(z, g(h(x), y)), y), y cannot be changed to
- # x, h(x), or g(h(x), y)
- for a in _atomic(self.expr, recursive=True):
- for i in range(1, len(newargs)):
- vi, _ = newargs[i]
- if a == vi and vi != args[i][0]:
- return Subs(self, old, new)
- # more arg-wise checks
- vc = newargs[1:]
- oldv = self._wrt_variables
- newe = self.expr
- subs = []
- for i, (vi, ci) in enumerate(vc):
- if not vi._diff_wrt:
- # case (3) invalid differentiation expression so
- # create a replacement dummy
- xi = Dummy('xi_%i' % i)
- # replace the old valid variable with the dummy
- # in the expression
- newe = newe.xreplace({oldv[i]: xi})
- # and replace the bad variable with the dummy
- vc[i] = (xi, ci)
- # and record the dummy with the new (invalid)
- # differentiation expression
- subs.append((xi, vi))
- if subs:
- # handle any residual substitution in the expression
- newe = newe._subs(old, new)
- # return the Subs-wrapped derivative
- return Subs(Derivative(newe, *vc), *zip(*subs))
- # everything was ok
- return _derivative_dispatch(*newargs)
- def _eval_lseries(self, x, logx, cdir=0):
- dx = self.variables
- for term in self.expr.lseries(x, logx=logx, cdir=cdir):
- yield self.func(term, *dx)
- def _eval_nseries(self, x, n, logx, cdir=0):
- arg = self.expr.nseries(x, n=n, logx=logx)
- o = arg.getO()
- dx = self.variables
- rv = [self.func(a, *dx) for a in Add.make_args(arg.removeO())]
- if o:
- rv.append(o/x)
- return Add(*rv)
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- series_gen = self.expr.lseries(x)
- d = S.Zero
- for leading_term in series_gen:
- d = diff(leading_term, *self.variables)
- if d != 0:
- break
- return d
- def as_finite_difference(self, points=1, x0=None, wrt=None):
- """ Expresses a Derivative instance as a finite difference.
- Parameters
- ==========
- points : sequence or coefficient, optional
- If sequence: discrete values (length >= order+1) of the
- independent variable used for generating the finite
- difference weights.
- If it is a coefficient, it will be used as the step-size
- for generating an equidistant sequence of length order+1
- centered around ``x0``. Default: 1 (step-size 1)
- x0 : number or Symbol, optional
- the value of the independent variable (``wrt``) at which the
- derivative is to be approximated. Default: same as ``wrt``.
- wrt : Symbol, optional
- "with respect to" the variable for which the (partial)
- derivative is to be approximated for. If not provided it
- is required that the derivative is ordinary. Default: ``None``.
- Examples
- ========
- >>> from sympy import symbols, Function, exp, sqrt, Symbol
- >>> x, h = symbols('x h')
- >>> f = Function('f')
- >>> f(x).diff(x).as_finite_difference()
- -f(x - 1/2) + f(x + 1/2)
- The default step size and number of points are 1 and
- ``order + 1`` respectively. We can change the step size by
- passing a symbol as a parameter:
- >>> f(x).diff(x).as_finite_difference(h)
- -f(-h/2 + x)/h + f(h/2 + x)/h
- We can also specify the discretized values to be used in a
- sequence:
- >>> f(x).diff(x).as_finite_difference([x, x+h, x+2*h])
- -3*f(x)/(2*h) + 2*f(h + x)/h - f(2*h + x)/(2*h)
- The algorithm is not restricted to use equidistant spacing, nor
- do we need to make the approximation around ``x0``, but we can get
- an expression estimating the derivative at an offset:
- >>> e, sq2 = exp(1), sqrt(2)
- >>> xl = [x-h, x+h, x+e*h]
- >>> f(x).diff(x, 1).as_finite_difference(xl, x+h*sq2) # doctest: +ELLIPSIS
- 2*h*((h + sqrt(2)*h)/(2*h) - (-sqrt(2)*h + h)/(2*h))*f(E*h + x)/...
- To approximate ``Derivative`` around ``x0`` using a non-equidistant
- spacing step, the algorithm supports assignment of undefined
- functions to ``points``:
- >>> dx = Function('dx')
- >>> f(x).diff(x).as_finite_difference(points=dx(x), x0=x-h)
- -f(-h + x - dx(-h + x)/2)/dx(-h + x) + f(-h + x + dx(-h + x)/2)/dx(-h + x)
- Partial derivatives are also supported:
- >>> y = Symbol('y')
- >>> d2fdxdy=f(x,y).diff(x,y)
- >>> d2fdxdy.as_finite_difference(wrt=x)
- -Derivative(f(x - 1/2, y), y) + Derivative(f(x + 1/2, y), y)
- We can apply ``as_finite_difference`` to ``Derivative`` instances in
- compound expressions using ``replace``:
- >>> (1 + 42**f(x).diff(x)).replace(lambda arg: arg.is_Derivative,
- ... lambda arg: arg.as_finite_difference())
- 42**(-f(x - 1/2) + f(x + 1/2)) + 1
- See also
- ========
- sympy.calculus.finite_diff.apply_finite_diff
- sympy.calculus.finite_diff.differentiate_finite
- sympy.calculus.finite_diff.finite_diff_weights
- """
- from sympy.calculus.finite_diff import _as_finite_diff
- return _as_finite_diff(self, points, x0, wrt)
- @classmethod
- def _get_zero_with_shape_like(cls, expr):
- return S.Zero
- @classmethod
- def _dispatch_eval_derivative_n_times(cls, expr, v, count):
- # Evaluate the derivative `n` times. If
- # `_eval_derivative_n_times` is not overridden by the current
- # object, the default in `Basic` will call a loop over
- # `_eval_derivative`:
- return expr._eval_derivative_n_times(v, count)
- def _derivative_dispatch(expr, *variables, **kwargs):
- from sympy.matrices.common import MatrixCommon
- from sympy.matrices.expressions.matexpr import MatrixExpr
- from sympy.tensor.array import NDimArray
- array_types = (MatrixCommon, MatrixExpr, NDimArray, list, tuple, Tuple)
- if isinstance(expr, array_types) or any(isinstance(i[0], array_types) if isinstance(i, (tuple, list, Tuple)) else isinstance(i, array_types) for i in variables):
- from sympy.tensor.array.array_derivatives import ArrayDerivative
- return ArrayDerivative(expr, *variables, **kwargs)
- return Derivative(expr, *variables, **kwargs)
- class Lambda(Expr):
- """
- Lambda(x, expr) represents a lambda function similar to Python's
- 'lambda x: expr'. A function of several variables is written as
- Lambda((x, y, ...), expr).
- Examples
- ========
- A simple example:
- >>> from sympy import Lambda
- >>> from sympy.abc import x
- >>> f = Lambda(x, x**2)
- >>> f(4)
- 16
- For multivariate functions, use:
- >>> from sympy.abc import y, z, t
- >>> f2 = Lambda((x, y, z, t), x + y**z + t**z)
- >>> f2(1, 2, 3, 4)
- 73
- It is also possible to unpack tuple arguments:
- >>> f = Lambda(((x, y), z), x + y + z)
- >>> f((1, 2), 3)
- 6
- A handy shortcut for lots of arguments:
- >>> p = x, y, z
- >>> f = Lambda(p, x + y*z)
- >>> f(*p)
- x + y*z
- """
- is_Function = True
- def __new__(cls, signature, expr):
- if iterable(signature) and not isinstance(signature, (tuple, Tuple)):
- sympy_deprecation_warning(
- """
- Using a non-tuple iterable as the first argument to Lambda
- is deprecated. Use Lambda(tuple(args), expr) instead.
- """,
- deprecated_since_version="1.5",
- active_deprecations_target="deprecated-non-tuple-lambda",
- )
- signature = tuple(signature)
- sig = signature if iterable(signature) else (signature,)
- sig = sympify(sig)
- cls._check_signature(sig)
- if len(sig) == 1 and sig[0] == expr:
- return S.IdentityFunction
- return Expr.__new__(cls, sig, sympify(expr))
- @classmethod
- def _check_signature(cls, sig):
- syms = set()
- def rcheck(args):
- for a in args:
- if a.is_symbol:
- if a in syms:
- raise BadSignatureError("Duplicate symbol %s" % a)
- syms.add(a)
- elif isinstance(a, Tuple):
- rcheck(a)
- else:
- raise BadSignatureError("Lambda signature should be only tuples"
- " and symbols, not %s" % a)
- if not isinstance(sig, Tuple):
- raise BadSignatureError("Lambda signature should be a tuple not %s" % sig)
- # Recurse through the signature:
- rcheck(sig)
- @property
- def signature(self):
- """The expected form of the arguments to be unpacked into variables"""
- return self._args[0]
- @property
- def expr(self):
- """The return value of the function"""
- return self._args[1]
- @property
- def variables(self):
- """The variables used in the internal representation of the function"""
- def _variables(args):
- if isinstance(args, Tuple):
- for arg in args:
- yield from _variables(arg)
- else:
- yield args
- return tuple(_variables(self.signature))
- @property
- def nargs(self):
- from sympy.sets.sets import FiniteSet
- return FiniteSet(len(self.signature))
- bound_symbols = variables
- @property
- def free_symbols(self):
- return self.expr.free_symbols - set(self.variables)
- def __call__(self, *args):
- n = len(args)
- if n not in self.nargs: # Lambda only ever has 1 value in nargs
- # XXX: exception message must be in exactly this format to
- # make it work with NumPy's functions like vectorize(). See,
- # for example, https://github.com/numpy/numpy/issues/1697.
- # The ideal solution would be just to attach metadata to
- # the exception and change NumPy to take advantage of this.
- ## XXX does this apply to Lambda? If not, remove this comment.
- temp = ('%(name)s takes exactly %(args)s '
- 'argument%(plural)s (%(given)s given)')
- raise BadArgumentsError(temp % {
- 'name': self,
- 'args': list(self.nargs)[0],
- 'plural': 's'*(list(self.nargs)[0] != 1),
- 'given': n})
- d = self._match_signature(self.signature, args)
- return self.expr.xreplace(d)
- def _match_signature(self, sig, args):
- symargmap = {}
- def rmatch(pars, args):
- for par, arg in zip(pars, args):
- if par.is_symbol:
- symargmap[par] = arg
- elif isinstance(par, Tuple):
- if not isinstance(arg, (tuple, Tuple)) or len(args) != len(pars):
- raise BadArgumentsError("Can't match %s and %s" % (args, pars))
- rmatch(par, arg)
- rmatch(sig, args)
- return symargmap
- @property
- def is_identity(self):
- """Return ``True`` if this ``Lambda`` is an identity function. """
- return self.signature == self.expr
- def _eval_evalf(self, prec):
- return self.func(self.args[0], self.args[1].evalf(n=prec_to_dps(prec)))
- class Subs(Expr):
- """
- Represents unevaluated substitutions of an expression.
- ``Subs(expr, x, x0)`` represents the expression resulting
- from substituting x with x0 in expr.
- Parameters
- ==========
- expr : Expr
- An expression.
- x : tuple, variable
- A variable or list of distinct variables.
- x0 : tuple or list of tuples
- A point or list of evaluation points
- corresponding to those variables.
- Examples
- ========
- >>> from sympy import Subs, Function, sin, cos
- >>> from sympy.abc import x, y, z
- >>> f = Function('f')
- Subs are created when a particular substitution cannot be made. The
- x in the derivative cannot be replaced with 0 because 0 is not a
- valid variables of differentiation:
- >>> f(x).diff(x).subs(x, 0)
- Subs(Derivative(f(x), x), x, 0)
- Once f is known, the derivative and evaluation at 0 can be done:
- >>> _.subs(f, sin).doit() == sin(x).diff(x).subs(x, 0) == cos(0)
- True
- Subs can also be created directly with one or more variables:
- >>> Subs(f(x)*sin(y) + z, (x, y), (0, 1))
- Subs(z + f(x)*sin(y), (x, y), (0, 1))
- >>> _.doit()
- z + f(0)*sin(1)
- Notes
- =====
- ``Subs`` objects are generally useful to represent unevaluated derivatives
- calculated at a point.
- The variables may be expressions, but they are subjected to the limitations
- of subs(), so it is usually a good practice to use only symbols for
- variables, since in that case there can be no ambiguity.
- There's no automatic expansion - use the method .doit() to effect all
- possible substitutions of the object and also of objects inside the
- expression.
- When evaluating derivatives at a point that is not a symbol, a Subs object
- is returned. One is also able to calculate derivatives of Subs objects - in
- this case the expression is always expanded (for the unevaluated form, use
- Derivative()).
- In order to allow expressions to combine before doit is done, a
- representation of the Subs expression is used internally to make
- expressions that are superficially different compare the same:
- >>> a, b = Subs(x, x, 0), Subs(y, y, 0)
- >>> a + b
- 2*Subs(x, x, 0)
- This can lead to unexpected consequences when using methods
- like `has` that are cached:
- >>> s = Subs(x, x, 0)
- >>> s.has(x), s.has(y)
- (True, False)
- >>> ss = s.subs(x, y)
- >>> ss.has(x), ss.has(y)
- (True, False)
- >>> s, ss
- (Subs(x, x, 0), Subs(y, y, 0))
- """
- def __new__(cls, expr, variables, point, **assumptions):
- if not is_sequence(variables, Tuple):
- variables = [variables]
- variables = Tuple(*variables)
- if has_dups(variables):
- repeated = [str(v) for v, i in Counter(variables).items() if i > 1]
- __ = ', '.join(repeated)
- raise ValueError(filldedent('''
- The following expressions appear more than once: %s
- ''' % __))
- point = Tuple(*(point if is_sequence(point, Tuple) else [point]))
- if len(point) != len(variables):
- raise ValueError('Number of point values must be the same as '
- 'the number of variables.')
- if not point:
- return sympify(expr)
- # denest
- if isinstance(expr, Subs):
- variables = expr.variables + variables
- point = expr.point + point
- expr = expr.expr
- else:
- expr = sympify(expr)
- # use symbols with names equal to the point value (with prepended _)
- # to give a variable-independent expression
- pre = "_"
- pts = sorted(set(point), key=default_sort_key)
- from sympy.printing.str import StrPrinter
- class CustomStrPrinter(StrPrinter):
- def _print_Dummy(self, expr):
- return str(expr) + str(expr.dummy_index)
- def mystr(expr, **settings):
- p = CustomStrPrinter(settings)
- return p.doprint(expr)
- while 1:
- s_pts = {p: Symbol(pre + mystr(p)) for p in pts}
- reps = [(v, s_pts[p])
- for v, p in zip(variables, point)]
- # if any underscore-prepended symbol is already a free symbol
- # and is a variable with a different point value, then there
- # is a clash, e.g. _0 clashes in Subs(_0 + _1, (_0, _1), (1, 0))
- # because the new symbol that would be created is _1 but _1
- # is already mapped to 0 so __0 and __1 are used for the new
- # symbols
- if any(r in expr.free_symbols and
- r in variables and
- Symbol(pre + mystr(point[variables.index(r)])) != r
- for _, r in reps):
- pre += "_"
- continue
- break
- obj = Expr.__new__(cls, expr, Tuple(*variables), point)
- obj._expr = expr.xreplace(dict(reps))
- return obj
- def _eval_is_commutative(self):
- return self.expr.is_commutative
- def doit(self, **hints):
- e, v, p = self.args
- # remove self mappings
- for i, (vi, pi) in enumerate(zip(v, p)):
- if vi == pi:
- v = v[:i] + v[i + 1:]
- p = p[:i] + p[i + 1:]
- if not v:
- return self.expr
- if isinstance(e, Derivative):
- # apply functions first, e.g. f -> cos
- undone = []
- for i, vi in enumerate(v):
- if isinstance(vi, FunctionClass):
- e = e.subs(vi, p[i])
- else:
- undone.append((vi, p[i]))
- if not isinstance(e, Derivative):
- e = e.doit()
- if isinstance(e, Derivative):
- # do Subs that aren't related to differentiation
- undone2 = []
- D = Dummy()
- arg = e.args[0]
- for vi, pi in undone:
- if D not in e.xreplace({vi: D}).free_symbols:
- if arg.has(vi):
- e = e.subs(vi, pi)
- else:
- undone2.append((vi, pi))
- undone = undone2
- # differentiate wrt variables that are present
- wrt = []
- D = Dummy()
- expr = e.expr
- free = expr.free_symbols
- for vi, ci in e.variable_count:
- if isinstance(vi, Symbol) and vi in free:
- expr = expr.diff((vi, ci))
- elif D in expr.subs(vi, D).free_symbols:
- expr = expr.diff((vi, ci))
- else:
- wrt.append((vi, ci))
- # inject remaining subs
- rv = expr.subs(undone)
- # do remaining differentiation *in order given*
- for vc in wrt:
- rv = rv.diff(vc)
- else:
- # inject remaining subs
- rv = e.subs(undone)
- else:
- rv = e.doit(**hints).subs(list(zip(v, p)))
- if hints.get('deep', True) and rv != self:
- rv = rv.doit(**hints)
- return rv
- def evalf(self, prec=None, **options):
- return self.doit().evalf(prec, **options)
- n = evalf # type:ignore
- @property
- def variables(self):
- """The variables to be evaluated"""
- return self._args[1]
- bound_symbols = variables
- @property
- def expr(self):
- """The expression on which the substitution operates"""
- return self._args[0]
- @property
- def point(self):
- """The values for which the variables are to be substituted"""
- return self._args[2]
- @property
- def free_symbols(self):
- return (self.expr.free_symbols - set(self.variables) |
- set(self.point.free_symbols))
- @property
- def expr_free_symbols(self):
- sympy_deprecation_warning("""
- The expr_free_symbols property is deprecated. Use free_symbols to get
- the free symbols of an expression.
- """,
- deprecated_since_version="1.9",
- active_deprecations_target="deprecated-expr-free-symbols")
- # Don't show the warning twice from the recursive call
- with ignore_warnings(SymPyDeprecationWarning):
- return (self.expr.expr_free_symbols - set(self.variables) |
- set(self.point.expr_free_symbols))
- def __eq__(self, other):
- if not isinstance(other, Subs):
- return False
- return self._hashable_content() == other._hashable_content()
- def __ne__(self, other):
- return not(self == other)
- def __hash__(self):
- return super().__hash__()
- def _hashable_content(self):
- return (self._expr.xreplace(self.canonical_variables),
- ) + tuple(ordered([(v, p) for v, p in
- zip(self.variables, self.point) if not self.expr.has(v)]))
- def _eval_subs(self, old, new):
- # Subs doit will do the variables in order; the semantics
- # of subs for Subs is have the following invariant for
- # Subs object foo:
- # foo.doit().subs(reps) == foo.subs(reps).doit()
- pt = list(self.point)
- if old in self.variables:
- if _atomic(new) == {new} and not any(
- i.has(new) for i in self.args):
- # the substitution is neutral
- return self.xreplace({old: new})
- # any occurrence of old before this point will get
- # handled by replacements from here on
- i = self.variables.index(old)
- for j in range(i, len(self.variables)):
- pt[j] = pt[j]._subs(old, new)
- return self.func(self.expr, self.variables, pt)
- v = [i._subs(old, new) for i in self.variables]
- if v != list(self.variables):
- return self.func(self.expr, self.variables + (old,), pt + [new])
- expr = self.expr._subs(old, new)
- pt = [i._subs(old, new) for i in self.point]
- return self.func(expr, v, pt)
- def _eval_derivative(self, s):
- # Apply the chain rule of the derivative on the substitution variables:
- f = self.expr
- vp = V, P = self.variables, self.point
- val = Add.fromiter(p.diff(s)*Subs(f.diff(v), *vp).doit()
- for v, p in zip(V, P))
- # these are all the free symbols in the expr
- efree = f.free_symbols
- # some symbols like IndexedBase include themselves and args
- # as free symbols
- compound = {i for i in efree if len(i.free_symbols) > 1}
- # hide them and see what independent free symbols remain
- dums = {Dummy() for i in compound}
- masked = f.xreplace(dict(zip(compound, dums)))
- ifree = masked.free_symbols - dums
- # include the compound symbols
- free = ifree | compound
- # remove the variables already handled
- free -= set(V)
- # add back any free symbols of remaining compound symbols
- free |= {i for j in free & compound for i in j.free_symbols}
- # if symbols of s are in free then there is more to do
- if free & s.free_symbols:
- val += Subs(f.diff(s), self.variables, self.point).doit()
- return val
- def _eval_nseries(self, x, n, logx, cdir=0):
- if x in self.point:
- # x is the variable being substituted into
- apos = self.point.index(x)
- other = self.variables[apos]
- else:
- other = x
- arg = self.expr.nseries(other, n=n, logx=logx)
- o = arg.getO()
- terms = Add.make_args(arg.removeO())
- rv = Add(*[self.func(a, *self.args[1:]) for a in terms])
- if o:
- rv += o.subs(other, x)
- return rv
- def _eval_as_leading_term(self, x, logx=None, cdir=0):
- if x in self.point:
- ipos = self.point.index(x)
- xvar = self.variables[ipos]
- return self.expr.as_leading_term(xvar)
- if x in self.variables:
- # if `x` is a dummy variable, it means it won't exist after the
- # substitution has been performed:
- return self
- # The variable is independent of the substitution:
- return self.expr.as_leading_term(x)
- def diff(f, *symbols, **kwargs):
- """
- Differentiate f with respect to symbols.
- Explanation
- ===========
- This is just a wrapper to unify .diff() and the Derivative class; its
- interface is similar to that of integrate(). You can use the same
- shortcuts for multiple variables as with Derivative. For example,
- diff(f(x), x, x, x) and diff(f(x), x, 3) both return the third derivative
- of f(x).
- You can pass evaluate=False to get an unevaluated Derivative class. Note
- that if there are 0 symbols (such as diff(f(x), x, 0), then the result will
- be the function (the zeroth derivative), even if evaluate=False.
- Examples
- ========
- >>> from sympy import sin, cos, Function, diff
- >>> from sympy.abc import x, y
- >>> f = Function('f')
- >>> diff(sin(x), x)
- cos(x)
- >>> diff(f(x), x, x, x)
- Derivative(f(x), (x, 3))
- >>> diff(f(x), x, 3)
- Derivative(f(x), (x, 3))
- >>> diff(sin(x)*cos(y), x, 2, y, 2)
- sin(x)*cos(y)
- >>> type(diff(sin(x), x))
- cos
- >>> type(diff(sin(x), x, evaluate=False))
- <class 'sympy.core.function.Derivative'>
- >>> type(diff(sin(x), x, 0))
- sin
- >>> type(diff(sin(x), x, 0, evaluate=False))
- sin
- >>> diff(sin(x))
- cos(x)
- >>> diff(sin(x*y))
- Traceback (most recent call last):
- ...
- ValueError: specify differentiation variables to differentiate sin(x*y)
- Note that ``diff(sin(x))`` syntax is meant only for convenience
- in interactive sessions and should be avoided in library code.
- References
- ==========
- .. [1] https://reference.wolfram.com/legacy/v5_2/Built-inFunctions/AlgebraicComputation/Calculus/D.html
- See Also
- ========
- Derivative
- idiff: computes the derivative implicitly
- """
- if hasattr(f, 'diff'):
- return f.diff(*symbols, **kwargs)
- kwargs.setdefault('evaluate', True)
- return _derivative_dispatch(f, *symbols, **kwargs)
- def expand(e, deep=True, modulus=None, power_base=True, power_exp=True,
- mul=True, log=True, multinomial=True, basic=True, **hints):
- r"""
- Expand an expression using methods given as hints.
- Explanation
- ===========
- Hints evaluated unless explicitly set to False are: ``basic``, ``log``,
- ``multinomial``, ``mul``, ``power_base``, and ``power_exp`` The following
- hints are supported but not applied unless set to True: ``complex``,
- ``func``, and ``trig``. In addition, the following meta-hints are
- supported by some or all of the other hints: ``frac``, ``numer``,
- ``denom``, ``modulus``, and ``force``. ``deep`` is supported by all
- hints. Additionally, subclasses of Expr may define their own hints or
- meta-hints.
- The ``basic`` hint is used for any special rewriting of an object that
- should be done automatically (along with the other hints like ``mul``)
- when expand is called. This is a catch-all hint to handle any sort of
- expansion that may not be described by the existing hint names. To use
- this hint an object should override the ``_eval_expand_basic`` method.
- Objects may also define their own expand methods, which are not run by
- default. See the API section below.
- If ``deep`` is set to ``True`` (the default), things like arguments of
- functions are recursively expanded. Use ``deep=False`` to only expand on
- the top level.
- If the ``force`` hint is used, assumptions about variables will be ignored
- in making the expansion.
- Hints
- =====
- These hints are run by default
- mul
- ---
- Distributes multiplication over addition:
- >>> from sympy import cos, exp, sin
- >>> from sympy.abc import x, y, z
- >>> (y*(x + z)).expand(mul=True)
- x*y + y*z
- multinomial
- -----------
- Expand (x + y + ...)**n where n is a positive integer.
- >>> ((x + y + z)**2).expand(multinomial=True)
- x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2
- power_exp
- ---------
- Expand addition in exponents into multiplied bases.
- >>> exp(x + y).expand(power_exp=True)
- exp(x)*exp(y)
- >>> (2**(x + y)).expand(power_exp=True)
- 2**x*2**y
- power_base
- ----------
- Split powers of multiplied bases.
- This only happens by default if assumptions allow, or if the
- ``force`` meta-hint is used:
- >>> ((x*y)**z).expand(power_base=True)
- (x*y)**z
- >>> ((x*y)**z).expand(power_base=True, force=True)
- x**z*y**z
- >>> ((2*y)**z).expand(power_base=True)
- 2**z*y**z
- Note that in some cases where this expansion always holds, SymPy performs
- it automatically:
- >>> (x*y)**2
- x**2*y**2
- log
- ---
- Pull out power of an argument as a coefficient and split logs products
- into sums of logs.
- Note that these only work if the arguments of the log function have the
- proper assumptions--the arguments must be positive and the exponents must
- be real--or else the ``force`` hint must be True:
- >>> from sympy import log, symbols
- >>> log(x**2*y).expand(log=True)
- log(x**2*y)
- >>> log(x**2*y).expand(log=True, force=True)
- 2*log(x) + log(y)
- >>> x, y = symbols('x,y', positive=True)
- >>> log(x**2*y).expand(log=True)
- 2*log(x) + log(y)
- basic
- -----
- This hint is intended primarily as a way for custom subclasses to enable
- expansion by default.
- These hints are not run by default:
- complex
- -------
- Split an expression into real and imaginary parts.
- >>> x, y = symbols('x,y')
- >>> (x + y).expand(complex=True)
- re(x) + re(y) + I*im(x) + I*im(y)
- >>> cos(x).expand(complex=True)
- -I*sin(re(x))*sinh(im(x)) + cos(re(x))*cosh(im(x))
- Note that this is just a wrapper around ``as_real_imag()``. Most objects
- that wish to redefine ``_eval_expand_complex()`` should consider
- redefining ``as_real_imag()`` instead.
- func
- ----
- Expand other functions.
- >>> from sympy import gamma
- >>> gamma(x + 1).expand(func=True)
- x*gamma(x)
- trig
- ----
- Do trigonometric expansions.
- >>> cos(x + y).expand(trig=True)
- -sin(x)*sin(y) + cos(x)*cos(y)
- >>> sin(2*x).expand(trig=True)
- 2*sin(x)*cos(x)
- Note that the forms of ``sin(n*x)`` and ``cos(n*x)`` in terms of ``sin(x)``
- and ``cos(x)`` are not unique, due to the identity `\sin^2(x) + \cos^2(x)
- = 1`. The current implementation uses the form obtained from Chebyshev
- polynomials, but this may change. See `this MathWorld article
- <https://mathworld.wolfram.com/Multiple-AngleFormulas.html>`_ for more
- information.
- Notes
- =====
- - You can shut off unwanted methods::
- >>> (exp(x + y)*(x + y)).expand()
- x*exp(x)*exp(y) + y*exp(x)*exp(y)
- >>> (exp(x + y)*(x + y)).expand(power_exp=False)
- x*exp(x + y) + y*exp(x + y)
- >>> (exp(x + y)*(x + y)).expand(mul=False)
- (x + y)*exp(x)*exp(y)
- - Use deep=False to only expand on the top level::
- >>> exp(x + exp(x + y)).expand()
- exp(x)*exp(exp(x)*exp(y))
- >>> exp(x + exp(x + y)).expand(deep=False)
- exp(x)*exp(exp(x + y))
- - Hints are applied in an arbitrary, but consistent order (in the current
- implementation, they are applied in alphabetical order, except
- multinomial comes before mul, but this may change). Because of this,
- some hints may prevent expansion by other hints if they are applied
- first. For example, ``mul`` may distribute multiplications and prevent
- ``log`` and ``power_base`` from expanding them. Also, if ``mul`` is
- applied before ``multinomial`, the expression might not be fully
- distributed. The solution is to use the various ``expand_hint`` helper
- functions or to use ``hint=False`` to this function to finely control
- which hints are applied. Here are some examples::
- >>> from sympy import expand, expand_mul, expand_power_base
- >>> x, y, z = symbols('x,y,z', positive=True)
- >>> expand(log(x*(y + z)))
- log(x) + log(y + z)
- Here, we see that ``log`` was applied before ``mul``. To get the mul
- expanded form, either of the following will work::
- >>> expand_mul(log(x*(y + z)))
- log(x*y + x*z)
- >>> expand(log(x*(y + z)), log=False)
- log(x*y + x*z)
- A similar thing can happen with the ``power_base`` hint::
- >>> expand((x*(y + z))**x)
- (x*y + x*z)**x
- To get the ``power_base`` expanded form, either of the following will
- work::
- >>> expand((x*(y + z))**x, mul=False)
- x**x*(y + z)**x
- >>> expand_power_base((x*(y + z))**x)
- x**x*(y + z)**x
- >>> expand((x + y)*y/x)
- y + y**2/x
- The parts of a rational expression can be targeted::
- >>> expand((x + y)*y/x/(x + 1), frac=True)
- (x*y + y**2)/(x**2 + x)
- >>> expand((x + y)*y/x/(x + 1), numer=True)
- (x*y + y**2)/(x*(x + 1))
- >>> expand((x + y)*y/x/(x + 1), denom=True)
- y*(x + y)/(x**2 + x)
- - The ``modulus`` meta-hint can be used to reduce the coefficients of an
- expression post-expansion::
- >>> expand((3*x + 1)**2)
- 9*x**2 + 6*x + 1
- >>> expand((3*x + 1)**2, modulus=5)
- 4*x**2 + x + 1
- - Either ``expand()`` the function or ``.expand()`` the method can be
- used. Both are equivalent::
- >>> expand((x + 1)**2)
- x**2 + 2*x + 1
- >>> ((x + 1)**2).expand()
- x**2 + 2*x + 1
- API
- ===
- Objects can define their own expand hints by defining
- ``_eval_expand_hint()``. The function should take the form::
- def _eval_expand_hint(self, **hints):
- # Only apply the method to the top-level expression
- ...
- See also the example below. Objects should define ``_eval_expand_hint()``
- methods only if ``hint`` applies to that specific object. The generic
- ``_eval_expand_hint()`` method defined in Expr will handle the no-op case.
- Each hint should be responsible for expanding that hint only.
- Furthermore, the expansion should be applied to the top-level expression
- only. ``expand()`` takes care of the recursion that happens when
- ``deep=True``.
- You should only call ``_eval_expand_hint()`` methods directly if you are
- 100% sure that the object has the method, as otherwise you are liable to
- get unexpected ``AttributeError``s. Note, again, that you do not need to
- recursively apply the hint to args of your object: this is handled
- automatically by ``expand()``. ``_eval_expand_hint()`` should
- generally not be used at all outside of an ``_eval_expand_hint()`` method.
- If you want to apply a specific expansion from within another method, use
- the public ``expand()`` function, method, or ``expand_hint()`` functions.
- In order for expand to work, objects must be rebuildable by their args,
- i.e., ``obj.func(*obj.args) == obj`` must hold.
- Expand methods are passed ``**hints`` so that expand hints may use
- 'metahints'--hints that control how different expand methods are applied.
- For example, the ``force=True`` hint described above that causes
- ``expand(log=True)`` to ignore assumptions is such a metahint. The
- ``deep`` meta-hint is handled exclusively by ``expand()`` and is not
- passed to ``_eval_expand_hint()`` methods.
- Note that expansion hints should generally be methods that perform some
- kind of 'expansion'. For hints that simply rewrite an expression, use the
- .rewrite() API.
- Examples
- ========
- >>> from sympy import Expr, sympify
- >>> class MyClass(Expr):
- ... def __new__(cls, *args):
- ... args = sympify(args)
- ... return Expr.__new__(cls, *args)
- ...
- ... def _eval_expand_double(self, *, force=False, **hints):
- ... '''
- ... Doubles the args of MyClass.
- ...
- ... If there more than four args, doubling is not performed,
- ... unless force=True is also used (False by default).
- ... '''
- ... if not force and len(self.args) > 4:
- ... return self
- ... return self.func(*(self.args + self.args))
- ...
- >>> a = MyClass(1, 2, MyClass(3, 4))
- >>> a
- MyClass(1, 2, MyClass(3, 4))
- >>> a.expand(double=True)
- MyClass(1, 2, MyClass(3, 4, 3, 4), 1, 2, MyClass(3, 4, 3, 4))
- >>> a.expand(double=True, deep=False)
- MyClass(1, 2, MyClass(3, 4), 1, 2, MyClass(3, 4))
- >>> b = MyClass(1, 2, 3, 4, 5)
- >>> b.expand(double=True)
- MyClass(1, 2, 3, 4, 5)
- >>> b.expand(double=True, force=True)
- MyClass(1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
- See Also
- ========
- expand_log, expand_mul, expand_multinomial, expand_complex, expand_trig,
- expand_power_base, expand_power_exp, expand_func, sympy.simplify.hyperexpand.hyperexpand
- """
- # don't modify this; modify the Expr.expand method
- hints['power_base'] = power_base
- hints['power_exp'] = power_exp
- hints['mul'] = mul
- hints['log'] = log
- hints['multinomial'] = multinomial
- hints['basic'] = basic
- return sympify(e).expand(deep=deep, modulus=modulus, **hints)
- # This is a special application of two hints
- def _mexpand(expr, recursive=False):
- # expand multinomials and then expand products; this may not always
- # be sufficient to give a fully expanded expression (see
- # test_issue_8247_8354 in test_arit)
- if expr is None:
- return
- was = None
- while was != expr:
- was, expr = expr, expand_mul(expand_multinomial(expr))
- if not recursive:
- break
- return expr
- # These are simple wrappers around single hints.
- def expand_mul(expr, deep=True):
- """
- Wrapper around expand that only uses the mul hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import symbols, expand_mul, exp, log
- >>> x, y = symbols('x,y', positive=True)
- >>> expand_mul(exp(x+y)*(x+y)*log(x*y**2))
- x*exp(x + y)*log(x*y**2) + y*exp(x + y)*log(x*y**2)
- """
- return sympify(expr).expand(deep=deep, mul=True, power_exp=False,
- power_base=False, basic=False, multinomial=False, log=False)
- def expand_multinomial(expr, deep=True):
- """
- Wrapper around expand that only uses the multinomial hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import symbols, expand_multinomial, exp
- >>> x, y = symbols('x y', positive=True)
- >>> expand_multinomial((x + exp(x + 1))**2)
- x**2 + 2*x*exp(x + 1) + exp(2*x + 2)
- """
- return sympify(expr).expand(deep=deep, mul=False, power_exp=False,
- power_base=False, basic=False, multinomial=True, log=False)
- def expand_log(expr, deep=True, force=False, factor=False):
- """
- Wrapper around expand that only uses the log hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import symbols, expand_log, exp, log
- >>> x, y = symbols('x,y', positive=True)
- >>> expand_log(exp(x+y)*(x+y)*log(x*y**2))
- (x + y)*(log(x) + 2*log(y))*exp(x + y)
- """
- from sympy.functions.elementary.exponential import log
- if factor is False:
- def _handle(x):
- x1 = expand_mul(expand_log(x, deep=deep, force=force, factor=True))
- if x1.count(log) <= x.count(log):
- return x1
- return x
- expr = expr.replace(
- lambda x: x.is_Mul and all(any(isinstance(i, log) and i.args[0].is_Rational
- for i in Mul.make_args(j)) for j in x.as_numer_denom()),
- _handle)
- return sympify(expr).expand(deep=deep, log=True, mul=False,
- power_exp=False, power_base=False, multinomial=False,
- basic=False, force=force, factor=factor)
- def expand_func(expr, deep=True):
- """
- Wrapper around expand that only uses the func hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import expand_func, gamma
- >>> from sympy.abc import x
- >>> expand_func(gamma(x + 2))
- x*(x + 1)*gamma(x)
- """
- return sympify(expr).expand(deep=deep, func=True, basic=False,
- log=False, mul=False, power_exp=False, power_base=False, multinomial=False)
- def expand_trig(expr, deep=True):
- """
- Wrapper around expand that only uses the trig hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import expand_trig, sin
- >>> from sympy.abc import x, y
- >>> expand_trig(sin(x+y)*(x+y))
- (x + y)*(sin(x)*cos(y) + sin(y)*cos(x))
- """
- return sympify(expr).expand(deep=deep, trig=True, basic=False,
- log=False, mul=False, power_exp=False, power_base=False, multinomial=False)
- def expand_complex(expr, deep=True):
- """
- Wrapper around expand that only uses the complex hint. See the expand
- docstring for more information.
- Examples
- ========
- >>> from sympy import expand_complex, exp, sqrt, I
- >>> from sympy.abc import z
- >>> expand_complex(exp(z))
- I*exp(re(z))*sin(im(z)) + exp(re(z))*cos(im(z))
- >>> expand_complex(sqrt(I))
- sqrt(2)/2 + sqrt(2)*I/2
- See Also
- ========
- sympy.core.expr.Expr.as_real_imag
- """
- return sympify(expr).expand(deep=deep, complex=True, basic=False,
- log=False, mul=False, power_exp=False, power_base=False, multinomial=False)
- def expand_power_base(expr, deep=True, force=False):
- """
- Wrapper around expand that only uses the power_base hint.
- A wrapper to expand(power_base=True) which separates a power with a base
- that is a Mul into a product of powers, without performing any other
- expansions, provided that assumptions about the power's base and exponent
- allow.
- deep=False (default is True) will only apply to the top-level expression.
- force=True (default is False) will cause the expansion to ignore
- assumptions about the base and exponent. When False, the expansion will
- only happen if the base is non-negative or the exponent is an integer.
- >>> from sympy.abc import x, y, z
- >>> from sympy import expand_power_base, sin, cos, exp, Symbol
- >>> (x*y)**2
- x**2*y**2
- >>> (2*x)**y
- (2*x)**y
- >>> expand_power_base(_)
- 2**y*x**y
- >>> expand_power_base((x*y)**z)
- (x*y)**z
- >>> expand_power_base((x*y)**z, force=True)
- x**z*y**z
- >>> expand_power_base(sin((x*y)**z), deep=False)
- sin((x*y)**z)
- >>> expand_power_base(sin((x*y)**z), force=True)
- sin(x**z*y**z)
- >>> expand_power_base((2*sin(x))**y + (2*cos(x))**y)
- 2**y*sin(x)**y + 2**y*cos(x)**y
- >>> expand_power_base((2*exp(y))**x)
- 2**x*exp(y)**x
- >>> expand_power_base((2*cos(x))**y)
- 2**y*cos(x)**y
- Notice that sums are left untouched. If this is not the desired behavior,
- apply full ``expand()`` to the expression:
- >>> expand_power_base(((x+y)*z)**2)
- z**2*(x + y)**2
- >>> (((x+y)*z)**2).expand()
- x**2*z**2 + 2*x*y*z**2 + y**2*z**2
- >>> expand_power_base((2*y)**(1+z))
- 2**(z + 1)*y**(z + 1)
- >>> ((2*y)**(1+z)).expand()
- 2*2**z*y**(z + 1)
- The power that is unexpanded can be expanded safely when
- ``y != 0``, otherwise different values might be obtained for the expression:
- >>> prev = _
- If we indicate that ``y`` is positive but then replace it with
- a value of 0 after expansion, the expression becomes 0:
- >>> p = Symbol('p', positive=True)
- >>> prev.subs(y, p).expand().subs(p, 0)
- 0
- But if ``z = -1`` the expression would not be zero:
- >>> prev.subs(y, 0).subs(z, -1)
- 1
- See Also
- ========
- expand
- """
- return sympify(expr).expand(deep=deep, log=False, mul=False,
- power_exp=False, power_base=True, multinomial=False,
- basic=False, force=force)
- def expand_power_exp(expr, deep=True):
- """
- Wrapper around expand that only uses the power_exp hint.
- See the expand docstring for more information.
- Examples
- ========
- >>> from sympy import expand_power_exp, Symbol
- >>> from sympy.abc import x, y
- >>> expand_power_exp(3**(y + 2))
- 9*3**y
- >>> expand_power_exp(x**(y + 2))
- x**(y + 2)
- If ``x = 0`` the value of the expression depends on the
- value of ``y``; if the expression were expanded the result
- would be 0. So expansion is only done if ``x != 0``:
- >>> expand_power_exp(Symbol('x', zero=False)**(y + 2))
- x**2*x**y
- """
- return sympify(expr).expand(deep=deep, complex=False, basic=False,
- log=False, mul=False, power_exp=True, power_base=False, multinomial=False)
- def count_ops(expr, visual=False):
- """
- Return a representation (integer or expression) of the operations in expr.
- Parameters
- ==========
- expr : Expr
- If expr is an iterable, the sum of the op counts of the
- items will be returned.
- visual : bool, optional
- If ``False`` (default) then the sum of the coefficients of the
- visual expression will be returned.
- If ``True`` then the number of each type of operation is shown
- with the core class types (or their virtual equivalent) multiplied by the
- number of times they occur.
- Examples
- ========
- >>> from sympy.abc import a, b, x, y
- >>> from sympy import sin, count_ops
- Although there is not a SUB object, minus signs are interpreted as
- either negations or subtractions:
- >>> (x - y).count_ops(visual=True)
- SUB
- >>> (-x).count_ops(visual=True)
- NEG
- Here, there are two Adds and a Pow:
- >>> (1 + a + b**2).count_ops(visual=True)
- 2*ADD + POW
- In the following, an Add, Mul, Pow and two functions:
- >>> (sin(x)*x + sin(x)**2).count_ops(visual=True)
- ADD + MUL + POW + 2*SIN
- for a total of 5:
- >>> (sin(x)*x + sin(x)**2).count_ops(visual=False)
- 5
- Note that "what you type" is not always what you get. The expression
- 1/x/y is translated by sympy into 1/(x*y) so it gives a DIV and MUL rather
- than two DIVs:
- >>> (1/x/y).count_ops(visual=True)
- DIV + MUL
- The visual option can be used to demonstrate the difference in
- operations for expressions in different forms. Here, the Horner
- representation is compared with the expanded form of a polynomial:
- >>> eq=x*(1 + x*(2 + x*(3 + x)))
- >>> count_ops(eq.expand(), visual=True) - count_ops(eq, visual=True)
- -MUL + 3*POW
- The count_ops function also handles iterables:
- >>> count_ops([x, sin(x), None, True, x + 2], visual=False)
- 2
- >>> count_ops([x, sin(x), None, True, x + 2], visual=True)
- ADD + SIN
- >>> count_ops({x: sin(x), x + 2: y + 1}, visual=True)
- 2*ADD + SIN
- """
- from .relational import Relational
- from sympy.concrete.summations import Sum
- from sympy.integrals.integrals import Integral
- from sympy.logic.boolalg import BooleanFunction
- from sympy.simplify.radsimp import fraction
- expr = sympify(expr)
- if isinstance(expr, Expr) and not expr.is_Relational:
- ops = []
- args = [expr]
- NEG = Symbol('NEG')
- DIV = Symbol('DIV')
- SUB = Symbol('SUB')
- ADD = Symbol('ADD')
- EXP = Symbol('EXP')
- while args:
- a = args.pop()
- # if the following fails because the object is
- # not Basic type, then the object should be fixed
- # since it is the intention that all args of Basic
- # should themselves be Basic
- if a.is_Rational:
- #-1/3 = NEG + DIV
- if a is not S.One:
- if a.p < 0:
- ops.append(NEG)
- if a.q != 1:
- ops.append(DIV)
- continue
- elif a.is_Mul or a.is_MatMul:
- if _coeff_isneg(a):
- ops.append(NEG)
- if a.args[0] is S.NegativeOne:
- a = a.as_two_terms()[1]
- else:
- a = -a
- n, d = fraction(a)
- if n.is_Integer:
- ops.append(DIV)
- if n < 0:
- ops.append(NEG)
- args.append(d)
- continue # won't be -Mul but could be Add
- elif d is not S.One:
- if not d.is_Integer:
- args.append(d)
- ops.append(DIV)
- args.append(n)
- continue # could be -Mul
- elif a.is_Add or a.is_MatAdd:
- aargs = list(a.args)
- negs = 0
- for i, ai in enumerate(aargs):
- if _coeff_isneg(ai):
- negs += 1
- args.append(-ai)
- if i > 0:
- ops.append(SUB)
- else:
- args.append(ai)
- if i > 0:
- ops.append(ADD)
- if negs == len(aargs): # -x - y = NEG + SUB
- ops.append(NEG)
- elif _coeff_isneg(aargs[0]): # -x + y = SUB, but already recorded ADD
- ops.append(SUB - ADD)
- continue
- if a.is_Pow and a.exp is S.NegativeOne:
- ops.append(DIV)
- args.append(a.base) # won't be -Mul but could be Add
- continue
- if a == S.Exp1:
- ops.append(EXP)
- continue
- if a.is_Pow and a.base == S.Exp1:
- ops.append(EXP)
- args.append(a.exp)
- continue
- if a.is_Mul or isinstance(a, LatticeOp):
- o = Symbol(a.func.__name__.upper())
- # count the args
- ops.append(o*(len(a.args) - 1))
- elif a.args and (
- a.is_Pow or
- a.is_Function or
- isinstance(a, Derivative) or
- isinstance(a, Integral) or
- isinstance(a, Sum)):
- # if it's not in the list above we don't
- # consider a.func something to count, e.g.
- # Tuple, MatrixSymbol, etc...
- if isinstance(a.func, UndefinedFunction):
- o = Symbol("FUNC_" + a.func.__name__.upper())
- else:
- o = Symbol(a.func.__name__.upper())
- ops.append(o)
- if not a.is_Symbol:
- args.extend(a.args)
- elif isinstance(expr, Dict):
- ops = [count_ops(k, visual=visual) +
- count_ops(v, visual=visual) for k, v in expr.items()]
- elif iterable(expr):
- ops = [count_ops(i, visual=visual) for i in expr]
- elif isinstance(expr, (Relational, BooleanFunction)):
- ops = []
- for arg in expr.args:
- ops.append(count_ops(arg, visual=True))
- o = Symbol(func_name(expr, short=True).upper())
- ops.append(o)
- elif not isinstance(expr, Basic):
- ops = []
- else: # it's Basic not isinstance(expr, Expr):
- if not isinstance(expr, Basic):
- raise TypeError("Invalid type of expr")
- else:
- ops = []
- args = [expr]
- while args:
- a = args.pop()
- if a.args:
- o = Symbol(type(a).__name__.upper())
- if a.is_Boolean:
- ops.append(o*(len(a.args)-1))
- else:
- ops.append(o)
- args.extend(a.args)
- if not ops:
- if visual:
- return S.Zero
- return 0
- ops = Add(*ops)
- if visual:
- return ops
- if ops.is_Number:
- return int(ops)
- return sum(int((a.args or [1])[0]) for a in Add.make_args(ops))
- def nfloat(expr, n=15, exponent=False, dkeys=False):
- """Make all Rationals in expr Floats except those in exponents
- (unless the exponents flag is set to True) and those in undefined
- functions. When processing dictionaries, do not modify the keys
- unless ``dkeys=True``.
- Examples
- ========
- >>> from sympy import nfloat, cos, pi, sqrt
- >>> from sympy.abc import x, y
- >>> nfloat(x**4 + x/2 + cos(pi/3) + 1 + sqrt(y))
- x**4 + 0.5*x + sqrt(y) + 1.5
- >>> nfloat(x**4 + sqrt(y), exponent=True)
- x**4.0 + y**0.5
- Container types are not modified:
- >>> type(nfloat((1, 2))) is tuple
- True
- """
- from sympy.matrices.matrices import MatrixBase
- kw = {"n": n, "exponent": exponent, "dkeys": dkeys}
- if isinstance(expr, MatrixBase):
- return expr.applyfunc(lambda e: nfloat(e, **kw))
- # handling of iterable containers
- if iterable(expr, exclude=str):
- if isinstance(expr, (dict, Dict)):
- if dkeys:
- args = [tuple((nfloat(i, **kw) for i in a))
- for a in expr.items()]
- else:
- args = [(k, nfloat(v, **kw)) for k, v in expr.items()]
- if isinstance(expr, dict):
- return type(expr)(args)
- else:
- return expr.func(*args)
- elif isinstance(expr, Basic):
- return expr.func(*[nfloat(a, **kw) for a in expr.args])
- return type(expr)([nfloat(a, **kw) for a in expr])
- rv = sympify(expr)
- if rv.is_Number:
- return Float(rv, n)
- elif rv.is_number:
- # evalf doesn't always set the precision
- rv = rv.n(n)
- if rv.is_Number:
- rv = Float(rv.n(n), n)
- else:
- pass # pure_complex(rv) is likely True
- return rv
- elif rv.is_Atom:
- return rv
- elif rv.is_Relational:
- args_nfloat = (nfloat(arg, **kw) for arg in rv.args)
- return rv.func(*args_nfloat)
- # watch out for RootOf instances that don't like to have
- # their exponents replaced with Dummies and also sometimes have
- # problems with evaluating at low precision (issue 6393)
- from sympy.polys.rootoftools import RootOf
- rv = rv.xreplace({ro: ro.n(n) for ro in rv.atoms(RootOf)})
- from .power import Pow
- if not exponent:
- reps = [(p, Pow(p.base, Dummy())) for p in rv.atoms(Pow)]
- rv = rv.xreplace(dict(reps))
- rv = rv.n(n)
- if not exponent:
- rv = rv.xreplace({d.exp: p.exp for p, d in reps})
- else:
- # Pow._eval_evalf special cases Integer exponents so if
- # exponent is suppose to be handled we have to do so here
- rv = rv.xreplace(Transform(
- lambda x: Pow(x.base, Float(x.exp, n)),
- lambda x: x.is_Pow and x.exp.is_Integer))
- return rv.xreplace(Transform(
- lambda x: x.func(*nfloat(x.args, n, exponent)),
- lambda x: isinstance(x, Function) and not isinstance(x, AppliedUndef)))
- from .symbol import Dummy, Symbol
|