[docs]classPiecewise(Function):""" Represents a piecewise function. Usage: Piecewise( (expr,cond), (expr,cond), ... ) - Each argument is a 2-tuple defining an expression and condition - The conds are evaluated in turn returning the first that is True. If any of the evaluated conds are not determined explicitly False, e.g. x < 1, the function is returned in symbolic form. - If the function is evaluated at a place where all conditions are False, a ValueError exception will be raised. - Pairs where the cond is explicitly False, will be removed. Examples ======== >>> from sympy import Piecewise, log >>> from sympy.abc import x >>> f = x**2 >>> g = log(x) >>> p = Piecewise( (0, x<-1), (f, x<=1), (g, True)) >>> p.subs(x,1) 1 >>> p.subs(x,5) log(5) See Also ======== piecewise_fold """nargs=Noneis_Piecewise=Truedef__new__(cls,*args,**options):# (Try to) sympify args firstnewargs=[]forecinargs:pair=ExprCondPair(*ec)cond=pair.condifcond==false:continueifnotisinstance(cond,(bool,Relational,Boolean)):raiseTypeError("Cond %s is of type %s, but must be a Relational,"" Boolean, or a built-in bool."%(cond,type(cond)))newargs.append(pair)ifcond==True:breakifoptions.pop('evaluate',True):r=cls.eval(*newargs)else:r=NoneifrisNone:returnBasic.__new__(cls,*newargs,**options)else:returnr@classmethoddefeval(cls,*args):# Check for situations where we can evaluate the Piecewise object.# 1) Hit an unevaluable cond (e.g. x<1) -> keep object# 2) Hit a true condition -> return that expr# 3) Remove false conditions, if no conditions left -> raise ValueErrorall_conds_evaled=True# Do all conds eval to a bool?piecewise_again=False# Should we pass args to Piecewise again?non_false_ecpairs=[]or1=Or(*[condfor(_,cond)inargsifcond!=true])forexpr,condinargs:# Check here if expr is a Piecewise and collapse if one of# the conds in expr matches cond. This allows the collapsing# of Piecewise((Piecewise(x,x<0),x<0)) to Piecewise((x,x<0)).# This is important when using piecewise_fold to simplify# multiple Piecewise instances having the same conds.# Eventually, this code should be able to collapse Piecewise's# having different intervals, but this will probably require# using the new assumptions.ifisinstance(expr,Piecewise):or2=Or(*[cfor(_,c)inexpr.argsifc!=true])fore,cinexpr.args:# Don't collapse if cond is "True" as this leads to# incorrect simplifications with nested Piecewises.ifc==condand(or1==or2orcond!=true):expr=epiecewise_again=Truecond_eval=cls.__eval_cond(cond)ifcond_evalisNone:all_conds_evaled=Falseelifcond_eval:ifall_conds_evaled:returnexpriflen(non_false_ecpairs)!=0:ifnon_false_ecpairs[-1].cond==cond:continueelifnon_false_ecpairs[-1].expr==expr:newcond=Or(cond,non_false_ecpairs[-1].cond)ifisinstance(newcond,(And,Or)):newcond=distribute_and_over_or(newcond)non_false_ecpairs[-1]=ExprCondPair(expr,newcond)continuenon_false_ecpairs.append(ExprCondPair(expr,cond))iflen(non_false_ecpairs)!=len(args)orpiecewise_again:returncls(*non_false_ecpairs)returnNone

