Piecewise functions in Sage 7.2

141 views
Skip to first unread message

paulmasson

unread,
May 21, 2016, 6:06:27 PM5/21/16
to sage-devel
Since the SageMathCell server has been updated to Sage 7.2, I've been experiencing issues with piecewise functions. For example, define a simple saw tooth with

f=piecewise([[(0,1),x],[(1,3),2-x],[(3,4),x-4]])

The endpoints appear to be defined just fine,

f.end_points()
[0, 1, 3, 4]

but if I evaluate f(1) I get this message:


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-3e93894c0576> in <module>()
      2 #show(plot(f))
      3 f.end_points()
----> 4 f(Integer(1))

/home/sc_serv/sage/local/lib/python2.7/site-packages/sage/symbolic/function_factory.pyc in new_f(ex, *args, **kwds)
    400         new_args = list(ex._unpack_operands())
    401         new_args.extend(args)
--> 402         return f(ex, *new_args, **kwds)
    403     return new_f

/home/sc_serv/sage/local/lib/python2.7/site-packages/sage/functions/piecewise.py in __call__(cls, self, parameters, variable, value, **kwds)
    447             if value is not None:
    448                 substitution[variable] = value
--> 449             return self.subs(substitution)
    450
    451         def _fast_float_(cls, self, *args):

/home/sc_serv/sage/src/sage/symbolic/expression.pyx in sage.symbolic.expression.Expression.substitute (/home/sc_serv/sage/src/build/cythonized/sage/symbolic/expression.cpp:27845)()
   4883
   4884         return new_Expression_from_GEx(self._parent,
-> 4885                                        self._gobj.subs_map(smap, 0))
   4886
   4887     subs = substitute

/home/sc_serv/sage/local/lib/python2.7/site-packages/sage/functions/piecewise.py in _subs_(self, subs_map, options, parameters, x)
    228             if domain.contains(point):
    229                 return subs_map.apply_to(func, 0)
--> 230         raise ValueError('point {} is not in the domain'.format(point))
    231
    232     @staticmethod

ValueError: point 1 is not in the domain

Adding decimal points to the endpoints to coerce them to float values doesn't fix the problem.

Sage 7.2 includes http://trac.sagemath.org/ticket/14801 that is supposed to fix problems with symbolics. Am I missing some new behavior or is this a bug?

Volker Braun

unread,
May 21, 2016, 6:47:29 PM5/21/16
to sage-devel
Thats the expected output since 1 is not in the domain:

sage: f.domain()
(0, 1) + (1, 3) + (3, 4)

You probably want

sage: g = piecewise([[(0,1),x], [[1,3],2-x], [(3,4),x-4]])
sage: g.domain()
(0, 4)
sage: g.plot([0,4])

Michael Orlitzky

unread,
May 21, 2016, 6:58:27 PM5/21/16
to sage-...@googlegroups.com
On 05/21/2016 06:06 PM, paulmasson wrote:
> Since the SageMathCell server has been updated to Sage 7.2, I've been
> experiencing issues with piecewise functions. For example, define a
> simple saw tooth with
>

Piecewise functions were totally rewritten in #14801. Lower-case
"piecewise" now works a little bit different than upper-case "Piecewise"
used to. The new version differentiates between the open interval (1,2)
and the closed interval [1,2].

If you need the compatibility, the upper-case version is still there.

(I noticed piecewise? doesn't give me any useful docs, I get the
docstring for LazyImport.)

paulmasson

unread,
May 21, 2016, 7:13:38 PM5/21/16
to sage-devel
Thanks for clearing that up. This is definitely new behavior.

paulmasson

unread,
May 21, 2016, 7:38:06 PM5/21/16
to sage-devel
Another problem I've encountered concerns including numerical integrals in piecewise functions. This used to work in Sage 6.9:

def g(x):
   
var('u')
   
return numerical_integral(u^2,0,x)[0]

f
=piecewise([ [(0,1), g ] ])

but now gives the error message


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-d490d0695d4b> in <module>()
      4
      5
----> 6 f=piecewise([ [(Integer(0),Integer(1)), g ] ])
      7

