piecewise -- the domain of
conditionally defined objects
Introductionpiecewise([condition1, object1], [condition2, object2],
...) generates a conditionally defined object that equals
object1 if condition1 is satisfied,
object2 if condition2 is satisfied, etc.
Creating
Elementspiecewise([condition1, object1], [condition2, object2],
...)
Parameterscondition1, condition2, ... |
- | Boolean constants or expressions representing logical formulas |
object1, object2, ... |
- | arbitrary objects |
Side
EffectsProperties of identifiers set
by assume are taken
into account.
Related
Functions
Detailspiecewise differs from the if and case branching statements in two ways.
First, the property mechanism is
used to decide the truth of the conditions. Hence the result depends on
the properties of the identifiers that
appear in the conditions. Second, piecewise treats
conditions mathematically, while if and case evaluate them syntactically. Cf.
example 2.[condition, object] is called a
branch. If condition is provably false, then the
branch is discarded altogether. If condition is provably
true, then piecewise returns object. If none
of the conditions is provably true, an object of type
piecewise is created containing all branches that have not
been discarded.
If all conditions are provably false, or if no branch is given, then
piecewise returns undefined. Cf. example 1.
piecewise returns the first object defined under a
condition that is recognized to be true. The user has to
ensure that the objects corresponding to the true conditions all have
the same mathematical meaning. You cannot rely on the system to
recognize the first mathematically true condition as true.piecewise is evaluated, the
truth of the conditions is checked again for the current values and the
current properties of the identifiers involved. This may be used to
simplify the result of a computation under various different
assumptions.piecewise
automatically de-nests (``flattens'') such
objects. For example, ``if A then (if B then C)'' becomes ``if A and B
then C''. Cf. example 7.f is such an operation and
p1, p2, ... are conditionally defined objects, then
f(p1, p2, ...) is the conditionally defined object
consisting of all branches of the form [condition1 and condition2
and ..., f(object1, object2, ...)], where[condition1, object1] is a branch of p1,
[condition2, object2] is a branch of p2, etc.
This can also be understood as follows: applying f
commutes with any assignment to free parameters in the conditions.
Conditionally defined objects can also be mixed with other objects
in such operations: If, e.g., p1 is not a conditionally
defined object, it is handled like a conditionally defined object with
the only branch [TRUE, p1].
piecewise on the left hand
side_in(piecewise p, set
S)p is an element of S''._in.contains to the objects in all
branchescontains(piecewise p, any a)diff(piecewise p <, identifier x...>)p with respect to the given variables, starting with the
leftmost one.p is returned.diff.discont(piecewise p, identifier x <, domain
F>)p regarded as a function depending on x,
namely, the union of the results of applying discont to the objects in all
branches of p, plus the boundary points of the conditions
of p with respect to x.p must be arithmetical expressions.discont.discont.piecewise::disregardPoints(piecewise
p)expand to the objects in all
branchesexpand(piecewise p)expand.piecewise::getElement(piecewise
p)p. All such objects must represent sets.FAIL if
no such common element can be found.solvelib::getElement.int(piecewise p, identifier x <, range
r>)p, where p is regarded as a piecewise
defined function of x. It applies the function int to the objects in all branches
of p.a..b is given, this method computes the
definite integral of p when x runs through
that range.int.piecewise::isFinite(piecewise
p)TRUE if the objects in all branches of
p are finite sets, and it returns FALSE if the objects in all branches
of p are infinite sets. Otherwise, it returns UNKNOWN.solvelib::isFinite.normal to the objects in all
branchesnormal(piecewise p)normal.piecewise::restrict(any p, condition C)p is not a conditionally defined object, this
method creates the conditionally defined object with a single branch
[C, p]. If p is conditionally defined, each
condition cond in p is replaced by cond
and C.piecewise
on the right hand sidepiecewise::set2expr(piecewise p, identifier x)x that is equivalent to ``x is an element of
p''.p must represent
sets._in.simplify(piecewise p)simplify
is applied to the objects in all branches.simplify.solve(piecewise p, identifier x <, option1, option2, ...>)p for the variable x.
The objects in all branches of p must be either equations,
inequalities, or arithmetical expressions;
each arithmetical expression e is replaced by an equation
e = 0.[condition, object] of p,
with object being an equation or inequality, the method
determines the set of all values x such that both
condition and object become true
mathematically, and returns the union of all obtained sets. The return
value may be a conditionally defined set.solve. See the corresponding help page
for a description of the available options and an overview of the types
of sets that may be returned.piecewise::solveConditions(piecewise p,
identifier x)p containing
x in the form ``x in S'' for some appropriate
set S.piecewise::Union(piecewise p, identifier x, set indexset)p, where p is regarded as a system of sets
parameterized by x and x runs through all
elements of indexset.p must represent
sets.[condition, object] of p,
this method does the following. It substitutes for x in
object all those values from indexset
satisfying condition and takes the union over all obtained
sets. Then it returns the union over the resulting sets for all
branches.solvelib::Union._concat(piecewise p...)_concat.piecewise::condition(piecewise p, positive integer i)ith branch of
p. Cf. example 4.piecewise::expression(piecewise p, positive integer i)ith branch of
p. Cf. example 4.piecewise::insert(piecewise p, branch b, positive integer
i)map(piecewise p, any
f <, any a...>)[condition, object] of p,
object is replaced by f(object <,
a...>).map.piecewise::mapConditions(piecewise p, any f <, any
a...>)[condition, object] of p,
condition is replaced by f(condition <,
a...>).map to the objects in all branchespiecewise::remove(piecewise p, positive integer i)p by deleting the ith branch. Cf.
example 4.piecewise::selectConditions(piecewise p,
any f <, any
a...>)select with the selection criterion
given by f applied to the conditions of p. It
returns the piecewise object derived from p by removing
every branch [condition, object] for which
f(condition <, a...>) does not yield TRUE.p, f(condition <,
a...>) must return a Boolean constant.undefined is
returned.piecewise::splitConditions(piecewise p,
any f <, any
a...>)split with the splitting criterion
given by f applied to the conditions of p. It
returns a list of three conditionally defined objects, comprising those
branches [condition, object] of p for which
f(condition <, a...>) yields TRUE, FALSE, and UNKNOWN, respectively.
If, for some of the three Boolean values, no branch yields that
value, then the returned list contains undefined instead of a
conditionally defined object with zero branches at the corresponding
position.
p, f(condition <,
a...>) must return a Boolean constant.subs(piecewise p, substitution s...)s in both the
conditions and the objects of p.subs. The calling syntax is identical
to that function; cf. the corresponding help page for a description of
the various types that are allowed for s.zip(any p1, any p2,
any f)p1 and p2 are conditionally
defined objects, then this method returns the conditionally defined
object comprising all branches of the form [condition1 and
condition2, f(object1, object2)], where [condition1,
object1] is a branch of p1 and [condition2,
object2] is a branch of p2.p1, say--is of
type piecewise, then each branch [condition,
object] of p1 is replaced by [condition,
f(object, p2)].p1 nor p2 are of type
piecewise, then piecewise::zip(p1, p2, f)
returns f(p1, p2).zip.
Example
1We define f as the characteristic function
of the interval [0,1]:
>> f := x -> piecewise([x < 0 or x > 1, 0], [x >= 0 or x <= 1, 1])
x -> piecewise([x < 0 or 1 < x, 0], [0 <= x or x <= 1, 1])
None of the conditions can be evaluated to TRUE or FALSE, unless more is known about the
variable x. When we evaluate f at some point,
the conditions are checked again:
>> f(0), f(2), f(I)
1, 0, undefined
Example
2piecewise performs a case analysis using
the property mechanism. It checks whether the given conditions are
mathematically true or false; it may also decide that not
enough information is available. In the following example, it cannot be
decided whether a is zero as long as no assumptions on
a have been made:
>> delete a: p := piecewise([a = 0, 0], [a <> 0, 1/a])
/ 1 \
piecewise| 0 if a = 0, - if a <> 0 |
\ a /
In contrast, if-statements evaluate the conditions
syntactically: a=0 is technically false since the
identifier a and the integer 0 are different
objects:
>> if a = 0 then 0 else 1/a end
1
-
a
Moreover, piecewise takes properties of
identifiers into account:
>> assume(a = 0): p; delete a, p:
0
Example
3Conditionally defined objects can be created by rewriting special functions:
>> f := rewrite(sign(x), piecewise)
/
piecewise| 1 if 0 < x, -1 if x < 0, 0 if x = 0,
|
\
x \
-------------------- if not x in R_ |
2 2 1/2 |
(Im(x) + Re(x) ) /
In contrast to MuPAD, most people like to regard
sign as a function
defined for real numbers only. You might therefore want to restrict the
domain of f:
>> f := piecewise::restrict(f, x in R_)
piecewise(1 if x in ]0, infinity[, -1 if x in ]-infinity, 0[,
0 if x in {0})
Conditionally defined arithmetical expressions allow roughly the same operations as ordinary arithmetical expressions. The result of an arithmetical operation is only defined at those points where all of the arguments are defined:
>> f + piecewise([x < 2, 5])
piecewise(6 if 0 < x and x < 2, 4 if x < 0, 5 if x = 0)
Example
4There are several methods for extracting branches, conditions, and objects. Consider the following conditionally defined object:
>> f := piecewise([x > 0, 1], [x < -3, x^2])
2
piecewise(1 if 0 < x, x if x < -3)
You can extract a specific condition or object:
>> piecewise::condition(f, 1), piecewise::expression(f, 2)
2
0 < x, x
The function op extracts whole branches:
>> op(f, 1)
1 if 0 < x
You can form another piecewise defined object out of
those branches for which the condition satisfies a given selection
criterion, or split the input into two piecewise defined objects, as
the system functions select and split do it for lists:
>> piecewise::selectConditions(f, has, 0)
piecewise(1 if 0 < x)
>> piecewise::splitConditions(f, has, 0)
2
[piecewise(1 if 0 < x), piecewise(x if x < -3), undefined]
You can also create a copy of f with some
branches added or removed:
>> piecewise::remove(f, 1)
2
piecewise(x if x < -3)
>> piecewise::insert(f, [x > -3 and x < 0, sin(x)], 2)
2
piecewise(1 if 0 < x, sin(x) if x < 0 and -3 < x, x if x < -3)
Example
5Most unary functions are overloaded for
piecewise by mapping them to the objects in all branches
of the input. This can also be achieved using map:
>> f := piecewise([x >= 0, arcsin(x)], [x < 0, arccos(x)]): sin(f)
2 1/2
piecewise(x if 0 <= x, (- x + 1) if x < 0)
>> map(f, sin)
2 1/2
piecewise(x if 0 <= x, (- x + 1) if x < 0)
This causes the following problem. If one of the
conditions becomes true, e.g., by some assumption on x,
then f evaluates to an object that is not of type
piecewise. Applying sin then still works, but map maps the sine function to the
operands of f. Hence map should be used with care:
>> assume(x < 0): sin(f); map(f, sin);
2 1/2
(1 - x )
arccos(sin(x))
>> delete x:
The converse problem occurs if you want to apply the
function map to the
objects in all branches. The method mapMap should be used
for this purpose.
Example
6Sets may also be conditionally defined. Such sets are
sometimes returned by solve:
>> S := solve(a*x = 0, x)
piecewise(C_ if a = 0, {0} if a <> 0)
The usual set-theoretic operations work for such sets:
>> S intersect Dom::Interval(3, 5)
piecewise(]3, 5[ if a = 0, {} if a <> 0)
Sometimes it is interesting to exclude the ``rare cases'' which only cover a small set of parameter values:
>> piecewise::disregardPoints(S)
{0}
Example
7Consider the following case distinction:
>> p1 := piecewise([a > 0, a^2], [a <= 0, -a^2]): p2 := piecewise([b > 0, a + b], [b = 0, p1 + b], [b < 0, a + b])
2
piecewise(a + b if 0 < b, b + a if 0 < a and b = 0,
2
b - a if b = 0 and a <= 0, a + b if b < 0)
Note that the system has moved the case analysis done in
p1 to the top level automatically. However, some
simplifications are still possible: the branches b>0
and b<0 can be collected, and in the case
b=0 the identifier b may be replaced by the
value 0:
>> simplify(p2)
2
piecewise(a + b if b <> 0, a if 0 < a and b = 0,
2
- a if b = 0 and a <= 0)
Backgroundstdlib::branch.piecewise. This
simplifies the use of piecewise: it is always allowed to
enter p:=piecewise(...) and to call some method of
piecewise with p as argument. You need not
care about the special case where p is not of type
piecewise because some condition in its definition is true
or all conditions are false.