piecewise
-- the domain of
conditionally defined objectspiecewise(
[condition1, object1], [condition2, object2],
...)
generates a conditionally defined object that equals
object1
if condition1
is satisfied,
object2
if condition2
is satisfied, etc.
piecewise([condition1, object1], [condition2, object2],
...)
condition1, condition2, ... |
- | Boolean constants or expressions representing logical formulas |
object1, object2, ... |
- | arbitrary objects |
Properties of identifiers set
by assume
are taken
into account.
piecewise
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)
i
th branch of
p
. Cf. example 4.piecewise::expression(piecewise p, positive integer i)
i
th 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 i
th 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
.We 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
piecewise
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
Conditionally 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)
There 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)
Most 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.
Sets 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}
Consider 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)
stdlib::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.