Action verbs in sympy: user interface

16 views
Skip to first unread message

Brian Granger

unread,
Mar 29, 2010, 10:50:20 PM3/29/10
to sympy
Hi,

I have gone through sympy and made a list of the functions and methods
(of Basic) that are essential "action verbs", like "expand",
"simplify", etc. I plan on using sympy with students and for this
purpose, I need to have a super clean user interface. My students and
I will go crazy if I have to continually answer the "is * a method or
function" question. In this email I am only focusing on the user
interface (how users call things) NOT on the implementation.
Implementation is an entirely different issue.

I have divided the list into 3 categories:

1. The action verb appears as both a global function AND a method of Basic.
2. The action verb appears only as a global function.
3. The action verb appears only as a method of Basic.

* I have focused on action verbs that deal with a single expression,
so things like sum and product are not included.
* I probably have missed some of the more obscure ones.

Here is the data:

Both global function and Basic method
=====================================

evalf
series
diff
integrate
limit
expand
rewrite
conjugate

Only global function
====================

simplify
nsimplify
*simp
refine
cancel
invert
expand_*
apart
collect
separate
together
factor

Only Basic method
=================

subs
doit
n
nseries
lseries
fdiff
extract_multiplicatively

This data below suggest that the "action verbs" in sympy are not
treated in a consistent manner - in fact, to the untrained eye, it
looks like we flip a coin (OK, maybe a 3 sided coin) to determine
where we put these action verbs. I would love to help make a decision
on how to handle these action verbs in a uniform manner across sympy.
If we can reach a decision, I am willing to help implement the
decision AND document it clearly throughout the sympy sphinx docs.

Here are the options that I see:

A. Make all action verbs functions, never methods (the global
functions could still call _private implementation methods).
B. Make all action verbs methods, never functions (those methods
could still call a global but _private implementation function).
C. Make all action verbs both function and methods.

What do people think?

Cheers,

Brian

Ondrej Certik

unread,
Mar 29, 2010, 11:08:48 PM3/29/10
to sy...@googlegroups.com
Hi Brian!

On Mon, Mar 29, 2010 at 7:50 PM, Brian Granger <elliso...@gmail.com> wrote:
> Hi,
[...]
>
> What do people think?

First of all -- thanks for taking an initiative on this!

I suggest to take all functions in the cathegory 2 (Only global
function) and add them as methods of Basic too, in the manner that I
wrote in my previous email on this, e.g.:

--- a/sympy/core/basic.py
+++ b/sympy/core/basic.py
@@ -2256,6 +2256,10 @@ def _eval_nseries(self, x, x0, n):
"""
raise NotImplementedError("(%s).nseries(%s, %s, %s)" % (self, x, x0, n)

+ def simplify(self):
+ from sympy import simplify
+ return simplify(self)
+
def limit(self, x, xlim, direction='+'):
""" Compute limit x->xlim.
"""


This will not make the core more complex or less maintainable, and it
will fix this problem, that you reported, that annoys me too. So far
the opposing argument (from Ronan) is:

"
I agree that it's not too bad (I thought you meant something more
complex), but I'm still (weakly) against it since it introduces a
dependency of the core on the rest of code.
"

Is there anyone who has similar or stronger negative view on the above
change? If so, please speak up. If not, let's make the above thing
happen, let's say in a day or two.

Thanks,
Ondrej

Aaron S. Meurer

unread,
Mar 29, 2010, 11:54:15 PM3/29/10
to sy...@googlegroups.com
N() is equivalent to .evalf(). Also, Chris is refactoring the expand_* functions so that you can just do expand(expr, mul=True) instead of expand_mul(expr). I think it's one of his 1766 branches at smichr at github. I think .fdiff() not used very often by the user. .extract_multiplicatively() is mainly used for algorithms where you don't know what you are working with ahead of time (otherwise, you can just do regular division).

I'm not sure if *all* of these should be methods, but I agree that the most common ones should. I think that .subs() should be a function too, and .l/nseries should be functions, or at least keyword arguments to series().

By the way, it is easy to see what there is in isympy with ipython using the tab completion, so if your students don't see it in the namespace as a function, then it is a method, or visa-versa. Do you plan on using it mainly interactively, or through scripts?

Aaron Meurer

> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sy...@googlegroups.com.
> To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
>

Ronan Lamy

unread,
Mar 30, 2010, 12:13:46 AM3/30/10
to sy...@googlegroups.com
Note that in my branch Basic-split for issue 1798, many methods of Basic
have been moved to the new class Expr. It might not make much of a
difference to your students who will probably only use subclasses of
Expr, but it's something to keep in mind.

Ronan

Vinzent Steinberg

unread,
Mar 30, 2010, 12:29:00 PM3/30/10
to sympy
On Mar 30, 4:50 am, Brian Granger <ellisonbg....@gmail.com> wrote:
[...]

> What do people think?
>
> Cheers,
>
> Brian

The advantage of methods is that you can use them in sympy's case as
some kind of user-defined operators. Consider the following example:

(1) x + y + z

This could also be written using methods (please note how '.add' is
syntactically nearly identical to '+' minus precedence):

(2) x.add(y).add(z)

or using functions:

(3) add(x, add(y, z))

The same approach if you allow more arguments:

add(x, y, z)

So there are 3 possible syntaxes for doing the same thing. The
question is, when should/could we prefer which one?

(1) is not always possible, because Python does not allow to define
custom operators. There is however the question if we should overload
operators or rather use (2) or (3) (see for example Assume()).


Should (2) only be used when instances of Expr() (see Ronan's branch,
which should be imho merged before fixing these issues here) are
returned? This would not allow expr.ask() nor expr.evalf().

I think we should do it like Ondrej suggested: implement (2) and (3)
for any sympy function accepting instances of Expr() as argument. And
implement (3) "additionally" if it makes sense.
This would mean that we also need to implement expr.ask().

The basic operations on expressions in sympy are a special case, I
don't think we need (1) for Add(), Mul() or the non-existent Div().


Vinzent

Brian Granger

unread,
Mar 30, 2010, 2:01:09 PM3/30/10
to sy...@googlegroups.com
Ondrej,

> I suggest to take all functions in the cathegory 2 (Only global
> function) and add them as methods of Basic too, in the manner that I
> wrote in my previous email on this, e.g.:

I am +1 on this.

> --- a/sympy/core/basic.py
> +++ b/sympy/core/basic.py
> @@ -2256,6 +2256,10 @@ def _eval_nseries(self, x, x0, n):
>        """
>        raise NotImplementedError("(%s).nseries(%s, %s, %s)" % (self, x, x0, n)
>
> +    def simplify(self):
> +        from sympy import simplify
> +        return simplify(self)
> +
>    def limit(self, x, xlim, direction='+'):
>        """ Compute limit x->xlim.
>        """
>
>
> This will not make the core more complex or less maintainable, and it
> will fix this problem, that you reported, that annoys me too. So far
> the opposing argument (from Ronan) is:
>
> "
> I agree that it's not too bad (I thought you meant something more
> complex), but I'm still (weakly) against it since it introduces a
> dependency of the core on the rest of code.
> "
>
> Is there anyone who has similar or stronger negative view on the above
> change? If so, please speak up.  If not, let's make the above thing
> happen, let's say in a day or two.

I think this would be an improvement.

What about the action verbs that are methods only? Should be also
make them global functions?

Brian

Brian Granger

unread,
Mar 30, 2010, 2:04:28 PM3/30/10
to sy...@googlegroups.com
> N() is equivalent to .evalf().

I propose renaming .evalf() -> .N().

> Also, Chris is refactoring the expand_* functions so that you can just do expand(expr, mul=True) instead of expand_mul(expr).

