Plotting Python functions of two variables

1,075 views
Skip to first unread message

ObsessiveMathsFreak

unread,
Apr 8, 2011, 2:03:14 PM4/8/11
to sage-support
I have a python type function taking two variables is defined in such
a say that accidental evaluation is a possibility. Here is a
simplified version

def h(x,n):
if x>2:
return n-x
else:
return n*x-2

How can functions like this be plotted over x for a constant value of
n in sage?

John H Palmieri

unread,
Apr 8, 2011, 3:00:13 PM4/8/11
to sage-s...@googlegroups.com

sage: plot(lambda x: h(x,3), (x, 0, 4))

works for me.

--
John

 

achrzesz

unread,
Apr 8, 2011, 3:14:08 PM4/8/11
to sage-support
Or:
sage: plot(lambda x: h(x,3), (x, 0, 4),exclude=[2])

ObsessiveMathsFreak

unread,
Apr 8, 2011, 5:51:03 PM4/8/11
to sage-support
That worked, thank you. But I don't understand why the standard
notation has so many problems. What exactly is going wrong?

John H Palmieri

unread,
Apr 8, 2011, 6:25:07 PM4/8/11
to sage-s...@googlegroups.com
On Friday, April 8, 2011 2:51:03 PM UTC-7, ObsessiveMathsFreak wrote:
That worked, thank you. But I don't understand why the standard
notation has so many problems. What exactly is going wrong?

I think this is what's going on: if you start with this:

> > def h(x,n):
> >        if x>2:
> >            return n-x
> >        else:
> >            return n*x-2

and then do

sage: plot(h(x, 3), (x, 0, 4))

(I assume this is what you mean by the "standard notation"), then *first* Sage tries to evaluate h(x,3).  It can't tell that "x>2" is True, so it returns n*x-2, in this case 3*x-2.  Then it plots that.  In other words, it evaluates h symbolically as best it can:

sage: h(x, 3)
3*x-2

Then it calls 'plot' on the result.

--
John

ObsessiveMathsFreak

unread,
Apr 8, 2011, 6:59:23 PM4/8/11
to sage-support
This notation isn't very flexible though. For example, suppose I
wanted to plot h(-x,n) over the same range.

Can this be done without calling the symbolic engine? Is there a way
to bypass symbolic plots altogether?

Kelvin Li

unread,
Apr 8, 2011, 7:53:03 PM4/8/11
to sage-support
> On Apr 8, 11:25 pm, John H Palmieri <jhpalmier...@gmail.com> wrote:
>
> > On Friday, April 8, 2011 2:51:03 PM UTC-7, ObsessiveMathsFreak wrote:
>
> > > That worked, thank you. But I don't understand why the standard
> > > notation has so many problems. What exactly is going wrong?
>
> > I think this is what's going on: if you start with this:
>
> > > > def h(x,n):
> > > > >        if x>2:
> > > > >            return n-x
> > > > >        else:
> > > > >            return n*x-2
>
> > and then do
>
> > sage: plot(h(x, 3), (x, 0, 4))
>
> > (I assume this is what you mean by the "standard notation"), then *first*
> > Sage tries to evaluate h(x,3).  It can't tell that "x>2" is True, so it
> > returns n*x-2, in this case 3*x-2.  Then it plots that.  In other words, it
> > evaluates h symbolically as best it can:
>
> > sage: h(x, 3)
> > 3*x-2
>
> > Then it calls 'plot' on the result.
>
> > --
> > John

On Apr 8, 3:59 pm, ObsessiveMathsFreak <obsessivemathsfr...@gmail.com>
wrote:
> This notation isn't very flexible though. For example, suppose I
> wanted to plot h(-x,n) over the same range.
>
> Can this be done without calling the symbolic engine? Is there a way
> to bypass symbolic plots altogether?

Simply use:

sage: plot(lambda x: h(-x, n), (x, -4, 0))

-- Kelvin

John H Palmieri

unread,
Apr 8, 2011, 8:11:39 PM4/8/11
to sage-s...@googlegroups.com


On Friday, April 8, 2011 3:59:23 PM UTC-7, ObsessiveMathsFreak wrote:
This notation isn't very flexible though. For example, suppose I
wanted to plot h(-x,n) over the same range.

Can this be done without calling the symbolic engine? Is there a way
to bypass symbolic plots altogether?


I'm not sure how to avoid using lambda with a piecewise-defined function like yours, although Kelvin Li gives a perfectly good answer to your question.  With more simply defined functions, you can work like this:

sage: H(x,y) = sin(x)*sin(y)
sage: plot(H.substitute(y=3), (x, 0, 10))

sage: H.substitute(y=3)
(x, y) |--> sin(3)*sin(x)

(I couldn't get h(x, y) = Piecewise(...) to work.)

--
John

Jason Grout

unread,
Apr 9, 2011, 2:24:18 AM4/9/11
to sage-s...@googlegroups.com


Another approach that supplies default arguments for the *first*
variables is to use functools.partial:

def h(x,n):
if x>2:
return n-x
else:
return n*x-2

from functools import partial
plot(partial(h,1),(n,-1,1))

This effectively plots h(1,n), where n goes from -1 to 1.

Note that we can't do

plot(partial(h,x=1),(n,-1,1))

since plot calls the function by positional arguments, rather than
keyword arguments (i.e., this last plot calls h like this:
h(-.5434344,x=1), and so we get two values for x). I think this is a
bug; I think if the variable is specified in the plot range, the
function should be called with a keyword argument, so that the function
would be called as h(n=-.5434344,x=1). I believe I even have a patch
from late last year somewhere on my laptop that changes this behavior to
call a function using keyword arguments if the variable is specified in
the plot range. Once this bug is fixed, then doing plot(partial(h,x=1),
(n,-1,1)) would work.

Thanks,

Jason

ObsessiveMathsFreak

unread,
Apr 10, 2011, 9:08:02 AM4/10/11
to sage-support
Partial seems useful, thank you. The Lambda solutions also work.

But what IS lambda anyway? I don't see that its doing anything other
than being syntactic verbose.

Pierre

unread,
Apr 10, 2011, 1:48:56 PM4/10/11
to sage-support
"lambda x : f(x)" should read "the function which maps x to f(x)". It
has nothing to do with symbolic computations, and exists in Python.

note that "lambda x : x^2" is exactly the same as "lambda y : y^2",
which is mathematically very sound. Using the symbolic ring however,
if x and y are formal variables then x^2 is not the same as y^2
(though plotting them will give the same thing!)

if you think about it, what is *really* a little weird, is that
"plot(sin(x), (-1, 1))" should work at all. It assumes you are talking
about the function which to x assigns sin(x) (and not, say, the
constant function which to anything assigns the formal expression
sin(x)).

By the way if you fix a value for x, then "plot(sin(x), ...)" gives
you a constant plot -- so the trick (of replacing sin(x) by a
function) is context-dependent. By contrast, "plot(lambda x :
sin(x), ...)" always works. Oh, and the implicit replacement works for
symbolic expressions, like sin(x), and not your h(x,n) which is
defined as a python function (a very different thing).

As you have discovered, these sorts of things become much, much
clearer when more than one variable is involved. "plot( x*y, ...)", or
"plot(h(x, n))" is just ambiguous. You want to plot the function which
to x assigns h(x, n), with n being fixed? well that is precisely
lambda x : h(x, n).

hope this helps.

Pierre

On 10 avr, 06:08, ObsessiveMathsFreak <obsessivemathsfr...@gmail.com>
wrote:

Pierre

unread,
Apr 10, 2011, 2:09:10 PM4/10/11
to sage-support
I realize that my 4th paragraph (specifically "the implicit
replacement...") is not very truthful. It may be a good first
explanation, but there's a more technical answer if you (or anyone)
cares for one.

when you try

plot( foo, ...)

then the first thing sage does is evaluate "foo" once. It has to
evaluate to a function -- a python callable object with one parameter,
to be more precise, so basically foo.__call__ must exist, and
foo( value ) should return something.

if that works, sage goes on, computing foo( v ) for a bunch of values
v, and plots that.

when x is a formal variable, then foo= sin(x) evaluates to itself, and
it IS callable, which is what I declared was mildly weird. So for
example

sin(x)(0)

works and gives you 0 -- mind you at the moment sage also warns you
that this syntax is deprecated. But when plotting, people use this
syntax all the time!

Now, with your "def h(x, n):...", then "h" evaluates to a function,
but is tqkes two parameters, not one. On the other hand h(x, 3)
evaluates to 3*x - 2 when x is a formal variable (which it is by
default). In turn 3*x - 2 is callable, by the reason i just give,
hence the plot proceeds.

finally "lambda x : h(x, 3)" evaluates precisely to the function that
you want. And "lambda y : h(y, n)" would have worked, too, even if y
is not a formal variable.
Reply all
Reply to author
Forward
0 new messages