# problem/ possible bug with picewise constant functions

168 views

### Pablo De Napoli

Feb 16, 2015, 8:46:06 AM2/16/15
to sage-devel
Hi,

I'm having trouble with some piecewise constant functions.

Suppose that I define

f=Piecewise ([([0,1],0),([1,2],x-1)])

Then f.integral() works as expected, but f.derivative() will fail with

TypeError: 'sage.rings.integer.Integer' object is not callable

It seems that Sage does not understand that 0 is the null function,
and treat it as an integer for with a derivative is not meaningful

Then, I've tried defining

f1=Piecewise ([([0,1],ConstantFunction(0)),([1,2],x-1)])

f1.derivative() now works and one would expect, but
f1.integral() fails with a TypeError

My last try was to cast 0 to the symbolic ring

f2=Piecewise ([([0,1],SR(0)),([1,2],x-1)])

Now f2.integral() works, but f2.derivative() fails with the error message

ValueError: the number of arguments must be less than or equal to 0

Whats the right way to define my function so that both integral and
derivative work ? The behavior of Sage is annoying ! Any help is
welcome!

best regards
Pablo

### Pablo De Napoli

Feb 16, 2015, 9:22:17 AM2/16/15
to sage-devel
Hi,

Another strange behavoir:

sage: f=Piecewise([[(1/3,1/2),x]])
sage: f.extend_by_zero_to(0,1)
Piecewise defined function with 3 parts, [[(0, 1/3), 0], [(1/3, 1/2),
x], [(1/2, 1), 0]]
sage: f.domain()
(1/3, 1/2)

extend_by_zero shouldn't have changed the domain to (0,1) ?

sage: f.integral()
Piecewise defined function with 1 parts, [[(1/3, 1/2), x |--> 1/2*x^2 - 1/18]]

f.integral also forgets about the extension by zero?

sage: f.derivative()
/media/sdb3/pablo.sdb3/sage/sage-6.3-x86_64-Linux/src/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=...)
See http://trac.sagemath.org/5930 for details.
#!/usr/bin/env python
Piecewise defined function with 1 parts, [[(1/3, 1/2), x |--> 1]]

A bit annoying message, not?

Pablo

### Nils Bruin

Feb 16, 2015, 11:33:49 AM2/16/15

On Monday, February 16, 2015 at 5:46:06 AM UTC-8, pdenapo wrote:
Hi,

I'm having trouble with some piecewise constant functions.

Suppose that I define

f=Piecewise ([([0,1],0),([1,2],x-1)])

Then f.integral() works as expected, but f.derivative() will fail with

TypeError: 'sage.rings.integer.Integer' object is not callable
It seems that Sage does not understand that 0 is the null function,
and treat it as an integer for with a derivative is not meaningful

Well ... it isn't the null function. You have to construct it as such. Similarly "x-1" isn't a function but an expression. Computer algebra systems tend to be a little finicky that way. In order to get a function, you can do

sage: (x-1).function(x)
x |--> x - 1

which tells sage to make a function that uses its first (and only) argument to substitute for x in x-1. This is required to resolve ambiguity if there are more variables:

sage: A=(x-y).function(x,y)
sage: B=(x-y).function(y,x)

sage: var('y')
y
sage: A=(x-y).function(x,y)
sage: B=(x-y).function(y,x)
sage: A(1,2)
-1
sage: B(1,2)
1

You see that A,B are distinct functions.

Then, I've tried defining

f1=Piecewise ([([0,1],ConstantFunction(0)),([1,2],x-1)])

Yes, it seems ConstantFunction is poorly integrated with SR. You could use SR(0).function(x) instead.

My last try was to cast 0 to the symbolic ring

f2=Piecewise ([([0,1],SR(0)),([1,2],x-1)])
Now f2.integral() works, but f2.derivative() fails with the error message

ValueError: the number of arguments must be less than or equal to 0

Whats the right way to define my function so that both integral and
derivative work ?

f3=Piecewise([([0,1],SR(0).function(x)),([1,2],(1-x).function(x))])

The behavior of Sage is annoying !  Any help is
welcome!

The hard thing is probably to learn to distinguish between "functions" and "expressions". This distinction is important because from an expression you can't see which variables are supposed to be "parameters", and in which order they should be read off from a function call g(a,b,c,d).

On Monday, February 16, 2015 at 6:22:17 AM UTC-8, pdenapo wrote:Hi,

