piecewise.py 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506
  1. from sympy.core import S, Function, diff, Tuple, Dummy, Mul
  2. from sympy.core.basic import Basic, as_Basic
  3. from sympy.core.numbers import Rational, NumberSymbol, _illegal
  4. from sympy.core.parameters import global_parameters
  5. from sympy.core.relational import (Lt, Gt, Eq, Ne, Relational,
  6. _canonical, _canonical_coeff)
  7. from sympy.core.sorting import ordered
  8. from sympy.functions.elementary.miscellaneous import Max, Min
  9. from sympy.logic.boolalg import (And, Boolean, distribute_and_over_or, Not,
  10. true, false, Or, ITE, simplify_logic, to_cnf, distribute_or_over_and)
  11. from sympy.utilities.iterables import uniq, sift, common_prefix
  12. from sympy.utilities.misc import filldedent, func_name
  13. from itertools import product
  14. Undefined = S.NaN # Piecewise()
  15. class ExprCondPair(Tuple):
  16. """Represents an expression, condition pair."""
  17. def __new__(cls, expr, cond):
  18. expr = as_Basic(expr)
  19. if cond == True:
  20. return Tuple.__new__(cls, expr, true)
  21. elif cond == False:
  22. return Tuple.__new__(cls, expr, false)
  23. elif isinstance(cond, Basic) and cond.has(Piecewise):
  24. cond = piecewise_fold(cond)
  25. if isinstance(cond, Piecewise):
  26. cond = cond.rewrite(ITE)
  27. if not isinstance(cond, Boolean):
  28. raise TypeError(filldedent('''
  29. Second argument must be a Boolean,
  30. not `%s`''' % func_name(cond)))
  31. return Tuple.__new__(cls, expr, cond)
  32. @property
  33. def expr(self):
  34. """
  35. Returns the expression of this pair.
  36. """
  37. return self.args[0]
  38. @property
  39. def cond(self):
  40. """
  41. Returns the condition of this pair.
  42. """
  43. return self.args[1]
  44. @property
  45. def is_commutative(self):
  46. return self.expr.is_commutative
  47. def __iter__(self):
  48. yield self.expr
  49. yield self.cond
  50. def _eval_simplify(self, **kwargs):
  51. return self.func(*[a.simplify(**kwargs) for a in self.args])
  52. class Piecewise(Function):
  53. """
  54. Represents a piecewise function.
  55. Usage:
  56. Piecewise( (expr,cond), (expr,cond), ... )
  57. - Each argument is a 2-tuple defining an expression and condition
  58. - The conds are evaluated in turn returning the first that is True.
  59. If any of the evaluated conds are not explicitly False,
  60. e.g. ``x < 1``, the function is returned in symbolic form.
  61. - If the function is evaluated at a place where all conditions are False,
  62. nan will be returned.
  63. - Pairs where the cond is explicitly False, will be removed and no pair
  64. appearing after a True condition will ever be retained. If a single
  65. pair with a True condition remains, it will be returned, even when
  66. evaluation is False.
  67. Examples
  68. ========
  69. >>> from sympy import Piecewise, log, piecewise_fold
  70. >>> from sympy.abc import x, y
  71. >>> f = x**2
  72. >>> g = log(x)
  73. >>> p = Piecewise((0, x < -1), (f, x <= 1), (g, True))
  74. >>> p.subs(x,1)
  75. 1
  76. >>> p.subs(x,5)
  77. log(5)
  78. Booleans can contain Piecewise elements:
  79. >>> cond = (x < y).subs(x, Piecewise((2, x < 0), (3, True))); cond
  80. Piecewise((2, x < 0), (3, True)) < y
  81. The folded version of this results in a Piecewise whose
  82. expressions are Booleans:
  83. >>> folded_cond = piecewise_fold(cond); folded_cond
  84. Piecewise((2 < y, x < 0), (3 < y, True))
  85. When a Boolean containing Piecewise (like cond) or a Piecewise
  86. with Boolean expressions (like folded_cond) is used as a condition,
  87. it is converted to an equivalent :class:`~.ITE` object:
  88. >>> Piecewise((1, folded_cond))
  89. Piecewise((1, ITE(x < 0, y > 2, y > 3)))
  90. When a condition is an ``ITE``, it will be converted to a simplified
  91. Boolean expression:
  92. >>> piecewise_fold(_)
  93. Piecewise((1, ((x >= 0) | (y > 2)) & ((y > 3) | (x < 0))))
  94. See Also
  95. ========
  96. piecewise_fold
  97. piecewise_exclusive
  98. ITE
  99. """
  100. nargs = None
  101. is_Piecewise = True
  102. def __new__(cls, *args, **options):
  103. if len(args) == 0:
  104. raise TypeError("At least one (expr, cond) pair expected.")
  105. # (Try to) sympify args first
  106. newargs = []
  107. for ec in args:
  108. # ec could be a ExprCondPair or a tuple
  109. pair = ExprCondPair(*getattr(ec, 'args', ec))
  110. cond = pair.cond
  111. if cond is false:
  112. continue
  113. newargs.append(pair)
  114. if cond is true:
  115. break
  116. eval = options.pop('evaluate', global_parameters.evaluate)
  117. if eval:
  118. r = cls.eval(*newargs)
  119. if r is not None:
  120. return r
  121. elif len(newargs) == 1 and newargs[0].cond == True:
  122. return newargs[0].expr
  123. return Basic.__new__(cls, *newargs, **options)
  124. @classmethod
  125. def eval(cls, *_args):
  126. """Either return a modified version of the args or, if no
  127. modifications were made, return None.
  128. Modifications that are made here:
  129. 1. relationals are made canonical
  130. 2. any False conditions are dropped
  131. 3. any repeat of a previous condition is ignored
  132. 4. any args past one with a true condition are dropped
  133. If there are no args left, nan will be returned.
  134. If there is a single arg with a True condition, its
  135. corresponding expression will be returned.
  136. EXAMPLES
  137. ========
  138. >>> from sympy import Piecewise
  139. >>> from sympy.abc import x
  140. >>> cond = -x < -1
  141. >>> args = [(1, cond), (4, cond), (3, False), (2, True), (5, x < 1)]
  142. >>> Piecewise(*args, evaluate=False)
  143. Piecewise((1, -x < -1), (4, -x < -1), (2, True))
  144. >>> Piecewise(*args)
  145. Piecewise((1, x > 1), (2, True))
  146. """
  147. if not _args:
  148. return Undefined
  149. if len(_args) == 1 and _args[0][-1] == True:
  150. return _args[0][0]
  151. newargs = _piecewise_collapse_arguments(_args)
  152. # some conditions may have been redundant
  153. missing = len(newargs) != len(_args)
  154. # some conditions may have changed
  155. same = all(a == b for a, b in zip(newargs, _args))
  156. # if either change happened we return the expr with the
  157. # updated args
  158. if not newargs:
  159. raise ValueError(filldedent('''
  160. There are no conditions (or none that
  161. are not trivially false) to define an
  162. expression.'''))
  163. if missing or not same:
  164. return cls(*newargs)
  165. def doit(self, **hints):
  166. """
  167. Evaluate this piecewise function.
  168. """
  169. newargs = []
  170. for e, c in self.args:
  171. if hints.get('deep', True):
  172. if isinstance(e, Basic):
  173. newe = e.doit(**hints)
  174. if newe != self:
  175. e = newe
  176. if isinstance(c, Basic):
  177. c = c.doit(**hints)
  178. newargs.append((e, c))
  179. return self.func(*newargs)
  180. def _eval_simplify(self, **kwargs):
  181. return piecewise_simplify(self, **kwargs)
  182. def _eval_as_leading_term(self, x, logx=None, cdir=0):
  183. for e, c in self.args:
  184. if c == True or c.subs(x, 0) == True:
  185. return e.as_leading_term(x)
  186. def _eval_adjoint(self):
  187. return self.func(*[(e.adjoint(), c) for e, c in self.args])
  188. def _eval_conjugate(self):
  189. return self.func(*[(e.conjugate(), c) for e, c in self.args])
  190. def _eval_derivative(self, x):
  191. return self.func(*[(diff(e, x), c) for e, c in self.args])
  192. def _eval_evalf(self, prec):
  193. return self.func(*[(e._evalf(prec), c) for e, c in self.args])
  194. def _eval_is_meromorphic(self, x, a):
  195. # Conditions often implicitly assume that the argument is real.
  196. # Hence, there needs to be some check for as_set.
  197. if not a.is_real:
  198. return None
  199. # Then, scan ExprCondPairs in the given order to find a piece that would contain a,
  200. # possibly as a boundary point.
  201. for e, c in self.args:
  202. cond = c.subs(x, a)
  203. if cond.is_Relational:
  204. return None
  205. if a in c.as_set().boundary:
  206. return None
  207. # Apply expression if a is an interior point of the domain of e.
  208. if cond:
  209. return e._eval_is_meromorphic(x, a)
  210. def piecewise_integrate(self, x, **kwargs):
  211. """Return the Piecewise with each expression being
  212. replaced with its antiderivative. To obtain a continuous
  213. antiderivative, use the :func:`~.integrate` function or method.
  214. Examples
  215. ========
  216. >>> from sympy import Piecewise
  217. >>> from sympy.abc import x
  218. >>> p = Piecewise((0, x < 0), (1, x < 1), (2, True))
  219. >>> p.piecewise_integrate(x)
  220. Piecewise((0, x < 0), (x, x < 1), (2*x, True))
  221. Note that this does not give a continuous function, e.g.
  222. at x = 1 the 3rd condition applies and the antiderivative
  223. there is 2*x so the value of the antiderivative is 2:
  224. >>> anti = _
  225. >>> anti.subs(x, 1)
  226. 2
  227. The continuous derivative accounts for the integral *up to*
  228. the point of interest, however:
  229. >>> p.integrate(x)
  230. Piecewise((0, x < 0), (x, x < 1), (2*x - 1, True))
  231. >>> _.subs(x, 1)
  232. 1
  233. See Also
  234. ========
  235. Piecewise._eval_integral
  236. """
  237. from sympy.integrals import integrate
  238. return self.func(*[(integrate(e, x, **kwargs), c) for e, c in self.args])
  239. def _handle_irel(self, x, handler):
  240. """Return either None (if the conditions of self depend only on x) else
  241. a Piecewise expression whose expressions (handled by the handler that
  242. was passed) are paired with the governing x-independent relationals,
  243. e.g. Piecewise((A, a(x) & b(y)), (B, c(x) | c(y)) ->
  244. Piecewise(
  245. (handler(Piecewise((A, a(x) & True), (B, c(x) | True)), b(y) & c(y)),
  246. (handler(Piecewise((A, a(x) & True), (B, c(x) | False)), b(y)),
  247. (handler(Piecewise((A, a(x) & False), (B, c(x) | True)), c(y)),
  248. (handler(Piecewise((A, a(x) & False), (B, c(x) | False)), True))
  249. """
  250. # identify governing relationals
  251. rel = self.atoms(Relational)
  252. irel = list(ordered([r for r in rel if x not in r.free_symbols
  253. and r not in (S.true, S.false)]))
  254. if irel:
  255. args = {}
  256. exprinorder = []
  257. for truth in product((1, 0), repeat=len(irel)):
  258. reps = dict(zip(irel, truth))
  259. # only store the true conditions since the false are implied
  260. # when they appear lower in the Piecewise args
  261. if 1 not in truth:
  262. cond = None # flag this one so it doesn't get combined
  263. else:
  264. andargs = Tuple(*[i for i in reps if reps[i]])
  265. free = list(andargs.free_symbols)
  266. if len(free) == 1:
  267. from sympy.solvers.inequalities import (
  268. reduce_inequalities, _solve_inequality)
  269. try:
  270. t = reduce_inequalities(andargs, free[0])
  271. # ValueError when there are potentially
  272. # nonvanishing imaginary parts
  273. except (ValueError, NotImplementedError):
  274. # at least isolate free symbol on left
  275. t = And(*[_solve_inequality(
  276. a, free[0], linear=True)
  277. for a in andargs])
  278. else:
  279. t = And(*andargs)
  280. if t is S.false:
  281. continue # an impossible combination
  282. cond = t
  283. expr = handler(self.xreplace(reps))
  284. if isinstance(expr, self.func) and len(expr.args) == 1:
  285. expr, econd = expr.args[0]
  286. cond = And(econd, True if cond is None else cond)
  287. # the ec pairs are being collected since all possibilities
  288. # are being enumerated, but don't put the last one in since
  289. # its expr might match a previous expression and it
  290. # must appear last in the args
  291. if cond is not None:
  292. args.setdefault(expr, []).append(cond)
  293. # but since we only store the true conditions we must maintain
  294. # the order so that the expression with the most true values
  295. # comes first
  296. exprinorder.append(expr)
  297. # convert collected conditions as args of Or
  298. for k in args:
  299. args[k] = Or(*args[k])
  300. # take them in the order obtained
  301. args = [(e, args[e]) for e in uniq(exprinorder)]
  302. # add in the last arg
  303. args.append((expr, True))
  304. return Piecewise(*args)
  305. def _eval_integral(self, x, _first=True, **kwargs):
  306. """Return the indefinite integral of the
  307. Piecewise such that subsequent substitution of x with a
  308. value will give the value of the integral (not including
  309. the constant of integration) up to that point. To only
  310. integrate the individual parts of Piecewise, use the
  311. ``piecewise_integrate`` method.
  312. Examples
  313. ========
  314. >>> from sympy import Piecewise
  315. >>> from sympy.abc import x
  316. >>> p = Piecewise((0, x < 0), (1, x < 1), (2, True))
  317. >>> p.integrate(x)
  318. Piecewise((0, x < 0), (x, x < 1), (2*x - 1, True))
  319. >>> p.piecewise_integrate(x)
  320. Piecewise((0, x < 0), (x, x < 1), (2*x, True))
  321. See Also
  322. ========
  323. Piecewise.piecewise_integrate
  324. """
  325. from sympy.integrals.integrals import integrate
  326. if _first:
  327. def handler(ipw):
  328. if isinstance(ipw, self.func):
  329. return ipw._eval_integral(x, _first=False, **kwargs)
  330. else:
  331. return ipw.integrate(x, **kwargs)
  332. irv = self._handle_irel(x, handler)
  333. if irv is not None:
  334. return irv
  335. # handle a Piecewise from -oo to oo with and no x-independent relationals
  336. # -----------------------------------------------------------------------
  337. ok, abei = self._intervals(x)
  338. if not ok:
  339. from sympy.integrals.integrals import Integral
  340. return Integral(self, x) # unevaluated
  341. pieces = [(a, b) for a, b, _, _ in abei]
  342. oo = S.Infinity
  343. done = [(-oo, oo, -1)]
  344. for k, p in enumerate(pieces):
  345. if p == (-oo, oo):
  346. # all undone intervals will get this key
  347. for j, (a, b, i) in enumerate(done):
  348. if i == -1:
  349. done[j] = a, b, k
  350. break # nothing else to consider
  351. N = len(done) - 1
  352. for j, (a, b, i) in enumerate(reversed(done)):
  353. if i == -1:
  354. j = N - j
  355. done[j: j + 1] = _clip(p, (a, b), k)
  356. done = [(a, b, i) for a, b, i in done if a != b]
  357. # append an arg if there is a hole so a reference to
  358. # argument -1 will give Undefined
  359. if any(i == -1 for (a, b, i) in done):
  360. abei.append((-oo, oo, Undefined, -1))
  361. # return the sum of the intervals
  362. args = []
  363. sum = None
  364. for a, b, i in done:
  365. anti = integrate(abei[i][-2], x, **kwargs)
  366. if sum is None:
  367. sum = anti
  368. else:
  369. sum = sum.subs(x, a)
  370. e = anti._eval_interval(x, a, x)
  371. if sum.has(*_illegal) or e.has(*_illegal):
  372. sum = anti
  373. else:
  374. sum += e
  375. # see if we know whether b is contained in original
  376. # condition
  377. if b is S.Infinity:
  378. cond = True
  379. elif self.args[abei[i][-1]].cond.subs(x, b) == False:
  380. cond = (x < b)
  381. else:
  382. cond = (x <= b)
  383. args.append((sum, cond))
  384. return Piecewise(*args)
  385. def _eval_interval(self, sym, a, b, _first=True):
  386. """Evaluates the function along the sym in a given interval [a, b]"""
  387. # FIXME: Currently complex intervals are not supported. A possible
  388. # replacement algorithm, discussed in issue 5227, can be found in the
  389. # following papers;
  390. # http://portal.acm.org/citation.cfm?id=281649
  391. # http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4127&rep=rep1&type=pdf
  392. if a is None or b is None:
  393. # In this case, it is just simple substitution
  394. return super()._eval_interval(sym, a, b)
  395. else:
  396. x, lo, hi = map(as_Basic, (sym, a, b))
  397. if _first: # get only x-dependent relationals
  398. def handler(ipw):
  399. if isinstance(ipw, self.func):
  400. return ipw._eval_interval(x, lo, hi, _first=None)
  401. else:
  402. return ipw._eval_interval(x, lo, hi)
  403. irv = self._handle_irel(x, handler)
  404. if irv is not None:
  405. return irv
  406. if (lo < hi) is S.false or (
  407. lo is S.Infinity or hi is S.NegativeInfinity):
  408. rv = self._eval_interval(x, hi, lo, _first=False)
  409. if isinstance(rv, Piecewise):
  410. rv = Piecewise(*[(-e, c) for e, c in rv.args])
  411. else:
  412. rv = -rv
  413. return rv
  414. if (lo < hi) is S.true or (
  415. hi is S.Infinity or lo is S.NegativeInfinity):
  416. pass
  417. else:
  418. _a = Dummy('lo')
  419. _b = Dummy('hi')
  420. a = lo if lo.is_comparable else _a
  421. b = hi if hi.is_comparable else _b
  422. pos = self._eval_interval(x, a, b, _first=False)
  423. if a == _a and b == _b:
  424. # it's purely symbolic so just swap lo and hi and
  425. # change the sign to get the value for when lo > hi
  426. neg, pos = (-pos.xreplace({_a: hi, _b: lo}),
  427. pos.xreplace({_a: lo, _b: hi}))
  428. else:
  429. # at least one of the bounds was comparable, so allow
  430. # _eval_interval to use that information when computing
  431. # the interval with lo and hi reversed
  432. neg, pos = (-self._eval_interval(x, hi, lo, _first=False),
  433. pos.xreplace({_a: lo, _b: hi}))
  434. # allow simplification based on ordering of lo and hi
  435. p = Dummy('', positive=True)
  436. if lo.is_Symbol:
  437. pos = pos.xreplace({lo: hi - p}).xreplace({p: hi - lo})
  438. neg = neg.xreplace({lo: hi + p}).xreplace({p: lo - hi})
  439. elif hi.is_Symbol:
  440. pos = pos.xreplace({hi: lo + p}).xreplace({p: hi - lo})
  441. neg = neg.xreplace({hi: lo - p}).xreplace({p: lo - hi})
  442. # evaluate limits that may have unevaluate Min/Max
  443. touch = lambda _: _.replace(
  444. lambda x: isinstance(x, (Min, Max)),
  445. lambda x: x.func(*x.args))
  446. neg = touch(neg)
  447. pos = touch(pos)
  448. # assemble return expression; make the first condition be Lt
  449. # b/c then the first expression will look the same whether
  450. # the lo or hi limit is symbolic
  451. if a == _a: # the lower limit was symbolic
  452. rv = Piecewise(
  453. (pos,
  454. lo < hi),
  455. (neg,
  456. True))
  457. else:
  458. rv = Piecewise(
  459. (neg,
  460. hi < lo),
  461. (pos,
  462. True))
  463. if rv == Undefined:
  464. raise ValueError("Can't integrate across undefined region.")
  465. if any(isinstance(i, Piecewise) for i in (pos, neg)):
  466. rv = piecewise_fold(rv)
  467. return rv
  468. # handle a Piecewise with lo <= hi and no x-independent relationals
  469. # -----------------------------------------------------------------
  470. ok, abei = self._intervals(x)
  471. if not ok:
  472. from sympy.integrals.integrals import Integral
  473. # not being able to do the interval of f(x) can
  474. # be stated as not being able to do the integral
  475. # of f'(x) over the same range
  476. return Integral(self.diff(x), (x, lo, hi)) # unevaluated
  477. pieces = [(a, b) for a, b, _, _ in abei]
  478. done = [(lo, hi, -1)]
  479. oo = S.Infinity
  480. for k, p in enumerate(pieces):
  481. if p[:2] == (-oo, oo):
  482. # all undone intervals will get this key
  483. for j, (a, b, i) in enumerate(done):
  484. if i == -1:
  485. done[j] = a, b, k
  486. break # nothing else to consider
  487. N = len(done) - 1
  488. for j, (a, b, i) in enumerate(reversed(done)):
  489. if i == -1:
  490. j = N - j
  491. done[j: j + 1] = _clip(p, (a, b), k)
  492. done = [(a, b, i) for a, b, i in done if a != b]
  493. # return the sum of the intervals
  494. sum = S.Zero
  495. upto = None
  496. for a, b, i in done:
  497. if i == -1:
  498. if upto is None:
  499. return Undefined
  500. # TODO simplify hi <= upto
  501. return Piecewise((sum, hi <= upto), (Undefined, True))
  502. sum += abei[i][-2]._eval_interval(x, a, b)
  503. upto = b
  504. return sum
  505. def _intervals(self, sym, err_on_Eq=False):
  506. r"""Return a bool and a message (when bool is False), else a
  507. list of unique tuples, (a, b, e, i), where a and b
  508. are the lower and upper bounds in which the expression e of
  509. argument i in self is defined and $a < b$ (when involving
  510. numbers) or $a \le b$ when involving symbols.
  511. If there are any relationals not involving sym, or any
  512. relational cannot be solved for sym, the bool will be False
  513. a message be given as the second return value. The calling
  514. routine should have removed such relationals before calling
  515. this routine.
  516. The evaluated conditions will be returned as ranges.
  517. Discontinuous ranges will be returned separately with
  518. identical expressions. The first condition that evaluates to
  519. True will be returned as the last tuple with a, b = -oo, oo.
  520. """
  521. from sympy.solvers.inequalities import _solve_inequality
  522. assert isinstance(self, Piecewise)
  523. def nonsymfail(cond):
  524. return False, filldedent('''
  525. A condition not involving
  526. %s appeared: %s''' % (sym, cond))
  527. def _solve_relational(r):
  528. if sym not in r.free_symbols:
  529. return nonsymfail(r)
  530. try:
  531. rv = _solve_inequality(r, sym)
  532. except NotImplementedError:
  533. return False, 'Unable to solve relational %s for %s.' % (r, sym)
  534. if isinstance(rv, Relational):
  535. free = rv.args[1].free_symbols
  536. if rv.args[0] != sym or sym in free:
  537. return False, 'Unable to solve relational %s for %s.' % (r, sym)
  538. if rv.rel_op == '==':
  539. # this equality has been affirmed to have the form
  540. # Eq(sym, rhs) where rhs is sym-free; it represents
  541. # a zero-width interval which will be ignored
  542. # whether it is an isolated condition or contained
  543. # within an And or an Or
  544. rv = S.false
  545. elif rv.rel_op == '!=':
  546. try:
  547. rv = Or(sym < rv.rhs, sym > rv.rhs)
  548. except TypeError:
  549. # e.g. x != I ==> all real x satisfy
  550. rv = S.true
  551. elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity):
  552. rv = S.true
  553. return True, rv
  554. args = list(self.args)
  555. # make self canonical wrt Relationals
  556. keys = self.atoms(Relational)
  557. reps = {}
  558. for r in keys:
  559. ok, s = _solve_relational(r)
  560. if ok != True:
  561. return False, ok
  562. reps[r] = s
  563. # process args individually so if any evaluate, their position
  564. # in the original Piecewise will be known
  565. args = [i.xreplace(reps) for i in self.args]
  566. # precondition args
  567. expr_cond = []
  568. default = idefault = None
  569. for i, (expr, cond) in enumerate(args):
  570. if cond is S.false:
  571. continue
  572. if cond is S.true:
  573. default = expr
  574. idefault = i
  575. break
  576. if isinstance(cond, Eq):
  577. # unanticipated condition, but it is here in case a
  578. # replacement caused an Eq to appear
  579. if err_on_Eq:
  580. return False, 'encountered Eq condition: %s' % cond
  581. continue # zero width interval
  582. cond = to_cnf(cond)
  583. if isinstance(cond, And):
  584. cond = distribute_or_over_and(cond)
  585. if isinstance(cond, Or):
  586. expr_cond.extend(
  587. [(i, expr, o) for o in cond.args
  588. if not isinstance(o, Eq)])
  589. elif cond is not S.false:
  590. expr_cond.append((i, expr, cond))
  591. elif cond is S.true:
  592. default = expr
  593. idefault = i
  594. break
  595. # determine intervals represented by conditions
  596. int_expr = []
  597. for iarg, expr, cond in expr_cond:
  598. if isinstance(cond, And):
  599. lower = S.NegativeInfinity
  600. upper = S.Infinity
  601. exclude = []
  602. for cond2 in cond.args:
  603. if not isinstance(cond2, Relational):
  604. return False, 'expecting only Relationals'
  605. if isinstance(cond2, Eq):
  606. lower = upper # ignore
  607. if err_on_Eq:
  608. return False, 'encountered secondary Eq condition'
  609. break
  610. elif isinstance(cond2, Ne):
  611. l, r = cond2.args
  612. if l == sym:
  613. exclude.append(r)
  614. elif r == sym:
  615. exclude.append(l)
  616. else:
  617. return nonsymfail(cond2)
  618. continue
  619. elif cond2.lts == sym:
  620. upper = Min(cond2.gts, upper)
  621. elif cond2.gts == sym:
  622. lower = Max(cond2.lts, lower)
  623. else:
  624. return nonsymfail(cond2) # should never get here
  625. if exclude:
  626. exclude = list(ordered(exclude))
  627. newcond = []
  628. for i, e in enumerate(exclude):
  629. if e < lower == True or e > upper == True:
  630. continue
  631. if not newcond:
  632. newcond.append((None, lower)) # add a primer
  633. newcond.append((newcond[-1][1], e))
  634. newcond.append((newcond[-1][1], upper))
  635. newcond.pop(0) # remove the primer
  636. expr_cond.extend([(iarg, expr, And(i[0] < sym, sym < i[1])) for i in newcond])
  637. continue
  638. elif isinstance(cond, Relational) and cond.rel_op != '!=':
  639. lower, upper = cond.lts, cond.gts # part 1: initialize with givens
  640. if cond.lts == sym: # part 1a: expand the side ...
  641. lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0
  642. elif cond.gts == sym: # part 1a: ... that can be expanded
  643. upper = S.Infinity # e.g. x >= 0 ---> oo >= 0
  644. else:
  645. return nonsymfail(cond)
  646. else:
  647. return False, 'unrecognized condition: %s' % cond
  648. lower, upper = lower, Max(lower, upper)
  649. if err_on_Eq and lower == upper:
  650. return False, 'encountered Eq condition'
  651. if (lower >= upper) is not S.true:
  652. int_expr.append((lower, upper, expr, iarg))
  653. if default is not None:
  654. int_expr.append(
  655. (S.NegativeInfinity, S.Infinity, default, idefault))
  656. return True, list(uniq(int_expr))
  657. def _eval_nseries(self, x, n, logx, cdir=0):
  658. args = [(ec.expr._eval_nseries(x, n, logx), ec.cond) for ec in self.args]
  659. return self.func(*args)
  660. def _eval_power(self, s):
  661. return self.func(*[(e**s, c) for e, c in self.args])
  662. def _eval_subs(self, old, new):
  663. # this is strictly not necessary, but we can keep track
  664. # of whether True or False conditions arise and be
  665. # somewhat more efficient by avoiding other substitutions
  666. # and avoiding invalid conditions that appear after a
  667. # True condition
  668. args = list(self.args)
  669. args_exist = False
  670. for i, (e, c) in enumerate(args):
  671. c = c._subs(old, new)
  672. if c != False:
  673. args_exist = True
  674. e = e._subs(old, new)
  675. args[i] = (e, c)
  676. if c == True:
  677. break
  678. if not args_exist:
  679. args = ((Undefined, True),)
  680. return self.func(*args)
  681. def _eval_transpose(self):
  682. return self.func(*[(e.transpose(), c) for e, c in self.args])
  683. def _eval_template_is_attr(self, is_attr):
  684. b = None
  685. for expr, _ in self.args:
  686. a = getattr(expr, is_attr)
  687. if a is None:
  688. return
  689. if b is None:
  690. b = a
  691. elif b is not a:
  692. return
  693. return b
  694. _eval_is_finite = lambda self: self._eval_template_is_attr(
  695. 'is_finite')
  696. _eval_is_complex = lambda self: self._eval_template_is_attr('is_complex')
  697. _eval_is_even = lambda self: self._eval_template_is_attr('is_even')
  698. _eval_is_imaginary = lambda self: self._eval_template_is_attr(
  699. 'is_imaginary')
  700. _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer')
  701. _eval_is_irrational = lambda self: self._eval_template_is_attr(
  702. 'is_irrational')
  703. _eval_is_negative = lambda self: self._eval_template_is_attr('is_negative')
  704. _eval_is_nonnegative = lambda self: self._eval_template_is_attr(
  705. 'is_nonnegative')
  706. _eval_is_nonpositive = lambda self: self._eval_template_is_attr(
  707. 'is_nonpositive')
  708. _eval_is_nonzero = lambda self: self._eval_template_is_attr(
  709. 'is_nonzero')
  710. _eval_is_odd = lambda self: self._eval_template_is_attr('is_odd')
  711. _eval_is_polar = lambda self: self._eval_template_is_attr('is_polar')
  712. _eval_is_positive = lambda self: self._eval_template_is_attr('is_positive')
  713. _eval_is_extended_real = lambda self: self._eval_template_is_attr(
  714. 'is_extended_real')
  715. _eval_is_extended_positive = lambda self: self._eval_template_is_attr(
  716. 'is_extended_positive')
  717. _eval_is_extended_negative = lambda self: self._eval_template_is_attr(
  718. 'is_extended_negative')
  719. _eval_is_extended_nonzero = lambda self: self._eval_template_is_attr(
  720. 'is_extended_nonzero')
  721. _eval_is_extended_nonpositive = lambda self: self._eval_template_is_attr(
  722. 'is_extended_nonpositive')
  723. _eval_is_extended_nonnegative = lambda self: self._eval_template_is_attr(
  724. 'is_extended_nonnegative')
  725. _eval_is_real = lambda self: self._eval_template_is_attr('is_real')
  726. _eval_is_zero = lambda self: self._eval_template_is_attr(
  727. 'is_zero')
  728. @classmethod
  729. def __eval_cond(cls, cond):
  730. """Return the truth value of the condition."""
  731. if cond == True:
  732. return True
  733. if isinstance(cond, Eq):
  734. try:
  735. diff = cond.lhs - cond.rhs
  736. if diff.is_commutative:
  737. return diff.is_zero
  738. except TypeError:
  739. pass
  740. def as_expr_set_pairs(self, domain=None):
  741. """Return tuples for each argument of self that give
  742. the expression and the interval in which it is valid
  743. which is contained within the given domain.
  744. If a condition cannot be converted to a set, an error
  745. will be raised. The variable of the conditions is
  746. assumed to be real; sets of real values are returned.
  747. Examples
  748. ========
  749. >>> from sympy import Piecewise, Interval
  750. >>> from sympy.abc import x
  751. >>> p = Piecewise(
  752. ... (1, x < 2),
  753. ... (2,(x > 0) & (x < 4)),
  754. ... (3, True))
  755. >>> p.as_expr_set_pairs()
  756. [(1, Interval.open(-oo, 2)),
  757. (2, Interval.Ropen(2, 4)),
  758. (3, Interval(4, oo))]
  759. >>> p.as_expr_set_pairs(Interval(0, 3))
  760. [(1, Interval.Ropen(0, 2)),
  761. (2, Interval(2, 3))]
  762. """
  763. if domain is None:
  764. domain = S.Reals
  765. exp_sets = []
  766. U = domain
  767. complex = not domain.is_subset(S.Reals)
  768. cond_free = set()
  769. for expr, cond in self.args:
  770. cond_free |= cond.free_symbols
  771. if len(cond_free) > 1:
  772. raise NotImplementedError(filldedent('''
  773. multivariate conditions are not handled.'''))
  774. if complex:
  775. for i in cond.atoms(Relational):
  776. if not isinstance(i, (Eq, Ne)):
  777. raise ValueError(filldedent('''
  778. Inequalities in the complex domain are
  779. not supported. Try the real domain by
  780. setting domain=S.Reals'''))
  781. cond_int = U.intersect(cond.as_set())
  782. U = U - cond_int
  783. if cond_int != S.EmptySet:
  784. exp_sets.append((expr, cond_int))
  785. return exp_sets
  786. def _eval_rewrite_as_ITE(self, *args, **kwargs):
  787. byfree = {}
  788. args = list(args)
  789. default = any(c == True for b, c in args)
  790. for i, (b, c) in enumerate(args):
  791. if not isinstance(b, Boolean) and b != True:
  792. raise TypeError(filldedent('''
  793. Expecting Boolean or bool but got `%s`
  794. ''' % func_name(b)))
  795. if c == True:
  796. break
  797. # loop over independent conditions for this b
  798. for c in c.args if isinstance(c, Or) else [c]:
  799. free = c.free_symbols
  800. x = free.pop()
  801. try:
  802. byfree[x] = byfree.setdefault(
  803. x, S.EmptySet).union(c.as_set())
  804. except NotImplementedError:
  805. if not default:
  806. raise NotImplementedError(filldedent('''
  807. A method to determine whether a multivariate
  808. conditional is consistent with a complete coverage
  809. of all variables has not been implemented so the
  810. rewrite is being stopped after encountering `%s`.
  811. This error would not occur if a default expression
  812. like `(foo, True)` were given.
  813. ''' % c))
  814. if byfree[x] in (S.UniversalSet, S.Reals):
  815. # collapse the ith condition to True and break
  816. args[i] = list(args[i])
  817. c = args[i][1] = True
  818. break
  819. if c == True:
  820. break
  821. if c != True:
  822. raise ValueError(filldedent('''
  823. Conditions must cover all reals or a final default
  824. condition `(foo, True)` must be given.
  825. '''))
  826. last, _ = args[i] # ignore all past ith arg
  827. for a, c in reversed(args[:i]):
  828. last = ITE(c, a, last)
  829. return _canonical(last)
  830. def _eval_rewrite_as_KroneckerDelta(self, *args):
  831. from sympy.functions.special.tensor_functions import KroneckerDelta
  832. rules = {
  833. And: [False, False],
  834. Or: [True, True],
  835. Not: [True, False],
  836. Eq: [None, None],
  837. Ne: [None, None]
  838. }
  839. class UnrecognizedCondition(Exception):
  840. pass
  841. def rewrite(cond):
  842. if isinstance(cond, Eq):
  843. return KroneckerDelta(*cond.args)
  844. if isinstance(cond, Ne):
  845. return 1 - KroneckerDelta(*cond.args)
  846. cls, args = type(cond), cond.args
  847. if cls not in rules:
  848. raise UnrecognizedCondition(cls)
  849. b1, b2 = rules[cls]
  850. k = Mul(*[1 - rewrite(c) for c in args]) if b1 else Mul(*[rewrite(c) for c in args])
  851. if b2:
  852. return 1 - k
  853. return k
  854. conditions = []
  855. true_value = None
  856. for value, cond in args:
  857. if type(cond) in rules:
  858. conditions.append((value, cond))
  859. elif cond is S.true:
  860. if true_value is None:
  861. true_value = value
  862. else:
  863. return
  864. if true_value is not None:
  865. result = true_value
  866. for value, cond in conditions[::-1]:
  867. try:
  868. k = rewrite(cond)
  869. result = k * value + (1 - k) * result
  870. except UnrecognizedCondition:
  871. return
  872. return result
  873. def piecewise_fold(expr, evaluate=True):
  874. """
  875. Takes an expression containing a piecewise function and returns the
  876. expression in piecewise form. In addition, any ITE conditions are
  877. rewritten in negation normal form and simplified.
  878. The final Piecewise is evaluated (default) but if the raw form
  879. is desired, send ``evaluate=False``; if trivial evaluation is
  880. desired, send ``evaluate=None`` and duplicate conditions and
  881. processing of True and False will be handled.
  882. Examples
  883. ========
  884. >>> from sympy import Piecewise, piecewise_fold, S
  885. >>> from sympy.abc import x
  886. >>> p = Piecewise((x, x < 1), (1, S(1) <= x))
  887. >>> piecewise_fold(x*p)
  888. Piecewise((x**2, x < 1), (x, True))
  889. See Also
  890. ========
  891. Piecewise
  892. piecewise_exclusive
  893. """
  894. if not isinstance(expr, Basic) or not expr.has(Piecewise):
  895. return expr
  896. new_args = []
  897. if isinstance(expr, (ExprCondPair, Piecewise)):
  898. for e, c in expr.args:
  899. if not isinstance(e, Piecewise):
  900. e = piecewise_fold(e)
  901. # we don't keep Piecewise in condition because
  902. # it has to be checked to see that it's complete
  903. # and we convert it to ITE at that time
  904. assert not c.has(Piecewise) # pragma: no cover
  905. if isinstance(c, ITE):
  906. c = c.to_nnf()
  907. c = simplify_logic(c, form='cnf')
  908. if isinstance(e, Piecewise):
  909. new_args.extend([(piecewise_fold(ei), And(ci, c))
  910. for ei, ci in e.args])
  911. else:
  912. new_args.append((e, c))
  913. else:
  914. # Given
  915. # P1 = Piecewise((e11, c1), (e12, c2), A)
  916. # P2 = Piecewise((e21, c1), (e22, c2), B)
  917. # ...
  918. # the folding of f(P1, P2) is trivially
  919. # Piecewise(
  920. # (f(e11, e21), c1),
  921. # (f(e12, e22), c2),
  922. # (f(Piecewise(A), Piecewise(B)), True))
  923. # Certain objects end up rewriting themselves as thus, so
  924. # we do that grouping before the more generic folding.
  925. # The following applies this idea when f = Add or f = Mul
  926. # (and the expression is commutative).
  927. if expr.is_Add or expr.is_Mul and expr.is_commutative:
  928. p, args = sift(expr.args, lambda x: x.is_Piecewise, binary=True)
  929. pc = sift(p, lambda x: tuple([c for e,c in x.args]))
  930. for c in list(ordered(pc)):
  931. if len(pc[c]) > 1:
  932. pargs = [list(i.args) for i in pc[c]]
  933. # the first one is the same; there may be more
  934. com = common_prefix(*[
  935. [i.cond for i in j] for j in pargs])
  936. n = len(com)
  937. collected = []
  938. for i in range(n):
  939. collected.append((
  940. expr.func(*[ai[i].expr for ai in pargs]),
  941. com[i]))
  942. remains = []
  943. for a in pargs:
  944. if n == len(a): # no more args
  945. continue
  946. if a[n].cond == True: # no longer Piecewise
  947. remains.append(a[n].expr)
  948. else: # restore the remaining Piecewise
  949. remains.append(
  950. Piecewise(*a[n:], evaluate=False))
  951. if remains:
  952. collected.append((expr.func(*remains), True))
  953. args.append(Piecewise(*collected, evaluate=False))
  954. continue
  955. args.extend(pc[c])
  956. else:
  957. args = expr.args
  958. # fold
  959. folded = list(map(piecewise_fold, args))
  960. for ec in product(*[
  961. (i.args if isinstance(i, Piecewise) else
  962. [(i, true)]) for i in folded]):
  963. e, c = zip(*ec)
  964. new_args.append((expr.func(*e), And(*c)))
  965. if evaluate is None:
  966. # don't return duplicate conditions, otherwise don't evaluate
  967. new_args = list(reversed([(e, c) for c, e in {
  968. c: e for e, c in reversed(new_args)}.items()]))
  969. rv = Piecewise(*new_args, evaluate=evaluate)
  970. if evaluate is None and len(rv.args) == 1 and rv.args[0].cond == True:
  971. return rv.args[0].expr
  972. if any(s.expr.has(Piecewise) for p in rv.atoms(Piecewise) for s in p.args):
  973. return piecewise_fold(rv)
  974. return rv
  975. def _clip(A, B, k):
  976. """Return interval B as intervals that are covered by A (keyed
  977. to k) and all other intervals of B not covered by A keyed to -1.
  978. The reference point of each interval is the rhs; if the lhs is
  979. greater than the rhs then an interval of zero width interval will
  980. result, e.g. (4, 1) is treated like (1, 1).
  981. Examples
  982. ========
  983. >>> from sympy.functions.elementary.piecewise import _clip
  984. >>> from sympy import Tuple
  985. >>> A = Tuple(1, 3)
  986. >>> B = Tuple(2, 4)
  987. >>> _clip(A, B, 0)
  988. [(2, 3, 0), (3, 4, -1)]
  989. Interpretation: interval portion (2, 3) of interval (2, 4) is
  990. covered by interval (1, 3) and is keyed to 0 as requested;
  991. interval (3, 4) was not covered by (1, 3) and is keyed to -1.
  992. """
  993. a, b = B
  994. c, d = A
  995. c, d = Min(Max(c, a), b), Min(Max(d, a), b)
  996. a, b = Min(a, b), b
  997. p = []
  998. if a != c:
  999. p.append((a, c, -1))
  1000. else:
  1001. pass
  1002. if c != d:
  1003. p.append((c, d, k))
  1004. else:
  1005. pass
  1006. if b != d:
  1007. if d == c and p and p[-1][-1] == -1:
  1008. p[-1] = p[-1][0], b, -1
  1009. else:
  1010. p.append((d, b, -1))
  1011. else:
  1012. pass
  1013. return p
  1014. def piecewise_simplify_arguments(expr, **kwargs):
  1015. from sympy.simplify.simplify import simplify
  1016. # simplify conditions
  1017. f1 = expr.args[0].cond.free_symbols
  1018. args = None
  1019. if len(f1) == 1 and not expr.atoms(Eq):
  1020. x = f1.pop()
  1021. # this won't return intervals involving Eq
  1022. # and it won't handle symbols treated as
  1023. # booleans
  1024. ok, abe_ = expr._intervals(x, err_on_Eq=True)
  1025. def include(c, x, a):
  1026. "return True if c.subs(x, a) is True, else False"
  1027. try:
  1028. return c.subs(x, a) == True
  1029. except TypeError:
  1030. return False
  1031. if ok:
  1032. args = []
  1033. covered = S.EmptySet
  1034. from sympy.sets.sets import Interval
  1035. for a, b, e, i in abe_:
  1036. c = expr.args[i].cond
  1037. incl_a = include(c, x, a)
  1038. incl_b = include(c, x, b)
  1039. iv = Interval(a, b, not incl_a, not incl_b)
  1040. cset = iv - covered
  1041. if not cset:
  1042. continue
  1043. if incl_a and incl_b:
  1044. if a.is_infinite and b.is_infinite:
  1045. c = S.true
  1046. elif b.is_infinite:
  1047. c = (x >= a)
  1048. elif a in covered or a.is_infinite:
  1049. c = (x <= b)
  1050. else:
  1051. c = And(a <= x, x <= b)
  1052. elif incl_a:
  1053. if a in covered or a.is_infinite:
  1054. c = (x < b)
  1055. else:
  1056. c = And(a <= x, x < b)
  1057. elif incl_b:
  1058. if b.is_infinite:
  1059. c = (x > a)
  1060. else:
  1061. c = (x <= b)
  1062. else:
  1063. if a in covered:
  1064. c = (x < b)
  1065. else:
  1066. c = And(a < x, x < b)
  1067. covered |= iv
  1068. if a is S.NegativeInfinity and incl_a:
  1069. covered |= {S.NegativeInfinity}
  1070. if b is S.Infinity and incl_b:
  1071. covered |= {S.Infinity}
  1072. args.append((e, c))
  1073. if not S.Reals.is_subset(covered):
  1074. args.append((Undefined, True))
  1075. if args is None:
  1076. args = list(expr.args)
  1077. for i in range(len(args)):
  1078. e, c = args[i]
  1079. if isinstance(c, Basic):
  1080. c = simplify(c, **kwargs)
  1081. args[i] = (e, c)
  1082. # simplify expressions
  1083. doit = kwargs.pop('doit', None)
  1084. for i in range(len(args)):
  1085. e, c = args[i]
  1086. if isinstance(e, Basic):
  1087. # Skip doit to avoid growth at every call for some integrals
  1088. # and sums, see sympy/sympy#17165
  1089. newe = simplify(e, doit=False, **kwargs)
  1090. if newe != e:
  1091. e = newe
  1092. args[i] = (e, c)
  1093. # restore kwargs flag
  1094. if doit is not None:
  1095. kwargs['doit'] = doit
  1096. return Piecewise(*args)
  1097. def _piecewise_collapse_arguments(_args):
  1098. newargs = [] # the unevaluated conditions
  1099. current_cond = set() # the conditions up to a given e, c pair
  1100. for expr, cond in _args:
  1101. cond = cond.replace(
  1102. lambda _: _.is_Relational, _canonical_coeff)
  1103. # Check here if expr is a Piecewise and collapse if one of
  1104. # the conds in expr matches cond. This allows the collapsing
  1105. # of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)).
  1106. # This is important when using piecewise_fold to simplify
  1107. # multiple Piecewise instances having the same conds.
  1108. # Eventually, this code should be able to collapse Piecewise's
  1109. # having different intervals, but this will probably require
  1110. # using the new assumptions.
  1111. if isinstance(expr, Piecewise):
  1112. unmatching = []
  1113. for i, (e, c) in enumerate(expr.args):
  1114. if c in current_cond:
  1115. # this would already have triggered
  1116. continue
  1117. if c == cond:
  1118. if c != True:
  1119. # nothing past this condition will ever
  1120. # trigger and only those args before this
  1121. # that didn't match a previous condition
  1122. # could possibly trigger
  1123. if unmatching:
  1124. expr = Piecewise(*(
  1125. unmatching + [(e, c)]))
  1126. else:
  1127. expr = e
  1128. break
  1129. else:
  1130. unmatching.append((e, c))
  1131. # check for condition repeats
  1132. got = False
  1133. # -- if an And contains a condition that was
  1134. # already encountered, then the And will be
  1135. # False: if the previous condition was False
  1136. # then the And will be False and if the previous
  1137. # condition is True then then we wouldn't get to
  1138. # this point. In either case, we can skip this condition.
  1139. for i in ([cond] +
  1140. (list(cond.args) if isinstance(cond, And) else
  1141. [])):
  1142. if i in current_cond:
  1143. got = True
  1144. break
  1145. if got:
  1146. continue
  1147. # -- if not(c) is already in current_cond then c is
  1148. # a redundant condition in an And. This does not
  1149. # apply to Or, however: (e1, c), (e2, Or(~c, d))
  1150. # is not (e1, c), (e2, d) because if c and d are
  1151. # both False this would give no results when the
  1152. # true answer should be (e2, True)
  1153. if isinstance(cond, And):
  1154. nonredundant = []
  1155. for c in cond.args:
  1156. if isinstance(c, Relational):
  1157. if c.negated.canonical in current_cond:
  1158. continue
  1159. # if a strict inequality appears after
  1160. # a non-strict one, then the condition is
  1161. # redundant
  1162. if isinstance(c, (Lt, Gt)) and (
  1163. c.weak in current_cond):
  1164. cond = False
  1165. break
  1166. nonredundant.append(c)
  1167. else:
  1168. cond = cond.func(*nonredundant)
  1169. elif isinstance(cond, Relational):
  1170. if cond.negated.canonical in current_cond:
  1171. cond = S.true
  1172. current_cond.add(cond)
  1173. # collect successive e,c pairs when exprs or cond match
  1174. if newargs:
  1175. if newargs[-1].expr == expr:
  1176. orcond = Or(cond, newargs[-1].cond)
  1177. if isinstance(orcond, (And, Or)):
  1178. orcond = distribute_and_over_or(orcond)
  1179. newargs[-1] = ExprCondPair(expr, orcond)
  1180. continue
  1181. elif newargs[-1].cond == cond:
  1182. continue
  1183. newargs.append(ExprCondPair(expr, cond))
  1184. return newargs
  1185. _blessed = lambda e: getattr(e.lhs, '_diff_wrt', False) and (
  1186. getattr(e.rhs, '_diff_wrt', None) or
  1187. isinstance(e.rhs, (Rational, NumberSymbol)))
  1188. def piecewise_simplify(expr, **kwargs):
  1189. expr = piecewise_simplify_arguments(expr, **kwargs)
  1190. if not isinstance(expr, Piecewise):
  1191. return expr
  1192. args = list(expr.args)
  1193. args = _piecewise_simplify_eq_and(args)
  1194. args = _piecewise_simplify_equal_to_next_segment(args)
  1195. return Piecewise(*args)
  1196. def _piecewise_simplify_equal_to_next_segment(args):
  1197. """
  1198. See if expressions valid for an Equal expression happens to evaluate
  1199. to the same function as in the next piecewise segment, see:
  1200. https://github.com/sympy/sympy/issues/8458
  1201. """
  1202. prevexpr = None
  1203. for i, (expr, cond) in reversed(list(enumerate(args))):
  1204. if prevexpr is not None:
  1205. if isinstance(cond, And):
  1206. eqs, other = sift(cond.args,
  1207. lambda i: isinstance(i, Eq), binary=True)
  1208. elif isinstance(cond, Eq):
  1209. eqs, other = [cond], []
  1210. else:
  1211. eqs = other = []
  1212. _prevexpr = prevexpr
  1213. _expr = expr
  1214. if eqs and not other:
  1215. eqs = list(ordered(eqs))
  1216. for e in eqs:
  1217. # allow 2 args to collapse into 1 for any e
  1218. # otherwise limit simplification to only simple-arg
  1219. # Eq instances
  1220. if len(args) == 2 or _blessed(e):
  1221. _prevexpr = _prevexpr.subs(*e.args)
  1222. _expr = _expr.subs(*e.args)
  1223. # Did it evaluate to the same?
  1224. if _prevexpr == _expr:
  1225. # Set the expression for the Not equal section to the same
  1226. # as the next. These will be merged when creating the new
  1227. # Piecewise
  1228. args[i] = args[i].func(args[i + 1][0], cond)
  1229. else:
  1230. # Update the expression that we compare against
  1231. prevexpr = expr
  1232. else:
  1233. prevexpr = expr
  1234. return args
  1235. def _piecewise_simplify_eq_and(args):
  1236. """
  1237. Try to simplify conditions and the expression for
  1238. equalities that are part of the condition, e.g.
  1239. Piecewise((n, And(Eq(n,0), Eq(n + m, 0))), (1, True))
  1240. -> Piecewise((0, And(Eq(n, 0), Eq(m, 0))), (1, True))
  1241. """
  1242. for i, (expr, cond) in enumerate(args):
  1243. if isinstance(cond, And):
  1244. eqs, other = sift(cond.args,
  1245. lambda i: isinstance(i, Eq), binary=True)
  1246. elif isinstance(cond, Eq):
  1247. eqs, other = [cond], []
  1248. else:
  1249. eqs = other = []
  1250. if eqs:
  1251. eqs = list(ordered(eqs))
  1252. for j, e in enumerate(eqs):
  1253. # these blessed lhs objects behave like Symbols
  1254. # and the rhs are simple replacements for the "symbols"
  1255. if _blessed(e):
  1256. expr = expr.subs(*e.args)
  1257. eqs[j + 1:] = [ei.subs(*e.args) for ei in eqs[j + 1:]]
  1258. other = [ei.subs(*e.args) for ei in other]
  1259. cond = And(*(eqs + other))
  1260. args[i] = args[i].func(expr, cond)
  1261. return args
  1262. def piecewise_exclusive(expr, *, skip_nan=False, deep=True):
  1263. """
  1264. Rewrite :class:`Piecewise` with mutually exclusive conditions.
  1265. Explanation
  1266. ===========
  1267. SymPy represents the conditions of a :class:`Piecewise` in an
  1268. "if-elif"-fashion, allowing more than one condition to be simultaneously
  1269. True. The interpretation is that the first condition that is True is the
  1270. case that holds. While this is a useful representation computationally it
  1271. is not how a piecewise formula is typically shown in a mathematical text.
  1272. The :func:`piecewise_exclusive` function can be used to rewrite any
  1273. :class:`Piecewise` with more typical mutually exclusive conditions.
  1274. Note that further manipulation of the resulting :class:`Piecewise`, e.g.
  1275. simplifying it, will most likely make it non-exclusive. Hence, this is
  1276. primarily a function to be used in conjunction with printing the Piecewise
  1277. or if one would like to reorder the expression-condition pairs.
  1278. If it is not possible to determine that all possibilities are covered by
  1279. the different cases of the :class:`Piecewise` then a final
  1280. :class:`~sympy.core.numbers.NaN` case will be included explicitly. This
  1281. can be prevented by passing ``skip_nan=True``.
  1282. Examples
  1283. ========
  1284. >>> from sympy import piecewise_exclusive, Symbol, Piecewise, S
  1285. >>> x = Symbol('x', real=True)
  1286. >>> p = Piecewise((0, x < 0), (S.Half, x <= 0), (1, True))
  1287. >>> piecewise_exclusive(p)
  1288. Piecewise((0, x < 0), (1/2, Eq(x, 0)), (1, x > 0))
  1289. >>> piecewise_exclusive(Piecewise((2, x > 1)))
  1290. Piecewise((2, x > 1), (nan, x <= 1))
  1291. >>> piecewise_exclusive(Piecewise((2, x > 1)), skip_nan=True)
  1292. Piecewise((2, x > 1))
  1293. Parameters
  1294. ==========
  1295. expr: a SymPy expression.
  1296. Any :class:`Piecewise` in the expression will be rewritten.
  1297. skip_nan: ``bool`` (default ``False``)
  1298. If ``skip_nan`` is set to ``True`` then a final
  1299. :class:`~sympy.core.numbers.NaN` case will not be included.
  1300. deep: ``bool`` (default ``True``)
  1301. If ``deep`` is ``True`` then :func:`piecewise_exclusive` will rewrite
  1302. any :class:`Piecewise` subexpressions in ``expr`` rather than just
  1303. rewriting ``expr`` itself.
  1304. Returns
  1305. =======
  1306. An expression equivalent to ``expr`` but where all :class:`Piecewise` have
  1307. been rewritten with mutually exclusive conditions.
  1308. See Also
  1309. ========
  1310. Piecewise
  1311. piecewise_fold
  1312. """
  1313. def make_exclusive(*pwargs):
  1314. cumcond = false
  1315. newargs = []
  1316. # Handle the first n-1 cases
  1317. for expr_i, cond_i in pwargs[:-1]:
  1318. cancond = And(cond_i, Not(cumcond)).simplify()
  1319. cumcond = Or(cond_i, cumcond).simplify()
  1320. newargs.append((expr_i, cancond))
  1321. # For the nth case defer simplification of cumcond
  1322. expr_n, cond_n = pwargs[-1]
  1323. cancond_n = And(cond_n, Not(cumcond)).simplify()
  1324. newargs.append((expr_n, cancond_n))
  1325. if not skip_nan:
  1326. cumcond = Or(cond_n, cumcond).simplify()
  1327. if cumcond is not true:
  1328. newargs.append((Undefined, Not(cumcond).simplify()))
  1329. return Piecewise(*newargs, evaluate=False)
  1330. if deep:
  1331. return expr.replace(Piecewise, make_exclusive)
  1332. elif isinstance(expr, Piecewise):
  1333. return make_exclusive(*expr.args)
  1334. else:
  1335. return expr