/home/sc_serv/sage/src/sage/misc/lazy_import.pyx in sage.misc.lazy_import.LazyImport.__call__ (/home/sc_serv/sage/src/build/cythonized/sage/misc/lazy_import.c:3628)()
    384             True
    385         """
--> 386         return self._get_object()(*args, **kwds)
    387
    388     def __repr__(self):

/home/sc_serv/sage/local/lib/python2.7/site-packages/sage/functions/piecewise.py in __call__(self, function_pieces, **kwds)
    149                     function = function()
    150                 else:
--> 151                     function = function(var)
    152             function = SR(function)
    153             if var is None and len(function.variables()) > 0:

<ipython-input-1-d490d0695d4b> in g(x)
      1 def g(x):
      2     var('u')
----> 3     return numerical_integral(u**Integer(2),Integer(0),x)[Integer(0)]
      4
      5

/home/sc_serv/sage/src/sage/gsl/integration.pyx in sage.gsl.integration.numerical_integral (/home/sc_serv/sage/src/build/cythonized/sage/gsl/integration.c:3387)()
    329       else:
    330          _a=a
--> 331          _b=b
    332          W = <gsl_integration_workspace*> gsl_integration_workspace_alloc(n)
    333          sig_on()

/home/sc_serv/sage/src/sage/symbolic/expression.pyx in sage.symbolic.expression.Expression.__float__ (/home/sc_serv/sage/src/build/cythonized/sage/symbolic/expression.cpp:10403)()
   1384             return float(self._eval_self(float))
   1385         except TypeError:
-> 1386             raise TypeError("unable to simplify to float approximation")
   1387
   1388     def __complex__(self):

TypeError: unable to simplify to float approximation

which makes no sense since a numerical integral is already a float. Is there some other new behavior I'm missing?

Volker Braun

unread,
May 22, 2016, 4:14:13 AM5/22/16
to sage-devel
Piecewise functions are symbolic functions now; The problem is that your g doesn't define a symbolic function, so you can't use it as input to piecewise:

sage: g(x)
...
TypeError: unable to simplify to float approximation. You can manually define a symbolic function whose numeric evaluation is prescribed:

sage: def gnum(self, x, **kwds):
....:         var('u')
....:         return numerical_integral(u^2,0,x)[0] 
sage: gsym = function('gsym', nargs=1, evalf_func=gnum)
sage: gsym(x)
gsym(x)
sage: gsym(1)
gsym(1)
sage: gsym(1).n()
0.3333333333333333

Then you can use it to define a piecewise function
 
sage: piecewise([[(0,1), gsym(x)]])

We should probably automatically try that if g(x) raises an exception, though there might be further implication...

Ralf Stephan

unread,
May 22, 2016, 4:20:13 AM5/22/16
to sage-devel
sage: g(x,u) = integral(u^2,u,0,x)
sage: f=piecewise([ [(0,1), g(x,u) ] ])
sage: f
piecewise(x|-->1/3*x^3 on (0, 1); x)

paulmasson

unread,
May 22, 2016, 7:42:55 PM5/22/16
to sage-devel
Automatic creation of a symbolic function from a numeric one sounds like a good idea to me, if feasible. I'm coming to Sage from Mathematica, where one doesn't need to think about such issues. It's confusing to new users when numeric functions fail unexpectedly.

paulmasson

unread,
May 23, 2016, 9:11:55 PM5/23/16
to sage-devel
With all due deference to the work that went into making piecewise functions symbolic, it's too much overhead for purely numerical applications, as for example WKB approximations. Here's some sample code for a uniform approximation to a wavefunction in a power potential constructing symbolic functions from numeric functions, and here's the equivalent code without using piecewise and just plotting the numeric functions.

The code without symbolic functions is expected to be faster, but it really is a lot faster. Wouldn't it make sense to have an option to avoid symbolic functions in some way? Maybe define a numerical_piecewise function?

Nils Bruin

unread,
May 23, 2016, 10:48:08 PM5/23/16
to sage-devel
On Monday, May 23, 2016 at 6:11:55 PM UTC-7, paulmasson wrote:
With all due deference to the work that went into making piecewise functions symbolic, it's too much overhead for purely numerical applications, as for example WKB approximations. Here's some sample code for a uniform approximation to a wavefunction in a power potential constructing symbolic functions from numeric functions, and here's the equivalent code without using piecewise and just plotting the numeric functions.

The code without symbolic functions is expected to be faster, but it really is a lot faster. Wouldn't it make sense to have an option to avoid symbolic functions in some way? Maybe define a numerical_piecewise function

If you want to do numerical work, you should probably stick to the scipy/numpy framework. "numerical functions" there are usually callables. There's no need to involve symbolic functions at all. If you're finding a "numerical_piecewise" is useful for you, go ahead, but since you can write if/then/else constructions in a python function anyway, I don't expect it will give you much advantages to abstract that away.

Sage does provide a framework to go from symbolic and polynomial expressions (which are represented in a way such that certain mathematical properties of the functions are recognizable to the system) to black-box functions that can be used for fast evaluation at points via "fast_callable". If you have a mathematical expression (possibly symbolically derived) that you subsequently want to use for numerical work, making a callable out of it via "fast_callable" might be a good idea. It basically converts the expression into a straight-line program for evaluation.

If you're finding yourself in a scenario where you're often getting symbolic piecewise functions that you want to use numerically, then you could benefit from making "fast_callable" aware of piecewise functions and have it produce efficient code to handle them numerically. That could be an interesting project.
 
Reply all
Reply to author
Forward
0 new messages