> Another strange behavoir:
> sage: f=Piecewise([[(1/3,1/2),x]])
> sage: f.extend_by_zero_to(0,1)
> Piecewise defined function with 3 parts, [[(0, 1/3), 0], [(1/3, 1/2), x], [(1/2, 1), 0]]
> sage: f.domain()
> (1/3, 1/2)
> extend_by_zero shouldn't have changed the domain to (0,1) ?

no, it returned as its value a new function with the domain extended.

sage:  f=Piecewise([[(1/3,1/2),x.function(x)]])
sage: f
Piecewise defined function with 1 parts, [[(1/3, 1/2), x |--> x]]
sage: g=f.extend_by_zero_to(0,1)
sage: g
Piecewise defined function with 3 parts, [[(0, 1/3), 0], [(1/3, 1/2), x |--> x], [(1/2, 1), 0]]
sage: g.domain()
(0, 1)

> sage: f.derivative()
> /media/sdb3/pablo.sdb3/sage/
sage-6.3-x86_64-Linux/src/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=...)
See http://trac.sagemath.org/5930 for details.
#!/usr/bin/env python
Piecewise defined function with 1 parts, [[(1/3, 1/2), x |--> 1]]
A bit annoying message, not?

That message is mainly annoying because sage didn't give you an outright error before, when defining a piecewise function with invalid input (with an expression instead of a function). As it's saying, it's deprecation warning. It will turn into an outright error any day now (and Piecewise should probably have thrown an error at you earlier).

### Nils Bruin

Feb 16, 2015, 11:39:32 AM2/16/15
On Monday, February 16, 2015 at 8:33:49 AM UTC-8, Nils Bruin wrote:

f3=Piecewise([([0,1],SR(0).function(x)),([1,2],(1-x).function(x))])

Incidentally, the Piecewise documentation, which you can get with "Piecewise?" , has a nice shortcut form:

sage: f3 = Piecewise([([0,1],SR(0)), ([1,2],1-x)], x)

(unfortunately it is necessary to write "SR(0)" there)

### Pablo De Napoli

Feb 17, 2015, 7:14:37 AM2/17/15
to sage-devel
Many thanks Nils for your help.

I think that is important that sage has consistent and easy to use
interfaces, that functions do what most people would expect them to do
at every place. Specially if we want it to be used in calculus
classes, etc.

Writing something like

SR(0).function(x)

ConstantFunction(0)

is not what most mathematicians or students would do, I guess. Maybe
there is something to improve here.

Pablo
> --
> You received this message because you are subscribed to the Google Groups
> "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> To post to this group, send email to sage-...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sage-devel.
> For more options, visit https://groups.google.com/d/optout.

### Nils Bruin

Feb 17, 2015, 11:46:13 AM2/17/15
On Tuesday, February 17, 2015 at 4:14:37 AM UTC-8, pdenapo wrote:
Writing something like

SR(0).function(x)

ConstantFunction(0)

is not what most mathematicians or students would do,  I guess. Maybe
there is something to improve here.

Yes, looking at the documentation and the use of ConstantFunction in the sage library, it seems this was put in to have a convenient way of making constant maps in the coercion system. It is not at all integrated with symbolics. So from a calculus point of view, it is unfortunate this is exposed and discoverable (because it's presently useless for calculus). Perhaps it can be equipped with some hooks to better integrate it with symbolics or otherwise be hidden from view.

Incidentally, the fact that f.derivative() and f.integral() work for some symbolic functions (notably, the ones made with expr.function(...) ) is a byproduct of their implementation. Neither works in general:

sage: sin.derivative()
AttributeError: 'Function_sin' object has no attribute 'derivative'
sage: sin.integral()
AttributeError: 'Function_sin' object has no attribute 'integral'

Both operations need to be performed on expressions, i.e., sin(x).derivative(x) and sin(x).integral(x), because they need a variable name to do the operation with respect to.

The present situation:

sage: sin(x+y).function(x,y).integral() #gives a deprecation warning
(x, y) |--> -cos(x + y)
sage: sin(x+y).function(x,y).derivative() #this makes perfect sense: it's the total derivative.
(x, y) |--> (cos(x + y), cos(x + y))
sage: sin(x+y).function(x,y).derivative().derivative()
[(x, y) |--> -sin(x + y) (x, y) |--> -sin(x + y)]
[(x, y) |--> -sin(x + y) (x, y) |--> -sin(x + y)]
sage: sin(x+y).function(x,y).derivative().derivative().derivative() #fails

### Francisco Pena

Apr 21, 2016, 1:44:19 PM4/21/16
to sage-devel
Hi,

I believe the solution of Nils using SR(0) is very elegant, but it cannot be applied in every case. For example, when the piecewise is created by another method (trapezoid):

`f = Piecewise([[(-1,1), sin(x^2)]])t = f.trapezoid(3)`

Here t has a constant part in (-1/3,1/3):

`Piecewise defined function with 3 parts, [[(-1, -1/3), -3/2*(x + 1)*(sin(1) - sin(1/9)) + sin(1)], [(-1/3, 1/3), sin(1/9)], [(1/3, 1), 1/2*(3*x - 1)*(sin(1) - sin(1/9)) + sin(1/9)]]`

So t(0.1) gives the following error:

`ValueError: the number of arguments must be less than or equal to 0`

Best regards,
Francisco Pena

### Nils Bruin

Apr 21, 2016, 3:04:27 PM4/21/16
to sage-devel
On Thursday, April 21, 2016 at 10:44:19 AM UTC-7, Francisco Pena wrote:
Hi,

I believe the solution of Nils using SR(0) is very elegant, but it cannot be applied in every case. For example, when the piecewise is created by another method (trapezoid):

`f = Piecewise([[(-1,1), sin(x^2)]])`

To be sure of what sage is doing, this should be

`f = Piecewise([[(-1,1), sin(x^2).function(x)]])`

`t = f.trapezoid(3)`

That's just a bug in the implementation of the trapezoid method. A lot of people have worked on symbolics in sage without realizing the difference between callable functions and expressions. It sort-of works because expressions used to be callable (but you wouldn't know which variable the value would be substituted in!), but as you can see, only with the number of variables that actually occur.

