r"""
Piecewise-defined Functions
Sage implements a very simple class of piecewise-defined functions.
Functions may be any type of symbolic expression. Infinite
intervals are not supported. The endpoints of each interval must
line up.
TODO:
- Implement max/min location and values,
- Need: parent object - ring of piecewise functions
- This class should derive from an element-type class, and should
define ``_add_``, ``_mul_``, etc. That will automatically take care
of left multiplication and proper coercion. The coercion mentioned
below for scalar mult on right is bad, since it only allows ints and
rationals. The right way is to use an element class and only define
``_mul_``, and have a parent, so anything gets coerced properly.
AUTHORS:
- David Joyner (2006-04): initial version
- David Joyner (2006-09): added __eq__, extend_by_zero_to, unextend,
convolution, trapezoid, trapezoid_integral_approximation,
riemann_sum, riemann_sum_integral_approximation, tangent_line fixed
bugs in __mul__, __add__
- David Joyner (2007-03): adding Hann filter for FS, added general FS
filter methods for computing and plotting, added options to plotting
of FS (eg, specifying rgb values are now allowed). Fixed bug in
documentation reported by Pablo De Napoli.
- David Joyner (2007-09): bug fixes due to behaviour of
SymbolicArithmetic
- David Joyner (2008-04): fixed docstring bugs reported by J Morrow; added
support for Laplace transform of functions with infinite support.
- David Joyner (2008-07): fixed a left multiplication bug reported by
C. Boncelet (by defining __rmul__ = __mul__).
- Paul Butler (2009-01): added indefinite integration and default_variable
TESTS::
sage: R.<x> = QQ[]
sage: f = Piecewise([[(0,1),1*x^0]])
sage: 2*f
Piecewise defined function with 1 parts, [[(0, 1), 2]]
"""
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.misc.sage_eval import sage_eval
from sage.rings.all import QQ, RR, Integer, Rational, infinity
from sage.calculus.functional import derivative
from sage.symbolic.expression import is_Expression
from sage.symbolic.assumptions import assume, forget
from sage.calculus.calculus import SR, maxima
from sage.calculus.all import var
def piecewise(list_of_pairs, var=None):
"""
Returns a piecewise function from a list of (interval, function)
pairs.
``list_of_pairs`` is a list of pairs (I, fcn), where
fcn is a Sage function (such as a polynomial over RR, or functions
using the lambda notation), and I is an interval such as I = (1,3).
Two consecutive intervals must share a common endpoint.
If the optional ``var`` is specified, then any symbolic expressions
in the list will be converted to symbolic functions using
``fcn.function(var)``. (This says which variable is considered to
be "piecewise".)
We assume that these definitions are consistent (ie, no checking is
done).
EXAMPLES::
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f(1)
-1
sage: f(3)
2
sage: f = Piecewise([[(0,1),x], [(1,2),x^2]], x); f
Piecewise defined function with 2 parts, [[(0, 1), x |--> x], [(1, 2), x |--> x^2]]
sage: f(0.9)
0.900000000000000
sage: f(1.1)
1.21000000000000
"""
return PiecewisePolynomial(list_of_pairs, var=var)
Piecewise = piecewise
class PiecewisePolynomial:
"""
Returns a piecewise function from a list of (interval, function)
pairs.
EXAMPLES::
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f(1)
-1
sage: f(3)
2
"""
def __init__(self, list_of_pairs, var=None):
r"""
``list_of_pairs`` is a list of pairs (I, fcn), where
fcn is a Sage function (such as a polynomial over RR, or functions
using the lambda notation), and I is an interval such as I = (1,3).
Two consecutive intervals must share a common endpoint.
If the optional ``var`` is specified, then any symbolic
expressions in the list will be converted to symbolic
functions using ``fcn.function(var)``. (This says which
variable is considered to be "piecewise".)
We assume that these definitions are consistent (ie, no checking is
done).
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.list()
[[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]]
sage: f.length()
2
"""
self._length = len(list_of_pairs)
self._intervals = [x[0] for x in list_of_pairs]
functions = [x[1] for x in list_of_pairs]
if var is not None:
for i in range(len(functions)):
if is_Expression(functions[i]):
functions[i] = functions[i].function(var)
self._functions = functions
self._list = [[self._intervals[i], self._functions[i]] for i in range(self._length)]
def list(self):
"""
Returns the pieces of this function as a list of functions.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.list()
[[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]]
"""
return self._list
def length(self):
"""
Returns the number of pieces of this function.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.length()
2
"""
return self._length
def __repr__(self):
"""
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]); f
Piecewise defined function with 2 parts, [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]]
"""
return 'Piecewise defined function with %s parts, %s'%(
self.length(),self.list())
def _latex_(self):
r"""
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: latex(f)
\begin{cases}
x \ {\mapsto}\ 1 &\text{on $(0, 1)$}\cr
x \ {\mapsto}\ -x + 1 &\text{on $(1, 2)$}\cr
\end{cases}
::
sage: f(x) = sin(x*pi/2)
sage: g(x) = 1-(x-1)^2
sage: h(x) = -x
sage: P = Piecewise([[(0,1), f], [(1,3),g], [(3,5), h]])
sage: latex(P)
\begin{cases}
x \ {\mapsto}\ \sin\left(\frac{1}{2} \, \pi x\right) &\text{on $(0, 1)$}\cr
x \ {\mapsto}\ -{\left(x - 1\right)}^{2} + 1 &\text{on $(1, 3)$}\cr
x \ {\mapsto}\ -x &\text{on $(3, 5)$}\cr
\end{cases}
"""
from sage.misc.latex import latex
tex = ['\\begin{cases}\n']
for (left, right), f in self.list():
tex.append('%s &\\text{on $(%s, %s)$}\\cr\n' % (latex(f), left, right))
tex.append(r'\end{cases}')
return ''.join(tex)
def intervals(self):
"""
A piecewise non-polynomial example.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f.intervals()
[(0, 1), (1, 2), (2, 3), (3, 10)]
"""
return self._intervals
def domain(self):
"""
Returns the domain of the function.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f.domain()
(0, 10)
"""
endpoints = sum(self.intervals(), ())
return (min(endpoints), max(endpoints))
def functions(self):
"""
Returns the list of functions (the "pieces").
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f.functions()
[x |--> 1, x |--> -x + 1, x |--> e^x, x |--> sin(2*x)]
"""
return self._functions
def extend_by_zero_to(self,xmin=-1000,xmax=1000):
"""
This function simply returns the piecewise defined function which
is extended by 0 so it is defined on all of (xmin,xmax). This is
needed to add two piecewise functions in a reasonable way.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1 - x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.extend_by_zero_to(-1, 3)
Piecewise defined function with 4 parts, [[(-1, 0), 0], [(0, 1), x |--> 1], [(1, 2), x |--> -x + 1], [(2, 3), 0]]
"""
zero = QQ['x'](0)
list_of_pairs = self.list()
a, b = self.domain()
if xmin < a:
list_of_pairs = [[(xmin, a), zero]] + list_of_pairs
if xmax > b:
list_of_pairs = list_of_pairs + [[(b, xmax), zero]]
return Piecewise(list_of_pairs)
def unextend(self):
"""
This removes any parts in the front or back of the function which
is zero (the inverse to extend_by_zero_to).
EXAMPLES::
sage: R.<x> = QQ[]
sage: f = Piecewise([[(-3,-1),1+2+x],[(-1,1),1-x^2]])
sage: e = f.extend_by_zero_to(-10,10); e
Piecewise defined function with 4 parts, [[(-10, -3), 0], [(-3, -1), x + 3], [(-1, 1), -x^2 + 1], [(1, 10), 0]]
sage: d = e.unextend(); d
Piecewise defined function with 2 parts, [[(-3, -1), x + 3], [(-1, 1), -x^2 + 1]]
sage: d==f
True
"""
list_of_pairs = self.list()
funcs = self.functions()
if funcs[0] == 0:
list_of_pairs = list_of_pairs[1:]
if funcs[-1] == 0:
list_of_pairs = list_of_pairs[:-1]
return Piecewise(list_of_pairs)
def _riemann_sum_helper(self, N, func, initial=0):
"""
A helper function for computing Riemann sums.
INPUT:
- ``N`` - the number of subdivisions
- ``func`` - a function to apply to the endpoints of
each subdivision
- ``initial`` - the starting value
EXAMPLES::
sage: f1(x) = x^2 ## example 1
sage: f2(x) = 5-x^2
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f._riemann_sum_helper(6, lambda x0, x1: (x1-x0)*f(x1))
19/6
"""
a,b = self.domain()
rsum = initial
h = (b-a)/N
for i in range(N):
x0 = a+i*h
x1 = a+(i+1)*h
rsum += func(x0, x1)
return rsum
def riemann_sum_integral_approximation(self,N,mode=None):
"""
Returns the piecewise line function defined by the Riemann sums in
numerical integration based on a subdivision into N subintervals.
Set mode="midpoint" for the height of the rectangles to be
determined by the midpoint of the subinterval; set mode="right" for
the height of the rectangles to be determined by the right-hand
endpoint of the subinterval; the default is mode="left" (the height
of the rectangles to be determined by the left-hand endpoint of
the subinterval).
EXAMPLES::
sage: f1(x) = x^2 ## example 1
sage: f2(x) = 5-x^2
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.riemann_sum_integral_approximation(6)
17/6
sage: f.riemann_sum_integral_approximation(6,mode="right")
19/6
sage: f.riemann_sum_integral_approximation(6,mode="midpoint")
3
sage: f.integral(definite=True)
3
"""
if mode is None:
return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x0))
elif mode == "right":
return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x1))
elif mode == "midpoint":
return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self((x0+x1)/2))
else:
raise ValueError, "invalid mode"
def riemann_sum(self,N,mode=None):
"""
Returns the piecewise line function defined by the Riemann sums in
numerical integration based on a subdivision into N subintervals.
Set mode="midpoint" for the height of the rectangles to be
determined by the midpoint of the subinterval; set mode="right" for
the height of the rectangles to be determined by the right-hand
endpoint of the subinterval; the default is mode="left" (the height
of the rectangles to be determined by the left-hand endpoint of
the subinterval).
EXAMPLES::
sage: f1(x) = x^2
sage: f2(x) = 5-x^2
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.riemann_sum(6,mode="midpoint")
Piecewise defined function with 6 parts, [[(0, 1/3), 1/36], [(1/3, 2/3), 1/4], [(2/3, 1), 25/36], [(1, 4/3), 131/36], [(4/3, 5/3), 11/4], [(5/3, 2), 59/36]]
::
sage: f = Piecewise([[(-1,1),(1-x^2).function(x)]])
sage: rsf = f.riemann_sum(7)
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()])
sage: P + Q + L
::
sage: f = Piecewise([[(-1,1),(1/2+x-x^3)]], x) ## example 3
sage: rsf = f.riemann_sum(8)
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()])
sage: P + Q + L
"""
if mode is None:
rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x0))]],
initial=[])
elif mode == "right":
rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x1))]],
initial=[])
elif mode == "midpoint":
rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self((x0+x1)/2))]],
initial=[])
else:
raise ValueError, "invalid mode"
return Piecewise(rsum)
def trapezoid(self,N):
"""
Returns the piecewise line function defined by the trapezoid rule
for numerical integration based on a subdivision into N
subintervals.
EXAMPLES::
sage: R.<x> = QQ[]
sage: f1 = x^2
sage: f2 = 5-x^2
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.trapezoid(4)
Piecewise defined function with 4 parts, [[(0, 1/2), 1/2*x], [(1/2, 1), 9/2*x - 2], [(1, 3/2), 1/2*x + 2], [(3/2, 2), -7/2*x + 8]]
::
sage: R.<x> = QQ[]
sage: f = Piecewise([[(-1,1),1-x^2]])
sage: tf = f.trapezoid(4)
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()])
sage: P+Q+L
::
sage: R.<x> = QQ[]
sage: f = Piecewise([[(-1,1),1/2+x-x^3]]) ## example 3
sage: tf = f.trapezoid(6)
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()])
sage: P+Q+L
"""
x = QQ['x'].gen()
def f(x0, x1):
f0, f1 = self(x0), self(x1)
return [[(x0,x1),f0+(f1-f0)*(x1-x0)**(-1)*(x-x0)]]
rsum = self._riemann_sum_helper(N, f, initial=[])
return Piecewise(rsum)
def trapezoid_integral_approximation(self,N):
"""
Returns the approximation given by the trapezoid rule for numerical
integration based on a subdivision into N subintervals.
EXAMPLES::
sage: f1(x) = x^2 ## example 1
sage: f2(x) = 1-(1-x)^2
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: tf = f.trapezoid(6)
sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: ta = f.trapezoid_integral_approximation(6)
sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25))
sage: a = f.integral(definite=True)
sage: tt = text('area under curve = %s'%a, (1.5, -0.5))
sage: P + Q + t + tt
::
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) ## example 2
sage: tf = f.trapezoid(4)
sage: ta = f.trapezoid_integral_approximation(4)
sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40)
sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25))
sage: a = f.integral(definite=True)
sage: tt = text('area under curve = %s'%a, (1.5, -0.5))
sage: P+Q+t+tt
"""
def f(x0, x1):
f0, f1 = self(x0), self(x1)
return ((f1+f0)/2)*(x1-x0)
return self._riemann_sum_helper(N, f)
def critical_points(self):
"""
Return the critical points of this piecewise function.
.. warning::
Uses maxima, which prints the warning to use results with
caution. Only works for piecewise functions whose parts are
polynomials with real critical not occurring on the
interval endpoints.
EXAMPLES::
sage: R.<x> = QQ[]
sage: f1 = x^0
sage: f2 = 10*x - x^2
sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x
sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]])
sage: expected = [5, 12, 13, 14]
sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points()))
True
"""
from sage.calculus.calculus import maxima
x = var('x')
crit_pts = []
for (a,b), f in self.list():
for root in maxima.allroots(SR(f).diff(x)==0):
root = float(root.rhs())
if a < root < b:
crit_pts.append(root)
return crit_pts
def base_ring(self):
"""
Returns the base ring of the function pieces. This
is useful when this class is extended.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = x^2-5
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]])
sage: base_ring(f)
Symbolic Ring
::
sage: R.<x> = QQ[]
sage: f1 = x^0
sage: f2 = 10*x - x^2
sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x
sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]])
sage: f.base_ring()
Rational Field
"""
return (self.functions()[0]).base_ring()
def end_points(self):
"""
Returns a list of all interval endpoints for this function.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = x^2-5
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]])
sage: f.end_points()
[0, 1, 2, 3]
"""
intervals = self.intervals()
return [ intervals[0][0] ] + [b for a,b in intervals]
def __call__(self,x0):
"""
Evaluates self at x0. Returns the average value of the jump if x0
is an interior endpoint of one of the intervals of self and the
usual value otherwise.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f(0.5)
1
sage: f(5/2)
e^(5/2)
sage: f(5/2).n()
12.1824939607035
sage: f(1)
1/2
"""
n = self.length()
endpts = self.end_points()
for i in range(1,n):
if x0 == endpts[i]:
return (self.functions()[i-1](x0) + self.functions()[i](x0))/2
if x0 == endpts[0]:
return self.functions()[0](x0)
if x0 == endpts[n]:
return self.functions()[n-1](x0)
for i in range(n):
if endpts[i] < x0 < endpts[i+1]:
return self.functions()[i](x0)
raise ValueError,"Value not defined outside of domain."
def which_function(self,x0):
"""
Returns the function piece used to evaluate self at x0.
EXAMPLES::
sage: f1(z) = z
sage: f2(x) = 1-x
sage: f3(y) = exp(y)
sage: f4(t) = sin(2*t)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f.which_function(3/2)
x |--> -x + 1
"""
for (a,b), f in self.list():
if a <= x0 <= b:
return f
raise ValueError,"Function not defined outside of domain."
def default_variable(self):
r"""
Return the default variable. The default variable is defined as the
first variable in the first piece uses a variable. If no pieces have
a variable (each piece is a constant value), `x` is returned.
The result is cached.
AUTHOR: Paul Butler
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 5*x
sage: p = Piecewise([[(0,1),f1],[(1,4),f2]])
sage: p.default_variable()
x
sage: f1 = 3*var('y')
sage: p = Piecewise([[(0,1),4],[(1,4),f1]])
sage: p.default_variable()
y
"""
try:
return self.__default_variable
except AttributeError:
pass
for _, fun in self._list:
fun = SR(fun)
if fun.variables():
v = fun.variables()[0]
self.__default_variable = v
return v
v = var('x')
self.__default_value = v
return v
def integral(self, x=None, a=None, b=None, definite=False):
r"""
By default, returns the indefinite integral of the function.
If definite=True is given, returns the definite integral.
AUTHOR:
- Paul Butler
EXAMPLES::
sage: f1(x) = 1-x
sage: f = Piecewise([[(0,1),1],[(1,2),f1]])
sage: f.integral(definite=True)
1/2
::
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f.integral(definite=True)
1/2*pi
sage: f1(x) = 2
sage: f2(x) = 3 - x
sage: f = Piecewise([[(-2, 0), f1], [(0, 3), f2]])
sage: f.integral()
Piecewise defined function with 2 parts, [[(-2, 0), x |--> 2*x + 4], [(0, 3), x |--> -1/2*x^2 + 3*x + 4]]
sage: f1(y) = -1
sage: f2(y) = y + 3
sage: f3(y) = -y - 1
sage: f4(y) = y^2 - 1
sage: f5(y) = 3
sage: f = Piecewise([[(-4,-3),f1],[(-3,-2),f2],[(-2,0),f3],[(0,2),f4],[(2,3),f5]])
sage: F = f.integral(y)
sage: F
Piecewise defined function with 5 parts, [[(-4, -3), y |--> -y - 4], [(-3, -2), y |--> 1/2*y^2 + 3*y + 7/2], [(-2, 0), y |--> -1/2*y^2 - y - 1/2], [(0, 2), y |--> 1/3*y^3 - y - 1/2], [(2, 3), y |--> 3*y - 35/6]]
Ensure results are consistent with FTC::
sage: F(-3) - F(-4)
-1
sage: F(-1) - F(-3)
1
sage: F(2) - F(0)
2/3
sage: f.integral(y, 0, 2)
2/3
sage: F(3) - F(-4)
19/6
sage: f.integral(y, -4, 3)
19/6
sage: f.integral(definite=True)
19/6
::
sage: f1(y) = (y+3)^2
sage: f2(y) = y+3
sage: f3(y) = 3
sage: f = Piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]])
sage: f.integral()
Piecewise defined function with 3 parts, [[(-Infinity, -3), y |--> 1/3*y^3 + 3*y^2 + 9*y + 9], [(-3, 0), y |--> 1/2*y^2 + 3*y + 9/2], [(0, +Infinity), y |--> 3*y + 9/2]]
::
sage: f1(x) = e^(-abs(x))
sage: f = Piecewise([[(-infinity, infinity), f1]])
sage: f.integral(definite=True)
2
sage: f.integral()
Piecewise defined function with 1 parts, [[(-Infinity, +Infinity), x |--> -1/2*((sgn(x) - 1)*e^(2*x) - 2*e^x*sgn(x) + sgn(x) + 1)*e^(-x) - 1]]
::
sage: f = Piecewise([((0, 5), cos(x))])
sage: f.integral()
Piecewise defined function with 1 parts, [[(0, 5), x |--> sin(x)]]
TESTS:
Verify that piecewise integrals of zero work (trac #10841)::
sage: f0(x) = 0
sage: f = Piecewise([[(0,1),f0]])
sage: f.integral(x,0,1)
0
sage: f = Piecewise([[(0,1), 0]])
sage: f.integral(x,0,1)
0
sage: f = Piecewise([[(0,1), SR(0)]])
sage: f.integral(x,0,1)
0
"""
if a != None and b != None:
F = self.integral(x)
return F(b) - F(a)
if a != None or b != None:
raise TypeError, 'only one endpoint given'
area = 0
integrand_pieces = self.list()
integrand_pieces.sort()
new_pieces = []
if x == None:
x = self.default_variable()
for (start, end), fun in integrand_pieces:
fun = SR(fun)
if start == -infinity and not definite:
fun_integrated = fun.integral(x, end, x)
else:
try:
assume(start < x)
except ValueError:
pass
fun_integrated = fun.integral(x, start, x) + area
forget(start < x)
if definite or end != infinity:
area += fun.integral(x, start, end)
new_pieces.append([(start, end), SR(fun_integrated).function(x)])
if definite:
return SR(area)
else:
return Piecewise(new_pieces)
def convolution(self, other):
"""
Returns the convolution function,
`f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly
supported `f,g`.
EXAMPLES::
sage: x = PolynomialRing(QQ,'x').gen()
sage: f = Piecewise([[(0,1),1*x^0]]) ## example 0
sage: g = f.convolution(f)
sage: h = f.convolution(g)
sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1));
sage: # Type show(P+Q+R) to view
sage: f = Piecewise([[(0,1),1*x^0],[(1,2),2*x^0],[(2,3),1*x^0]]) ## example 1
sage: g = f.convolution(f)
sage: h = f.convolution(g)
sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1));
sage: # Type show(P+Q+R) to view
sage: f = Piecewise([[(-1,1),1]]) ## example 2
sage: g = Piecewise([[(0,3),x]])
sage: f.convolution(g)
Piecewise defined function with 3 parts, [[(-1, 1), 0], [(1, 2), -3/2*x], [(2, 4), -3/2*x]]
sage: g = Piecewise([[(0,3),1*x^0],[(3,4),2*x^0]])
sage: f.convolution(g)
Piecewise defined function with 5 parts, [[(-1, 1), x + 1], [(1, 2), 3], [(2, 3), x], [(3, 4), -x + 8], [(4, 5), -2*x + 10]]
"""
f = self
g = other
M = min(min(f.end_points()),min(g.end_points()))
N = max(max(f.end_points()),max(g.end_points()))
R2 = PolynomialRing(QQ,2,names=["tt","uu"])
tt,uu = R2.gens()
conv = 0
f0 = f.functions()[0]
g0 = g.functions()[0]
R1 = f0.parent()
xx = R1.gen()
var = repr(xx)
if len(f.intervals())==1 and len(g.intervals())==1:
f = f.unextend()
g = g.unextend()
a1 = f.intervals()[0][0]
a2 = f.intervals()[0][1]
b1 = g.intervals()[0][0]
b2 = g.intervals()[0][1]
i1 = repr(f0).replace(var,repr(uu))
i2 = repr(g0).replace(var,"("+repr(tt-uu)+")")
cmd1 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, tt-b1)
cmd2 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, tt-b1)
cmd3 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, a2)
cmd4 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, a2)
conv1 = maxima.eval(cmd1)
conv2 = maxima.eval(cmd2)
conv3 = maxima.eval(cmd3)
conv4 = maxima.eval(cmd4)
x = PolynomialRing(QQ,'x').gen()
fg1 = sage_eval(conv1.replace("tt",var), {'x':x})
fg2 = sage_eval(conv2.replace("tt",var), {'x':x})
fg3 = sage_eval(conv3.replace("tt",var), {'x':x})
fg4 = sage_eval(conv4.replace("tt",var), {'x':x})
if a1-b1<a2-b2:
if a2+b1!=a1+b2:
h = Piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b1),fg4],[(a2+b1,a2+b2),fg3]])
else:
h = Piecewise([[(a1+b1,a1+b2),fg1],[(a1+b2,a2+b2),fg3]])
else:
if a1+b2!=a2+b1:
h = Piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a1+b2),fg2],[(a1+b2,a2+b2),fg3]])
else:
h = Piecewise([[(a1+b1,a2+b1),fg1],[(a2+b1,a2+b2),fg3]])
return h
if len(f.intervals())>1 or len(g.intervals())>1:
z = Piecewise([[(-3*abs(N-M),3*abs(N-M)),0*xx**0]])
ff = f.functions()
gg = g.functions()
intvlsf = f.intervals()
intvlsg = g.intervals()
for i in range(len(ff)):
for j in range(len(gg)):
f0 = Piecewise([[intvlsf[i],ff[i]]])
g0 = Piecewise([[intvlsg[j],gg[j]]])
h = g0.convolution(f0)
z = z + h
return z.unextend()
def derivative(self):
r"""
Returns the derivative (as computed by maxima)
Piecewise(I,`(d/dx)(self|_I)`), as I runs over the
intervals belonging to self. self must be piecewise polynomial.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: f.derivative()
Piecewise defined function with 2 parts, [[(0, 1), x |--> 0], [(1, 2), x |--> -1]]
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f.derivative()
Piecewise defined function with 2 parts, [[(0, 1/2*pi), x |--> 0], [(1/2*pi, pi), x |--> 0]]
::
sage: f = Piecewise([[(0,1), (x * 2)]], x)
sage: f.derivative()
Piecewise defined function with 1 parts, [[(0, 1), x |--> 2]]
"""
x = var('x')
dlist = [[(a, b), derivative(f(x), x).function(x)] for (a,b),f in self.list()]
return Piecewise(dlist)
def tangent_line(self, pt):
"""
Computes the linear function defining the tangent line of the
piecewise function self.
EXAMPLES::
sage: f1(x) = x^2
sage: f2(x) = 5-x^3+x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2]])
sage: tf = f.tangent_line(0.9) ## tangent line at x=0.9
sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40)
sage: Q = tf.plot(rgbcolor=(0.7,0.2,0.2), plot_points=40)
sage: P + Q
"""
pt = QQ(pt)
R = QQ['x']
x = R.gen()
der = self.derivative()
tanline = (x-pt)*der(pt)+self(pt)
dlist = [[(a, b), tanline] for (a,b),f in self.list()]
return Piecewise(dlist)
def plot(self, *args, **kwds):
"""
Returns the plot of self.
Keyword arguments are passed onto the plot command for each piece
of the function. E.g., the plot_points keyword affects each
segment of the plot.
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: P = f.plot(rgbcolor=(0.7,0.1,0), plot_points=40)
sage: P
Remember: to view this, type show(P) or P.save("path/myplot.png")
and then open it in a graphics viewer such as GIMP.
TESTS:
We should not add each piece to the legend individually, since
this creates duplicates (:trac:`12651`). This tests that only
one of the graphics objects in the plot has a non-``None``
``legend_label``::
sage: f1 = sin(x)
sage: f2 = cos(x)
sage: f = piecewise([[(-1,0), f1],[(0,1), f2]])
sage: p = f.plot(legend_label='$f(x)$')
sage: lines = [
... line
... for line in p._Graphics__objects
... if line.options()['legend_label'] is not None ]
sage: len(lines)
1
"""
from sage.plot.all import plot, Graphics
g = Graphics()
for i, ((a,b), f) in enumerate(self.list()):
if i != 0 and 'legend_label' in kwds:
del kwds['legend_label']
g += plot(f, a, b, *args, **kwds)
return g
def fourier_series_cosine_coefficient(self,n,L):
r"""
Returns the n-th Fourier series coefficient of
`\cos(n\pi x/L)`, `a_n`.
INPUT:
- ``self`` - the function f(x), defined over -L x L
- ``n`` - an integer n=0
- ``L`` - (the period)/2
OUTPUT:
`a_n = \frac{1}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx`
EXAMPLES::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_cosine_coefficient(2,1)
pi^(-2)
sage: f(x) = x^2
sage: f = Piecewise([[(-pi,pi),f]])
sage: f.fourier_series_cosine_coefficient(2,pi)
1
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_cosine_coefficient(5,pi)
-3/5/pi
"""
from sage.all import cos, pi
x = var('x')
result = sum([(f(x)*cos(pi*x*n/L)/L).integrate(x, a, b)
for (a,b), f in self.list()])
if is_Expression(result):
return result.simplify_trig()
return result
def fourier_series_sine_coefficient(self,n,L):
r"""
Returns the n-th Fourier series coefficient of
`\sin(n\pi x/L)`, `b_n`.
INPUT:
- ``self`` - the function f(x), defined over -L x L
- ``n`` - an integer n0
- ``L`` - (the period)/2
OUTPUT:
`b_n = \frac{1}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx`
EXAMPLES::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_sine_coefficient(2,1) # L=1, n=2
0
"""
from sage.all import sin, pi
x = var('x')
result = sum([(f(x)*sin(pi*x*n/L)/L).integrate(x, a, b)
for (a,b), f in self.list()])
if is_Expression(result):
return result.simplify_trig()
return result
def _fourier_series_helper(self, N, L, scale_function):
r"""
A helper function for the construction of Fourier series. The
argument scale_function is a function which takes in n,
representing the `n^{th}` coefficient, and return an
expression to scale the sine and cosine coefficients by.
EXAMPLES::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f._fourier_series_helper(3, 1, lambda n: 1)
-4*cos(pi*x)/pi^2 + cos(2*pi*x)/pi^2 + 1/3
"""
from sage.all import pi, sin, cos, srange
x = var('x')
a0 = self.fourier_series_cosine_coefficient(0,L)
result = a0/2 + sum([(self.fourier_series_cosine_coefficient(n,L)*cos(n*pi*x/L) +
self.fourier_series_sine_coefficient(n,L)*sin(n*pi*x/L))*
scale_function(n)
for n in srange(1,N)])
return result.expand()
def fourier_series_partial_sum(self,N,L):
r"""
Returns the partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
as a string.
EXAMPLE::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_partial_sum(3,1)
-4*cos(pi*x)/pi^2 + cos(2*pi*x)/pi^2 + 1/3
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_partial_sum(3,pi)
-3*sin(2*x)/pi + 3*sin(x)/pi - 3*cos(x)/pi - 1/4
"""
return self._fourier_series_helper(N, L, lambda n: 1)
def fourier_series_partial_sum_cesaro(self,N,L):
r"""
Returns the Cesaro partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
as a string. This is a "smoother" partial sum - the Gibbs
phenomenon is mollified.
EXAMPLE::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_partial_sum_cesaro(3,1)
-8/3*cos(pi*x)/pi^2 + 1/3*cos(2*pi*x)/pi^2 + 1/3
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_partial_sum_cesaro(3,pi)
-sin(2*x)/pi + 2*sin(x)/pi - 2*cos(x)/pi - 1/4
"""
return self._fourier_series_helper(N, L, lambda n: 1-n/N)
def fourier_series_partial_sum_hann(self,N,L):
r"""
Returns the Hann-filtered partial sum (named after von Hann, not
Hamming)
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
as a string, where `H_N(x) = (1+\cos(\pi x/N))/2`. This is
a "smoother" partial sum - the Gibbs phenomenon is mollified.
EXAMPLE::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_partial_sum_hann(3,1)
-3*cos(pi*x)/pi^2 + 1/4*cos(2*pi*x)/pi^2 + 1/3
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_partial_sum_hann(3,pi)
-3/4*sin(2*x)/pi + 9/4*sin(x)/pi - 9/4*cos(x)/pi - 1/4
"""
from sage.all import cos, pi
return self._fourier_series_helper(N, L, lambda n: (1+cos(pi*n/N))/2)
def fourier_series_partial_sum_filtered(self,N,L,F):
r"""
Returns the "filtered" partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
as a string, where `F = [F_1,F_2, ..., F_{N}]` is a list
of length `N` consisting of real numbers. This can be used
to plot FS solutions to the heat and wave PDEs.
EXAMPLE::
sage: f(x) = x^2
sage: f = Piecewise([[(-1,1),f]])
sage: f.fourier_series_partial_sum_filtered(3,1,[1,1,1])
-4*cos(pi*x)/pi^2 + cos(2*pi*x)/pi^2 + 1/3
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_partial_sum_filtered(3,pi,[1,1,1])
-3*sin(2*x)/pi + 3*sin(x)/pi - 3*cos(x)/pi - 1/4
"""
return self._fourier_series_helper(N, L, lambda n: F[n])
def plot_fourier_series_partial_sum(self,N,L,xmin,xmax, **kwds):
r"""
Plots the partial sum
.. math::
f(x) \sim \frac{a_0}{2} + sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
over xmin x xmin.
EXAMPLE::
sage: f1(x) = -2
sage: f2(x) = 1
sage: f3(x) = -1
sage: f4(x) = 2
sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]])
sage: P = f.plot_fourier_series_partial_sum(3,pi,-5,5) # long time
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: P = f.plot_fourier_series_partial_sum(15,pi,-5,5) # long time
Remember, to view this type show(P) or P.save("path/myplot.png")
and then open it in a graphics viewer such as GIMP.
"""
from sage.plot.all import plot
return plot(self.fourier_series_partial_sum(N,L), xmin, xmax, **kwds)
def plot_fourier_series_partial_sum_cesaro(self,N,L,xmin,xmax, **kwds):
r"""
Plots the partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
over xmin x xmin. This is a "smoother" partial sum - the Gibbs
phenomenon is mollified.
EXAMPLE::
sage: f1(x) = -2
sage: f2(x) = 1
sage: f3(x) = -1
sage: f4(x) = 2
sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]])
sage: P = f.plot_fourier_series_partial_sum_cesaro(3,pi,-5,5) # long time
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: P = f.plot_fourier_series_partial_sum_cesaro(15,pi,-5,5) # long time
Remember, to view this type show(P) or P.save("path/myplot.png")
and then open it in a graphics viewer such as GIMP.
"""
from sage.plot.all import plot
return plot(self.fourier_series_partial_sum_cesaro(N,L), xmin, xmax, **kwds)
def plot_fourier_series_partial_sum_hann(self,N,L,xmin,xmax, **kwds):
r"""
Plots the partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
over xmin x xmin, where H_N(x) = (0.5)+(0.5)\*cos(x\*pi/N) is the
N-th Hann filter.
EXAMPLE::
sage: f1(x) = -2
sage: f2(x) = 1
sage: f3(x) = -1
sage: f4(x) = 2
sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]])
sage: P = f.plot_fourier_series_partial_sum_hann(3,pi,-5,5) # long time
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
sage: P = f.plot_fourier_series_partial_sum_hann(15,pi,-5,5) # long time
Remember, to view this type show(P) or P.save("path/myplot.png")
and then open it in a graphics viewer such as GIMP.
"""
from sage.plot.all import plot
return plot(self.fourier_series_partial_sum_hann(N,L), xmin, xmax, **kwds)
def plot_fourier_series_partial_sum_filtered(self,N,L,F,xmin,xmax, **kwds):
r"""
Plots the partial sum
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})],
over xmin x xmin, where `F = [F_1,F_2, ..., F_{N}]` is a
list of length `N` consisting of real numbers. This can be
used to plot FS solutions to the heat and wave PDEs.
EXAMPLE::
sage: f1(x) = -2
sage: f2(x) = 1
sage: f3(x) = -1
sage: f4(x) = 2
sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]])
sage: P = f.plot_fourier_series_partial_sum_filtered(3,pi,[1]*3,-5,5) # long time
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: P = f.plot_fourier_series_partial_sum_filtered(15,pi,[1]*15,-5,5) # long time
Remember, to view this type show(P) or P.save("path/myplot.png")
and then open it in a graphics viewer such as GIMP.
"""
from sage.plot.all import plot
return plot(self.fourier_series_partial_sum_filtered(N,L,F), xmin, xmax, **kwds)
def fourier_series_value(self,x,L):
r"""
Returns the value of the Fourier series coefficient of self at
`x`,
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^\infty [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], \ \ \ -L<x<L.
This method applies to piecewise non-polynomial functions as well.
INPUT:
- ``self`` - the function f(x), defined over -L x L
- ``x`` - a real number
- ``L`` - (the period)/2
OUTPUT: `(f^*(x+)+f^*(x-)/2`, where `f^*` denotes
the function `f` extended to `\RR` with period
`2L` (Dirichlet's Theorem for Fourier series).
EXAMPLES::
sage: f1(x) = 1
sage: f2(x) = 1-x
sage: f3(x) = exp(x)
sage: f4(x) = sin(2*x)
sage: f = Piecewise([[(-10,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f.fourier_series_value(101,10)
1/2
sage: f.fourier_series_value(100,10)
1
sage: f.fourier_series_value(10,10)
1/2*sin(20) + 1/2
sage: f.fourier_series_value(20,10)
1
sage: f.fourier_series_value(30,10)
1/2*sin(20) + 1/2
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(-pi,0),lambda x:0],[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f.fourier_series_value(-1,pi)
0
sage: f.fourier_series_value(20,pi)
-1
sage: f.fourier_series_value(pi/2,pi)
1/2
"""
xnew = x - int(RR(x/(2*L)))*2*L
endpts = self.end_points()
if xnew == endpts[0] or xnew == endpts[-1]:
return (self.functions()[0](endpts[0]) + self.functions()[-1](endpts[-1]))/2
else:
return self(xnew)
def cosine_series_coefficient(self,n,L):
r"""
Returns the n-th cosine series coefficient of
`\cos(n\pi x/L)`, `a_n`.
INPUT:
- ``self`` - the function f(x), defined over 0 x L (no
checking is done to insure this)
- ``n`` - an integer n=0
- ``L`` - (the period)/2
OUTPUT:
`a_n = \frac{2}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` such
that
.. math::
f(x) \sim \frac{a_0}{2} + \sum_{n=1}^\infty a_n\cos(\frac{n\pi x}{L}),\ \ 0<x<L.
EXAMPLES::
sage: f(x) = x
sage: f = Piecewise([[(0,1),f]])
sage: f.cosine_series_coefficient(2,1)
0
sage: f.cosine_series_coefficient(3,1)
-4/9/pi^2
sage: f1(x) = -1
sage: f2(x) = 2
sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]])
sage: f.cosine_series_coefficient(2,pi)
0
sage: f.cosine_series_coefficient(3,pi)
2/pi
sage: f.cosine_series_coefficient(111,pi)
2/37/pi
sage: f1 = lambda x: x*(pi-x)
sage: f = Piecewise([[(0,pi),f1]])
sage: f.cosine_series_coefficient(0,pi)
1/3*pi^2
"""
from sage.all import cos, pi
x = var('x')
result = sum([(2*f(x)*cos(pi*x*n/L)/L).integrate(x, a, b)
for (a,b), f in self.list()])
if is_Expression(result):
return result.simplify_trig()
return result
def sine_series_coefficient(self,n,L):
r"""
Returns the n-th sine series coefficient of
`\sin(n\pi x/L)`, `b_n`.
INPUT:
- ``self`` - the function f(x), defined over 0 x L (no
checking is done to insure this)
- ``n`` - an integer n0
- ``L`` - (the period)/2
OUTPUT:
`b_n = \frac{2}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx` such
that
.. math::
f(x) \sim \sum_{n=1}^\infty b_n\sin(\frac{n\pi x}{L}),\ \ 0<x<L.
EXAMPLES::
sage: f(x) = 1
sage: f = Piecewise([[(0,1),f]])
sage: f.sine_series_coefficient(2,1)
0
sage: f.sine_series_coefficient(3,1)
4/3/pi
"""
from sage.all import sin, pi
x = var('x')
result = sum([(2*f(x)*sin(pi*x*n/L)/L).integrate(x, a, b)
for (a,b), f in self.list()])
if is_Expression(result):
return result.simplify_trig()
return result
def laplace(self, x='x', s='t'):
r"""
Returns the Laplace transform of self with respect to the variable
var.
INPUT:
- ``x`` - variable of self
- ``s`` - variable of Laplace transform.
We assume that a piecewise function is 0 outside of its domain and
that the left-most endpoint of the domain is 0.
EXAMPLES::
sage: x, s, w = var('x, s, w')
sage: f = Piecewise([[(0,1),1],[(1,2), 1-x]])
sage: f.laplace(x, s)
(s + 1)*e^(-2*s)/s^2 - e^(-s)/s + 1/s - e^(-s)/s^2
sage: f.laplace(x, w)
(w + 1)*e^(-2*w)/w^2 - e^(-w)/w + 1/w - e^(-w)/w^2
::
sage: y, t = var('y, t')
sage: f = Piecewise([[(1,2), 1-y]])
sage: f.laplace(y, t)
(t + 1)*e^(-2*t)/t^2 - e^(-t)/t^2
::
sage: s = var('s')
sage: t = var('t')
sage: f1(t) = -t
sage: f2(t) = 2
sage: f = Piecewise([[(0,1),f1],[(1,infinity),f2]])
sage: f.laplace(t,s)
(s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2
"""
from sage.all import assume, exp, forget
x = var(x)
s = var(s)
assume(s>0)
result = sum([(SR(f)*exp(-s*x)).integral(x,a,b)
for (a,b),f in self.list()])
forget(s>0)
return result
def _make_compatible(self, other):
"""
Returns self and other extended to be defined on the same domain as
well as a refinement of their intervals. This is used for adding
and multiplying piecewise functions.
EXAMPLES::
sage: R.<x> = QQ[]
sage: f1 = Piecewise([[(0, 2), x]])
sage: f2 = Piecewise([[(1, 3), x^2]])
sage: f1._make_compatible(f2)
(Piecewise defined function with 2 parts, [[(0, 2), x], [(2, 3), 0]],
Piecewise defined function with 2 parts, [[(0, 1), 0], [(1, 3), x^2]],
[(0, 1), (1, 2), (2, 3)])
"""
a1, b1 = self.domain()
a2, b2 = other.domain()
a = min(a1, a2)
b = max(b1, b2)
F = self.extend_by_zero_to(a,b)
G = other.extend_by_zero_to(a,b)
endpts = list(set(F.end_points()).union(set(G.end_points())))
endpts.sort()
return F, G, zip(endpts, endpts[1:])
def __add__(self,other):
"""
Returns the piecewise defined function which is the sum of self and
other. Does not require both domains be the same.
EXAMPLES::
sage: x = PolynomialRing(QQ,'x').gen()
sage: f1 = x^0
sage: f2 = 1-x
sage: f3 = 2*x
sage: f4 = 10-x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: g1 = x-2
sage: g2 = x-5
sage: g = Piecewise([[(0,5),g1],[(5,10),g2]])
sage: h = f+g
sage: h
Piecewise defined function with 5 parts, [[(0, 1), x - 1], [(1, 2), -1], [(2, 3), 3*x - 2], [(3, 5), 8], [(5, 10), 5]]
Note that in this case the functions must be defined using
polynomial expressions *not* using the lambda notation.
"""
F, G, intervals = self._make_compatible(other)
fcn = []
for a,b in intervals:
fcn.append([(a,b), F.which_function(b)+G.which_function(b)])
return Piecewise(fcn)
def __mul__(self,other):
r"""
Returns the piecewise defined function which is the product of one
piecewise function (self) with another one (other).
EXAMPLES::
sage: x = PolynomialRing(QQ,'x').gen()
sage: f1 = x^0
sage: f2 = 1-x
sage: f3 = 2*x
sage: f4 = 10-x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: g1 = x-2
sage: g2 = x-5
sage: g = Piecewise([[(0,5),g1],[(5,10),g2]])
sage: h = f*g
sage: h
Piecewise defined function with 5 parts, [[(0, 1), x - 2], [(1, 2), -x^2 + 3*x - 2], [(2, 3), 2*x^2 - 4*x], [(3, 5), -x^2 + 12*x - 20], [(5, 10), -x^2 + 15*x - 50]]
sage: g*(11/2)
Piecewise defined function with 2 parts, [[(0, 5), 11/2*x - 11], [(5, 10), 11/2*x - 55/2]]
Note that in this method the functions must be defined using
polynomial expressions *not* using the lambda notation.
"""
if isinstance(other,Rational) or isinstance(other,Integer):
return Piecewise([[(a,b), other*f] for (a,b),f in self.list()])
else:
F, G, intervals = self._make_compatible(other)
fcn = []
for a,b in intervals:
fcn.append([(a,b),F.which_function(b)*G.which_function(b)])
return Piecewise(fcn)
__rmul__ = __mul__
def __eq__(self,other):
"""
Implements Boolean == operator.
EXAMPLES::
sage: f1 = x^0
sage: f2 = 1-x
sage: f3 = 2*x
sage: f4 = 10-x
sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: g = Piecewise([[(0,1),1],[(1,2),f2],[(2,3),f3],[(3,10),f4]])
sage: f==g
True
"""
return self.list()==other.list()