Great! That takes care of the expand_foo methods (let's remove them).

I think it's one of his 1766 branches at smichr at github.  I think
.fdiff() not used very often by the user.  .extract_multiplicatively()
is mainly used for algorithms where you don't know what you are
working with ahead of time (otherwise, you can just do regular
division).

OK, so maybe these two are not important enough to have as global functions.

> I'm not sure if *all* of these should be methods, but I agree that the most common ones should.  I think that .subs() should be a function too, and .l/nseries should be functions, or at least keyword arguments to series().

What about doit?

> By the way, it is easy to see what there is in isympy with ipython using the tab completion, so if your students don't see it in the namespace as a function, then it is a method, or visa-versa.  Do you plan on using it mainly interactively, or through scripts?

IPython, what's that? ;-) Yes, students would have tab completion at
their finger tips, but doing sympy.[TAB] pull up way too many results
to be really useful to a truly new user.


Cheers,

Brian

Tim Lahey

unread,
Mar 30, 2010, 2:11:44 PM3/30/10
to sy...@googlegroups.com
On Tue, Mar 30, 2010 at 2:04 PM, Brian Granger <elliso...@gmail.com> wrote:
> I propose renaming .evalf() -> .N().

-1. First, it would break existing code. Second, I see the point in
having both. First,
evalf is what Maple calls it while N is what Mathematica calls it. So,
it's handy for
people switching.

Since I'm a Maple user, I'd argue for evalf replacing N if that was
the case. However,
I think eliminating one is a bad idea.

Cheers,

Tim.

---
Tim Lahey
PhD Candidate, Systems Design Engineering
University of Waterloo
http://www.linkedin.com/in/timlahey

Brian Granger

unread,
Mar 30, 2010, 2:26:14 PM3/30/10
to sy...@googlegroups.com
Ronan,

Yes, for my students it doesn't matter, but for us devs it does. I
just left a comment on your branch. I do agree that your branch
should go in before adding any new methods to Basic.

Brian

Ondrej Certik

unread,
Mar 30, 2010, 2:27:34 PM3/30/10
to sy...@googlegroups.com
On Tue, Mar 30, 2010 at 11:11 AM, Tim Lahey <tim....@gmail.com> wrote:
> On Tue, Mar 30, 2010 at 2:04 PM, Brian Granger <elliso...@gmail.com> wrote:
>> I propose renaming .evalf() -> .N().
>
> -1. First, it would break existing code. Second, I see the point in
> having both. First,
> evalf is what Maple calls it while N is what Mathematica calls it. So,
> it's handy for
> people switching.
>
> Since I'm a Maple user, I'd argue for evalf replacing N if that was
> the case. However,
> I think eliminating one is a bad idea.

SymPy currently has both .evalf() and .n() methods. We also have a N()
global function. It's kind of a mess, I agree.

Ondrej

Brian Granger

unread,
Mar 30, 2010, 2:30:37 PM3/30/10
to sy...@googlegroups.com

Coming from Mathematica, evalf is confusing for me. But, if maple
users are familiar with evalf I think it makes sense to keep it. But
then, let simply also make .N a method so it is consistent.

Cheers,

Brian

> Ondrej

Ondrej Certik

unread,
Mar 30, 2010, 2:32:32 PM3/30/10
to sy...@googlegroups.com
On Tue, Mar 30, 2010 at 11:26 AM, Brian Granger <elliso...@gmail.com> wrote:
> Ronan,
>
> Yes, for my students it doesn't matter, but for us devs it does.  I
> just left a comment on your branch.  I do agree that your branch
> should go in before adding any new methods to Basic.

For the record, it's this issue:

http://code.google.com/p/sympy/issues/detail?id=1798

and this branch:

http://github.com/rlamy/sympy/tree/split_Basic

I apologize for not looking at this in details so far. I also didn't
like the idea initially, but it seems it's a good idea and if
everybody agrees to have this in, let's do that. I am busy today, but
will have some time in the evening to review it properly. If it
simplifies the core, then let's do that.

Ondrej

Ondrej Certik

unread,
Mar 30, 2010, 2:35:26 PM3/30/10
to sy...@googlegroups.com
On Tue, Mar 30, 2010 at 11:30 AM, Brian Granger <elliso...@gmail.com> wrote:
> On Tue, Mar 30, 2010 at 11:27 AM, Ondrej Certik <ond...@certik.cz> wrote:
>> On Tue, Mar 30, 2010 at 11:11 AM, Tim Lahey <tim....@gmail.com> wrote:
>>> On Tue, Mar 30, 2010 at 2:04 PM, Brian Granger <elliso...@gmail.com> wrote:
>>>> I propose renaming .evalf() -> .N().
>>>
>>> -1. First, it would break existing code. Second, I see the point in
>>> having both. First,
>>> evalf is what Maple calls it while N is what Mathematica calls it. So,
>>> it's handy for
>>> people switching.
>>>
>>> Since I'm a Maple user, I'd argue for evalf replacing N if that was
>>> the case. However,
>>> I think eliminating one is a bad idea.
>>
>> SymPy currently has both .evalf() and .n() methods. We also have a N()
>> global function. It's kind of a mess, I agree.
>
> Coming from Mathematica, evalf is confusing for me.  But, if maple
> users are familiar with evalf I think it makes sense to keep it.  But
> then, let simply also make .N a method so it is consistent.

Sage also only has the .n() method and a N() function.

So should we have all .evalf(), .n() and .N() methods? If someone
expects to have .N() too, we can do that (Sage doesn't have it and no
one seems to complain though).

Ondrej

Robert Kern

unread,
Mar 30, 2010, 2:39:58 PM3/30/10
to sy...@googlegroups.com

FWIW, I think this kind of consistency is overrated. What looks nice
as a method doesn't always look nice as a function. Users coming from
other languages expect all kinds of unreasonable things.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless
enigma that is made terrible by our own mad attempt to interpret it as
though it had an underlying truth."
-- Umberto Eco

Brian Granger

unread,
Mar 30, 2010, 3:01:10 PM3/30/10
to sy...@googlegroups.com
Robert,

>> So should we have all .evalf(), .n() and .N() methods? If someone
>> expects to have .N() too, we can do that (Sage doesn't have it and no
>> one seems to complain though).
>
> FWIW, I think this kind of consistency is overrated. What looks nice
> as a method doesn't always look nice as a function. Users coming from
> other languages expect all kinds of unreasonable things.

For folks like you or I I somewhat agree. As long as I can tab
complete I can adapt and adjust. But for my students whose only
computing experience is their iPhone and Internet explorer, this type
of thing does matter.

Also, while it may be overrated, there is no *harm* in making it more
consistent.

Cheers,

Brian

> --
> Robert Kern
>
> "I have come to believe that the whole world is an enigma, a harmless
> enigma that is made terrible by our own mad attempt to interpret it as
> though it had an underlying truth."
>  -- Umberto Eco
>

Ondrej Certik

unread,
Mar 30, 2010, 3:07:21 PM3/30/10
to sy...@googlegroups.com
On Tue, Mar 30, 2010 at 12:01 PM, Brian Granger <elliso...@gmail.com> wrote:
> Robert,
>
>>> So should we have all .evalf(), .n() and .N() methods? If someone
>>> expects to have .N() too, we can do that (Sage doesn't have it and no
>>> one seems to complain though).
>>
>> FWIW, I think this kind of consistency is overrated. What looks nice
>> as a method doesn't always look nice as a function. Users coming from
>> other languages expect all kinds of unreasonable things.
>
> For folks like you or I I somewhat agree.  As long as I can tab
> complete I can adapt and adjust.  But for my students whose only
> computing experience is their iPhone and Internet explorer, this type
> of thing does matter.
>
> Also, while it may be overrated, there is no *harm* in making it more
> consistent.

When adding these duplicate methods, we should also copy the docstring
automatically, so that it is well documented at one place and the
other methods just copy it somehow.

Ondrej

Aaron S. Meurer

unread,
Mar 30, 2010, 3:19:08 PM3/30/10
to sy...@googlegroups.com

On Mar 30, 2010, at 12:04 PM, Brian Granger wrote:

>> N() is equivalent to .evalf().
>
> I propose renaming .evalf() -> .N().
>
>> Also, Chris is refactoring the expand_* functions so that you can just do expand(expr, mul=True) instead of expand_mul(expr).
>
> Great! That takes care of the expand_foo methods (let's remove them).
>
> I think it's one of his 1766 branches at smichr at github. I think
> .fdiff() not used very often by the user. .extract_multiplicatively()
> is mainly used for algorithms where you don't know what you are
> working with ahead of time (otherwise, you can just do regular
> division).
>
> OK, so maybe these two are not important enough to have as global functions.
>
>> I'm not sure if *all* of these should be methods, but I agree that the most common ones should. I think that .subs() should be a function too, and .l/nseries should be functions, or at least keyword arguments to series().
>
> What about doit?
>
>> By the way, it is easy to see what there is in isympy with ipython using the tab completion, so if your students don't see it in the namespace as a function, then it is a method, or visa-versa. Do you plan on using it mainly interactively, or through scripts?
>
> IPython, what's that? ;-) Yes, students would have tab completion at
> their finger tips, but doing sympy.[TAB] pull up way too many results
> to be really useful to a truly new user.
>
>

So maybe your students would benefit from some kind of cheat sheet, as suggested in issue 1817.

Aaron Meurer

Robert Kern

unread,
Mar 30, 2010, 3:26:39 PM3/30/10
to sy...@googlegroups.com

I would argue that duplicating methods is the worst option for the
newbies and is more harmful than leaving things alone. Create the
consistent function/method if you must, but deprecate the inconsistent
one. Of course, that has costs as well.

Toon Verstraelen

unread,
Mar 31, 2010, 2:21:03 AM3/31/10
to sy...@googlegroups.com
Ondrej Certik wrote:
>
> When adding these duplicate methods, we should also copy the docstring
> automatically, so that it is well documented at one place and the
> other methods just copy it somehow.
>

That should be easy:

class Foo(object):
def some_method(self, x):
"""doc"""
pass

other_method = some_method

help(Foo) then shows that other_method is equal to some_method and one
can lookup the documentation of some_method. Alternatively:

class Foo(object):
def some_method(self, x):
"""doc"""
pass

def other_method(self, x):
self.some_method(x)
other_method.__doc__ = some_method.__doc__

help(Foo) now shows two methods with the same docstring. (Not sure if
that is better.)

cheers,

Toon

smichr

unread,
Mar 31, 2010, 8:41:49 AM3/31/10
to sympy
One thing nice about the methods is that the user doesn't have to
worry about importing them.

Ondrej Certik

unread,
Apr 6, 2010, 7:39:15 PM4/6/10
to sy...@googlegroups.com
Hi,

since there were no more comments, let's do the following:

* take the global functions:

simplify
nsimplify
*simp
refine
cancel
invert
expand_*
apart
collect
separate
together
factor

and add them as simple (3 lines) methods of Basic, together with
automatically transferring the docs.

* leave .n(), N(), .evalf() as it is for now


Brian, would that be acceptable? If so, let me know if you need any
help with implementing this.

Ondrej

Ondrej Certik

unread,
Apr 6, 2010, 7:54:00 PM4/6/10
to sy...@googlegroups.com

I have played with this split_Basic branch and all tests pass. It will
have to be rebased on top of our latest master, but that shouldn't be
a big problem.

Ronan --- I saw that your split_Basic_pc2 branch seems a bit more
polished, so I also tested that, all tests pass. Which one should we
use?

So in general I am ok with this. But I still don't understand exactly
what should be in Expr and what in Basic.

I see that all arithmetic methods are in Expr, as well as series() and
related methods. subs(), match() are still in Basic. This should be
explained in the docstring of Expr, how to decide what should go into
Expr and what into Basic.

After this is clarified, let's get it in, I can help with that. Brian
can then implement the rest of the methods in Expr.

Ondrej

Ondrej Certik

unread,
Apr 6, 2010, 8:00:36 PM4/6/10
to sy...@googlegroups.com

Ah, got it, the branch is actually Basic-split-rebased-2 and it is +1
from me, as well as Vinzent, so I have pushed this in.

Nevertheless, a way better docstring for the Expr class, together with
motivation is still missing, can you please fix that?

Ondrej

Brian Granger

unread,
Apr 7, 2010, 2:21:00 AM4/7/10
to sy...@googlegroups.com
Ondrej,

This week is insane for me as we just began classes. I will look at
this next week - once the Basic split branch has been merged I can
implement this.

Cheers,

Brian

> Ondrej

Brian Granger

unread,
Apr 7, 2010, 2:26:43 AM4/7/10
to sy...@googlegroups.com
Ok, I see that the Basic/Expr split has been merged, so I will work on
this stuff next week.

Cheers,

Brian

Vinzent Steinberg

unread,
Apr 19, 2010, 2:09:59 PM4/19/10
to sympy
On Apr 7, 8:26 am, Brian Granger <ellisonbg....@gmail.com> wrote:
> Ok, I see that the Basic/Expr split has been merged, so I will work on
> this stuff next week.

A patch has been sent to the list and is currently under review.

Vinzent
Reply all
Reply to author
Forward
0 new messages