f.trapezoid should be rewritten to include the appropriate `.function(x)` calls or make things into `lambda x:` expressions in the definition.

Here t has a constant part in (-1/3,1/3):

`Piecewise defined function with 3 parts, [[(-1, -1/3), -3/2*(x + 1)*(sin(1) - sin(1/9)) + sin(1)], [(-1/3, 1/3), sin(1/9)], [(1/3, 1), 1/2*(3*x - 1)*(sin(1) - sin(1/9)) + sin(1/9)]]`

So t(0.1) gives the following error:

`ValueError: the number of arguments must be less than or equal to 0`

Nice example!

### David Joyner

Apr 21, 2016, 3:21:47 PM4/21/16
to sage-devel
Sorry, but I missed this message earlier.

sage: PR.<x> = PolynomialRing(RR, "x")
sage: f = Piecewise([([0,1],0*x),([1,2],x-1)])
sage: f.derivative()
Piecewise defined function with 2 parts, [[(0, 1), x |--> 0], [(1, 2),
x |--> 1]]
sage: f.integral()
Piecewise defined function with 2 parts, [[(0, 1), x |--> 0], [(1, 2),
x |--> 1/2*x^2 - 1.0*x + 0.5]]

> best regards
> Pablo

### David Joyner

Apr 21, 2016, 3:26:02 PM4/21/16
to sage-devel
On Tue, Feb 17, 2015 at 7:14 AM, Pablo De Napoli <pde...@gmail.com> wrote:
> Many thanks Nils for your help.
>
> I think that is important that sage has consistent and easy to use
> interfaces, that functions do what most people would expect them to do
> at every place. Specially if we want it to be used in calculus
> classes, etc.
>
> Writing something like
>
> SR(0).function(x)
>
>
> ConstantFunction(0)
>
> is not what most mathematicians or students would do, I guess. Maybe
> there is something to improve here.
>

Agreed. The Piecewise class is very old and needs to be rewritten. I
think it even predates the nice symbolic function interface Sagemath
has.

### Ralf Stephan

Apr 21, 2016, 11:50:49 PM4/21/16
to sage-devel
Message has been deleted

### David Joyner

Apr 22, 2016, 4:11:09 PM4/22/16
to sage-devel
On Thu, Apr 21, 2016 at 11:50 PM, Ralf Stephan <gtr...@gmail.com> wrote:
> http://trac.sagemath.org/ticket/14801
>

I love this!!

You and Volker have done such great work on this and I really am very
much looking forward to this being put into Sage. Also, I don't see
any examples which used to work but don't work with the new version

A comment on how I tested this (since I don't know git), I downloaded
your piecewise.py file from git. Added it to a new version of SageMath
(since "sage -clone" no longer works) and ran "sage -b". Then I
started sage and just ran then examples in the docstring. I tweeked
several just to see how minor changes affected things. They all went
perfectly.

> Regards,
>
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To post to this group, send email to sage-...@googlegroups.com.
> Visit this group at https://groups.google.com/group/sage-devel.