def_eval_as_leading_term(self,x):fore,cinself.args:ifc==Trueorc.subs(x,0)==True:returne.as_leading_term(x)def_eval_adjoint(self):returnself.func(*[(e.adjoint(),c)fore,cinself.args])def_eval_conjugate(self):returnself.func(*[(e.conjugate(),c)fore,cinself.args])def_eval_derivative(self,x):returnself.func(*[(diff(e,x),c)fore,cinself.args])def_eval_evalf(self,prec):returnself.func(*[(e.evalf(prec),c)fore,cinself.args])def_eval_integral(self,x):fromsympy.integralsimportintegratereturnself.func(*[(integrate(e,x),c)fore,cinself.args])def_eval_interval(self,sym,a,b):"""Evaluates the function along the sym in a given interval ab"""# FIXME: Currently complex intervals are not supported. A possible# replacement algorithm, discussed in issue 5227, can be found in the# following papers;# http://portal.acm.org/citation.cfm?id=281649# http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4127&rep=rep1&type=pdfifaisNoneorbisNone:# In this case, it is just simple substitutionreturnpiecewise_fold(super(Piecewise,self)._eval_interval(sym,a,b))mul=1if(a==b)==True:returnS.Zeroelif(a>b)==True:a,b,mul=b,a,-1elif(a<=b)!=True:newargs=[]fore,cinself.args:intervals=self._sort_expr_cond(sym,S.NegativeInfinity,S.Infinity,c)values=[]forlower,upper,exprinintervals:if(a<lower)==True:mid=lowerrep=bval=e._eval_interval(sym,mid,b)val+=self._eval_interval(sym,a,mid)elif(a>upper)==True:mid=upperrep=bval=e._eval_interval(sym,mid,b)val+=self._eval_interval(sym,a,mid)elif(a>=lower)==Trueand(a<=upper)==True:rep=bval=e._eval_interval(sym,a,b)elif(b<lower)==True:mid=lowerrep=aval=e._eval_interval(sym,a,mid)val+=self._eval_interval(sym,mid,b)elif(b>upper)==True:mid=upperrep=aval=e._eval_interval(sym,a,mid)val+=self._eval_interval(sym,mid,b)elif((b>=lower)==True)and((b<=upper)==True):rep=aval=e._eval_interval(sym,a,b)else:raiseNotImplementedError("""The evaluation of a Piecewise interval when both the lower and the upper limit are symbolic is not yet implemented.""")values.append(val)iflen(set(values))==1:try:c=c.subs(sym,rep)exceptAttributeError:passe=values[0]newargs.append((e,c))else:foriinrange(len(values)):newargs.append((values[i],(c==Trueandi==len(values)-1)orAnd(rep>=intervals[i][0],rep<=intervals[i][1])))returnself.func(*newargs)# Determine what intervals the expr,cond pairs affect.int_expr=self._sort_expr_cond(sym,a,b)# Finally run through the intervals and sum the evaluation.ret_fun=0forint_a,int_b,exprinint_expr:ifisinstance(expr,Piecewise):# If we still have a Piecewise by now, _sort_expr_cond would# already have determined that its conditions are independent# of the integration variable, thus we just use substitution.ret_fun+=piecewise_fold(super(Piecewise,expr)._eval_interval(sym,Max(a,int_a),Min(b,int_b)))else:ret_fun+=expr._eval_interval(sym,Max(a,int_a),Min(b,int_b))returnmul*ret_fundef_sort_expr_cond(self,sym,a,b,targetcond=None):"""Determine what intervals the expr, cond pairs affect. 1) If cond is True, then log it as default 1.1) Currently if cond can't be evaluated, throw NotImplementedError. 2) For each inequality, if previous cond defines part of the interval update the new conds interval. - eg x < 1, x < 3 -> [oo,1],[1,3] instead of [oo,1],[oo,3] 3) Sort the intervals to make it easier to find correct exprs Under normal use, we return the expr,cond pairs in increasing order along the real axis corresponding to the symbol sym. If targetcond is given, we return a list of (lowerbound, upperbound) pairs for this condition."""fromsympy.solvers.inequalitiesimport_solve_inequalitydefault=Noneint_expr=[]expr_cond=[]or_cond=Falseor_intervals=[]independent_expr_cond=[]forexpr,condinself.args:ifisinstance(cond,Or):forcond2insorted(cond.args,key=default_sort_key):expr_cond.append((expr,cond2))else:expr_cond.append((expr,cond))ifcond==True:breakforexpr,condinexpr_cond:ifcond==True:independent_expr_cond.append((expr,cond))default=self.func(*independent_expr_cond)breakorig_cond=condifsymnotincond.free_symbols:independent_expr_cond.append((expr,cond))continueelifisinstance(cond,Equality):continueelifisinstance(cond,And):lower=S.NegativeInfinityupper=S.Infinityforcond2incond.args:ifsymnotin[cond2.lts,cond2.gts]:cond2=_solve_inequality(cond2,sym)ifcond2.lts==sym:upper=Min(cond2.gts,upper)elifcond2.gts==sym:lower=Max(cond2.lts,lower)else:raiseNotImplementedError("Unable to handle interval evaluation of expression.")else:ifsymnotin[cond.lts,cond.gts]:cond=_solve_inequality(cond,sym)lower,upper=cond.lts,cond.gts# part 1: initialize with givensifcond.lts==sym:# part 1a: expand the side ...lower=S.NegativeInfinity# e.g. x <= 0 ---> -oo <= 0elifcond.gts==sym:# part 1a: ... that can be expandedupper=S.Infinity# e.g. x >= 0 ---> oo >= 0else:raiseNotImplementedError("Unable to handle interval evaluation of expression.")# part 1b: Reduce (-)infinity to what was passed in.lower,upper=Max(a,lower),Min(b,upper)forninxrange(len(int_expr)):# Part 2: remove any interval overlap. For any conflicts, the# iterval already there wins, and the incoming interval updates# its bounds accordingly.ifself.__eval_cond(lower<int_expr[n][1])and \
self.__eval_cond(lower>=int_expr[n][0]):lower=int_expr[n][1]eliflen(int_expr[n][1].free_symbols)and \
self.__eval_cond(lower>=int_expr[n][0]):ifself.__eval_cond(lower==int_expr[n][0]):lower=int_expr[n][1]else:int_expr[n][1]=Min(lower,int_expr[n][1])eliflen(int_expr[n][0].free_symbols)and \
self.__eval_cond(upper==int_expr[n][1]):upper=Min(upper,int_expr[n][0])eliflen(int_expr[n][1].free_symbols)and \
(lower>=int_expr[n][0])!=Trueand \
(int_expr[n][1]==Min(lower,upper))!=True:upper=Min(upper,int_expr[n][0])elifself.__eval_cond(upper>int_expr[n][0])and \
self.__eval_cond(upper<=int_expr[n][1]):upper=int_expr[n][0]eliflen(int_expr[n][0].free_symbols)and \
self.__eval_cond(upper<int_expr[n][1]):int_expr[n][0]=Max(upper,int_expr[n][0])ifself.__eval_cond(lower>=upper)!=True:# Is it still an interval?int_expr.append([lower,upper,expr])iforig_cond==targetcond:return[(lower,upper,None)]elifisinstance(targetcond,Or)andcondintargetcond.args:or_cond=Or(or_cond,cond)or_intervals.append((lower,upper,None))ifor_cond==targetcond:or_intervals.sort(key=lambdax:x[0])returnor_intervalsint_expr.sort(key=lambdax:x[1].sort_key()ifx[1].is_numberelseS.NegativeInfinity.sort_key())int_expr.sort(key=lambdax:x[0].sort_key()ifx[0].is_numberelseS.Infinity.sort_key())forninxrange(len(int_expr)):iflen(int_expr[n][0].free_symbols)orlen(int_expr[n][1].free_symbols):ifisinstance(int_expr[n][1],Min)orint_expr[n][1]==b:newval=Min(*int_expr[n][:-1])ifn>0andint_expr[n][0]==int_expr[n-1][1]:int_expr[n-1][1]=newvalint_expr[n][0]=newvalelse:newval=Max(*int_expr[n][:-1])ifn<len(int_expr)-1andint_expr[n][1]==int_expr[n+1][0]:int_expr[n+1][0]=newvalint_expr[n][1]=newval# Add holes to list of intervals if there is a default value,# otherwise raise a ValueError.holes=[]curr_low=aforint_a,int_b,exprinint_expr:if(curr_low<int_a)==True:holes.append([curr_low,Min(b,int_a),default])elif(curr_low>=int_a)!=True:holes.append([curr_low,Min(b,int_a),default])curr_low=Min(b,int_b)if(curr_low<b)==True:holes.append([Min(b,curr_low),b,default])elif(curr_low>=b)!=True:holes.append([Min(b,curr_low),b,default])ifholesanddefaultisnotNone:int_expr.extend(holes)iftargetcond==True:return[(h[0],h[1],None)forhinholes]elifholesanddefaultisNone:raiseValueError("Called interval evaluation over piecewise ""function on undefined intervals %s"%", ".join([str((h[0],h[1]))forhinholes]))returnint_exprdef_eval_nseries(self,x,n,logx):args=[(ec.expr._eval_nseries(x,n,logx),ec.cond)forecinself.args]returnself.func(*args)def_eval_power(self,s):returnself.func(*[(e**s,c)fore,cinself.args])def_eval_subs(self,old,new):""" Piecewise conditions may contain bool which are not of Basic type. """args=list(self.args)fori,(e,c)inenumerate(args):ifisinstance(c,bool):passelifisinstance(c,Basic):c=c._subs(old,new)ifc!=False:e=e._subs(old,new)args[i]=e,cifc==True:returnself.func(*args)returnself.func(*args)def_eval_transpose(self):returnself.func(*[(e.transpose(),c)fore,cinself.args])def_eval_template_is_attr(self,is_attr,when_multiple=None):b=Noneforexpr,_inself.args:a=getattr(expr,is_attr)ifaisNone:returnNoneifbisNone:b=aelifbisnota:returnwhen_multiplereturnb_eval_is_finite=lambdaself:self._eval_template_is_attr('is_finite',when_multiple=False)_eval_is_complex=lambdaself:self._eval_template_is_attr('is_complex')_eval_is_even=lambdaself:self._eval_template_is_attr('is_even')_eval_is_imaginary=lambdaself:self._eval_template_is_attr('is_imaginary')_eval_is_integer=lambdaself:self._eval_template_is_attr('is_integer')_eval_is_irrational=lambdaself:self._eval_template_is_attr('is_irrational')_eval_is_negative=lambdaself:self._eval_template_is_attr('is_negative')_eval_is_nonnegative=lambdaself:self._eval_template_is_attr('is_nonnegative')_eval_is_nonpositive=lambdaself:self._eval_template_is_attr('is_nonpositive')_eval_is_nonzero=lambdaself:self._eval_template_is_attr('is_nonzero',when_multiple=True)_eval_is_odd=lambdaself:self._eval_template_is_attr('is_odd')_eval_is_polar=lambdaself:self._eval_template_is_attr('is_polar')_eval_is_positive=lambdaself:self._eval_template_is_attr('is_positive')_eval_is_real=lambdaself:self._eval_template_is_attr('is_real')_eval_is_zero=lambdaself:self._eval_template_is_attr('is_zero',when_multiple=False)@classmethoddef__eval_cond(cls,cond):"""Return the truth value of the condition."""fromsympy.solvers.solversimportchecksolifcond==True:returnTrueifisinstance(cond,Equality):ifchecksol(cond,{},minimal=True):# the equality is trivially solvedreturnTruediff=cond.lhs-cond.rhsifdiff.is_commutative:returndiff.is_zeroreturnNonedefas_expr_set_pairs(self):exp_sets=[]U=S.Realsforexpr,condinself.args:cond_int=U.intersect(cond.as_set())U=U-cond_intexp_sets.append((expr,cond_int))returnexp_sets