## page was renamed from PiecewiseSymbolicSEP = Symbolic Piecewise Functions = This Sage Enhancement Proposal (SEP) is to improve the existing piecewise functionality. == Authors == * Michael Orlitzky == Abstract == Sage has the ability to manipulate many familiar symbolic functions and expressions. This ability is rather robust; the code is well-tested and integrates nicely with the rest of the project. Piecewise functions are however an exception. In essence, piecewise functions can be thought dictionaries that map values to symbolic expressions. Unfortunately, due to historical reasons and lack of manpower, the interface to piecewise functions has not been kept at parity with the rest of the symbolics library. This causes users difficulty when they expect piecewise functions to behave like other expressions: the piecewise interface is not just different, it has fewer features. == Copyright == Public domain. == Problems with PiecewisePolynomial == There are several problems with the existing piecewise class, `PiecewisePolynomial`. Open tickets are listed at the end of this SEP. Here are some characteristic examples. {{{ sage: f = piecewise([[(-1,1), 0]]) sage: f(1) ... TypeError: 'sage.rings.integer.Integer' object is not callable }}} {{{ sage: f = piecewise([[(-1,1), 0]]) sage: x*f ... AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'domain' }}} {{{ f = piecewise([[(-1,1), 0]]) sage: abs(f) ... AttributeError: PiecewisePolynomial instance has no attribute '__abs__' }}} {{{ sage: x,y = var('x,y') sage: f = piecewise([[(-1,1), x*y]]) sage: f(1) /home/mjo/src/sage-5.0.beta3/local/bin/sage-ipython:1: DeprecationWarning: Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...) #!/usr/bin/env python y sage: f(x=0) ... TypeError: __call__() got an unexpected keyword argument 'x' }}} == Implementation == === Evaluation of Arguments === As it stands, piecewise functions have the familiar classroom behavior (deprecation warning omitted): {{{ sage: f = piecewise([[(-infinity,0), -x], [(0, infinity), x]]) sage: f(3) 3 }}} The fact that calling `f` evaluates one if its consituent functions leads to some difficulties with the rest of symbolics. It is proposed that we eliminate this behavior by default but provide an interface to it; a piecewise function `f` should act primarily as dictionary rather than as a callable function. Ideally, a piecewise function `f` is a set of `(A, g)` pairs where A is some object that supports a "contains" method and `g` is a function or at least behaves like one. When the piecewise function is evaluated at a point `x`, it should return the function `g` such that `(A, g)` is in the list and `A.contains(x)`. No deprecation warning will be thrown, as we expect (only) one positional argument. Unfortunately, this change would cause a regression: it seems silly, using the earlier example, to have to do `f(3)(3)`, to evaluate the appropriate function at `x=3`. This is not impossible to fix, however. One possible solution is that, when `f` is called with a named argument, a substitution is performed on all of its constituent functions. For example, {{{ sage: f = piecewise([[(-Infinity,0), -x], [(0, +Infinity), x]]) sage: f(x=3) Piecewise defined function with 2 parts, [[(-Infinity, 0), -3], [(0, +Infinity), 3]] }}} If `A.contains(g)` for any `(A, g)` ''after'' substitution, we can return `g`. === Piece Determination === The previous discussion uses "containment" to determine which piece is returned, but this could be generalized to any predicate. We could still support containment as the predicate, of course. Pseudocode for `f(x)`: {{{ for (A, g) in f.pieces(): if isinstance(A, interval): if x in A: return g else: # Assume it's a predicate. if A(x): return g }}} This would allow us to define "easy" piecewise functions more naturally by specifying conditions on the independent variable: `x < 0`, `x == 0`, etc. Robertwb: It may be simpler to simply disallow anonymous intervals altogether, requiring f(x) = piecewise(((x < 0), -x), ((x >= 0), x)) where of course the last predicate could be optional (just giving an expression would result in an else clause). mjo: I agree it would be simpler, but it would make the transition for users harder. If `a