Reputation: 6835
def f(u):
value = 0.0
if u > -1 and u < 1:
value = u * u
return value
Given the above, the following produces the expected plot:
plot(f,(x,-5,5))
But plot(f(x),(x,-5,5))
just draws a horizontal line. Can anyone explain what's going on?
Upvotes: 1
Views: 126
Reputation: 4402
Here is a (possibly) simpler solution for now, using lambdas.
sage: plot(lambda x:f(x), (x,-5,5))
Upvotes: 0
Reputation: 102166
Similar to what @Ignacio said, the cause is the function being called once. The problem with this vs other functions like sin
is the conditional. The if
statement is evaluated when the function is called and not preserved as a symbolic statement. That is, the u > -1 and u < 1
[1] is evaluated on the first function call and result
is treated accordingly (i.e. left at 0
).
As an illustration of what is happening:
sage: x = var('x')
sage: print ":)" if x > 0 else ":("
:(
There is no way to get around this in general[2], because Python has to evaluate the condition in the if
statement to work out which code path to take when the function is called.
There is a solution that should work (but doesn't yet). Sage provides Piecewise
, so you can define f
as:
f = Piecewise([((-5, -1), ConstantFunction(0)),
((-1, 1), x*x),
((1, 5), ConstantFunction(0))],
x)
Unfortunately, the implementation of Piecewise
is as yet incomplete, and severely lacking, so the only way to plot this seems to be:
f.plot()
(Limitations: trying to call f
with a variable causes errors; it doesn't work with the conventional plot
; you can't restrict the domain in Piecewise.plot
, it plots the whole thing (hence why I restricted it to ±5); it doesn't cope with infinite intervals.)
You could also just detect whether the argument to f
is a number or variable and do the appropriate action based on that:
def f(u):
try:
float(u) # see it's a number by trying to convert
return u*u if -1 < u < 1 else 0.0
except TypeError: # the conversion failed
if callable(f):
return lambda uu: f(u(uu))
else:
return f
Note the callable
call, it checks to see if u
is a function (in some sense), if so returns the composition of f
with u
.
This version allows us to do things like:
sage: f(10)
0.0
sage: f(x)(0.5)
0.25
sage: f(x+3)(-2.2)
0.64
and it also works perfectly fine with plot
, in either form. (Although it warns about DeprecationWarnings
because of the u(uu)
syntax; there are ways to get around this using u.variables
but they are fairly awkward.)
Note: This "working" solution is quite fragile, and very suboptimal; the Piecewise
version would be the correct solution, if it worked.
[1]: Python actually allows you to write this as -1 < u < 1
. Pretty cool.
[2]: Although in some special cases you can, e.g. if you know x > 0
, then you can use assume(x > 0)
which means the example will print :)
.
Upvotes: 1
Reputation: 798914
The former passes the function, allowing it to be called inside plot()
. The latter calls the function once and passes the returned value, resulting in the same value each time.
Upvotes: 1