Question on Interfaces

26 views
Skip to first unread message

Gilbert Gede

unread,
Aug 11, 2011, 3:14:03 PM8/11/11
to mhub...@ucdavis.edu, sy...@googlegroups.com, fo...@ucdavis.edu, sports-bio...@googlegroups.com, espe...@gmail.com, esse...@gmail.com
My Summer of Code project is writing a submodule of SymPy for creation of symbolic equations of motion for multibody systems. As part of this, I've implemented classes to represent vectors and dyadics. I have currently implemented three ways to do mathematical operations on vectors: an operator, a function, and a method interface. 
I think three interfaces for these operations is too many, and am looking for some input on which ones people prefer. The implemented interfaces look like:

Cross product:
>>> (vec1 ^ vec2)
>>> cross(vec1, vec2)
>>> vec1.cross(vec2)
Dot product:
>>> (vec1 & vec2)
>>> dot(vec1, vec2)
>>> vec1.dot(vec2)
Outer product:
>>> (vec1 | vec2)
>>> outer(vec1, vec2)
>>> vec1.outer(vec2)

Here is one example where vector and dyadic quantities are used in the code. I is a dyadic, and omega and alpha are vectors.

>>> -(I & alpha) - (omega ^ (I & omega))
>>> -dot(I, alpha) - cross(omega, dot(I, omega))
>>> -I.dot(alpha) - omega.cross(I.dot(omega))


I'd appreciate people's opinions on:
1) Which is clearest?
2) Which would be easiest to teach?
3) Which is least error prone?

I personally prefer the operator interface (&, ^, |), as I think once you learn what the three operators represent, it is clearer to read and write. 

I would especially value the input of anyone who would be interested in using this as part of teaching students.
I've also made a poll here: http://www.surveymonkey.com/s/CK8HDMD

Thanks, 
Gilbert

Matthew Rocklin

unread,
Aug 11, 2011, 3:31:39 PM8/11/11
to sy...@googlegroups.com
I personally like the functions, dot(a,b), etc... and would be sad to see them left out. If you had to kick one I would get rid of the object oriented syntax a.dot(b). This is just a personal preference though. 

It seems to me though that Python is all about having several overlapping syntaxes (for better or for worse). Even if I used dot(a,b) exclusively I don't think that a.dot(b) would ever get in my way. I don't think it's a terrible idea to keep them all around. 

While I'm stating personal preferences how about ** for dot? To me this says "I'm multiplication, but different". 

Also, in some areas of mathematics the wedge operator ∧ is used to represent the outer product. Your use of ^ might be mistaken for this. 

--
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.

Luke

unread,
Aug 11, 2011, 3:42:18 PM8/11/11
to sy...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, sports-bio...@googlegroups.com, espe...@gmail.com, esse...@gmail.com
I ran this same question by my girlfriend who teaches undergraduate
physics classes. She isn't an experienced programmer, so concepts of
operator overloading and object oriented vs functional programming
styles are not on here mind, but she has taught a lot of the core
required physics classes to both majors and non-majors at UC Davis, so
she has a lot of experience interacting with undergraduates on this
kind of thing. Surprisingly, she thought the operator interface
seemed the clearest because once you learn the meanings of the &, ^,
|, it is about as close to what you would write on a black board or on
paper as you could hope for.

That said, there are issues with &, ^, and |, namely it is unlikely to
know what they mean unless you read the docstrings of Vector or read
the Sphinx documentation that Gilbert has written. Additionally, as
Matthew brings up using '^' for the cross product may visually
conflict with the wedge operator. Then again, it doesn't seem to hurt
to have overlapping syntax's, as long as they are both well
documented.

I vote for keeping the operators and the functions, and if any are
removed, to remove the methods.

If people have thoughts on any negative implications of implementing
multiple ways to perform the same thing, especially related to long
term maintenance of the code, it would be great to hear them.

~Luke

> --
> 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.
>

--
"Those who would give up essential liberty to purchase a little
temporary safety deserve neither liberty nor safety."

-- Benjamin Franklin, Historical Review of Pennsylvania, 1759

Jason Moore

unread,
Aug 11, 2011, 4:15:28 PM8/11/11
to sports-bio...@googlegroups.com, sy...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, espe...@gmail.com, esse...@gmail.com
I'm for the functional names as it is how all the programming languages that I know of implement dot products and cross products. It is clear what they mean and the symbols we use in math for the dot product and cross product (a dot and a cross) do not exist explicitly on the key board. Secondly, those symbols you use are already resevered for python operations, what happens when I have a script that requires both the dot product of a vector and the bitwise and comparison?

I think operator assignment belongs in language design. You all have chosen python as your language of choice for the software, so your are stuck with syntax and style from the python language. Everyone that uses your software has to learn python to some degree before they can use it anyways. Autolev for example is it's own mini langague so they have the power to assign whatever keyboard keys to various opeartions, like the '>' for vectors and other operations. Python has standards and overloading symbols is funky. Another example I know of is the ggplot library in R. They overload the '+' symbol is a very non-R way. When you read R code with ggplot stuff embedded in it you have to think in a totally different way than the rest of the code to understand what is going on.

Keep in mind that people's scripts can have many modules imported from tons of python librarys, including your library (sympy.physics.mechanics). If you rewrite the language definition, then it doesn't necessarily fit with the rest of the code.

Jason


--
You received this message because you are subscribed to the Google Groups "Sports Bio Mechanics" group.
To post to this group, send email to sports-bio...@googlegroups.com.
To unsubscribe from this group, send email to sports-bio-mecha...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sports-bio-mechanics?hl=en.




--
http://mae.ucdavis.edu/~biosport/jkm/
Sports Biomechanics Lab, UC Davis
Davis Bike Collective Minister, Davis, CA
BikeDavis.info
Office: +01 530-752-2163
Lab: +01 530-752-2235
Home: +01 530-753-0794

Jason Moore

unread,
Aug 11, 2011, 4:19:23 PM8/11/11
to sports-bio...@googlegroups.com, sy...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, espe...@gmail.com, esse...@gmail.com
FYI: there is a lot of stuff on the subject we've already typed here: https://github.com/sympy/sympy/pull/450#issuecomment-1782199

For the sake of retyping.

Luke

unread,
Aug 11, 2011, 4:41:11 PM8/11/11
to sports-bio...@googlegroups.com, sy...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, espe...@gmail.com, esse...@gmail.com
One comment below.

On Thu, Aug 11, 2011 at 1:15 PM, Jason Moore <moore...@gmail.com> wrote:

> I'm for the functional names as it is how all the programming languages that
> I know of implement dot products and cross products. It is clear what they
> mean and the symbols we use in math for the dot product and cross product (a
> dot and a cross) do not exist explicitly on the key board. Secondly, those
> symbols you use are already resevered for python operations, what happens
> when I have a script that requires both the dot product of a vector and the
> bitwise and comparison?

He is only overloading these operators for his classes, so it would
have no effect on the same operators used with other classes. So, if
you wanted to do bitwise manipulations on ints (or any other classes
which support &/^/|) and vector products using the &/^/| operators in
the same script, you could, and the code would work properly, although
a reader would have to be more aware of the context in which the & is
being used. Doing a &/^/| between incompatible types (i.e., int and
Vector) would raise a TypeError exception.

However, one could argue that it is safer to use operators because
they are tied to the classes, whereas with the functions, if you
import them into the global namespace you could unintentionally
overwrite any other dot/cross/outer functions that may have existed.
Obviously this is avoidable with the import syntax Python offers, but
if you were careless, it could happen.

The binary operators he is overloading are really no different from
any other class method, except for the rules of precedence defined by
the Python language.

Obviously there are pros/cons of both ways, I think the real question
is whether to support both interfaces, or to be more restrictive and
only support one. This question of whether it is better to only
support one way of doing things versus a couple ways of doing things
has also come up in regards to how basis vectors of a Reference Frame
are accessed, and it seems like having two ways to do things might not
be too much of a hassle to support, with the added benefit that with
the extra functionality, the Python code can look very similar to how
it is printed/prettyprinted/latex'd, which I think is a very desirable
quality.

Matthew Rocklin

unread,
Aug 11, 2011, 4:45:22 PM8/11/11
to sy...@googlegroups.com, sports-bio...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, espe...@gmail.com, esse...@gmail.com
Thanks for the link Jason, there is some good discussion there which it would be optimal to not have to repeat. 

My opinion after reading through that is as follows. 

I like operators. If the language supports it and you can find a good set they really do make writing code feel more like writing math. For a package like SymPy that tries to attract scientific and mathematical users I think that this is very important. If a scientific user finds that their code just looks like math they get hooked pretty quickly. This is good and I think we should promote this.

I also think it's important that these operators act exactly as they should without reading documentation. We must assume that these users will not read documentation. Given this, are we setting up stumbling blocks for them to trip on? I brought up the ^ issue, Aaron brought up the operator precedence issue in the other discussion (the implicit parentheses in "a + b & c" don't lie where you think they do). Given these issues I probably would fall back on using just functions rather than the operators suggested. However, maybe there are some other operators that do work well for some (if not all) of the operations. 

Luke, are you already using *? **? These probably have precedence closer to what you'd like. Is // useful for anything?

If it was me I would write all internal code using functions, not operators. Then you could add in operators separately and pull them out easily if they caused trouble. Again, I like operators, I think they're important. I hope that there is a good way to make them work here. 

Jason Moore

unread,
Aug 11, 2011, 5:22:08 PM8/11/11
to sy...@googlegroups.com, sports-bio...@googlegroups.com
Couple more thoughts on operator overloading.

Let's assume the '+' symbol means 'to add' in the language of choice. It is very clear that what adding two numbers should do, but what about other objects:

object = matrix
'+' should add the matrix. we could write a function called 'add_matrix', but it is pretty clear that overloading the '+' is absolutely equivalent. There aren't other ways to add matrices (although you could invent them, like add rows to columns and columns to rows, or something)

same for vectors
'+' should add vectors, same as matrices, it adds each entry and there is probably no reason to write a function 'add_vector', just overload __add__

what about:
object = cat
does 'adding one cat to another' mean to squish them together or tie them by the legs, or stack them on top of each other, or combine their names? You can pick one these as the default add operation for __add__ but it is probably more meaningful to have functions and/or methods that tell the use what they are doing: cat.combine_names(othercat).

In the R ggplot example, the '+' means 'to draw another element in the graph' or 'add another element in the graph'. So you can "add" lots of plot objects to your figure by using the '+'. I find that it just offers confusion rather that being helpful, but once you get it your code is shorted and easier to read for you. It just doesn't match the rest of the code in your script. I'm of the position that overloading the mathematical operators should be for things that have a mathematical semblance to the operator.

So as an analogy, the symbol '&' means 'bitwise and comparison' in python.

So what does it mean to 'bitwise and compare' a vector?

It surely doesn't mean to take the dot product (inner product) of a vector. The '.' or '*' seem like more appropriate symbols for a dot product of a vector. In documents we typically use a dot to represent the dot product (or angle braces for inner products) and it isn't confused with a dot for multiplication if we know we are working with vectors. The outer product doesn't really have a symbol on the keyboard. It often uses an x with a circle around it. Same with cross...the 'x' would be the best choice, but our language choice probably doesn't allow that.

So, as Luke pointed out the & symbol would work with different classes in different ways. For vectors it would mean dot product, but for other things that can be bitwise compared, they'd overload to that. I guess that is fine, even thought initially confusing.

---------------------
All in all, I think all three methods should be supported if there are people who want to use them. This way no one is forced to type things a certain way. This reflects the numpy and scipy setup. You can use functions, methods and they even overload mathematical operators when appropriate. I'm sure they do this to make it comfortable for users of other programming languages to adopt the software without having to learn a new paradigm.

The only reason not to support all methods, would be if there is a reason for maintainability sake. Otherwise, more options are better, because more folks will be comfortable with using the program.
------------------------------

Jim Jewett

unread,
Aug 11, 2011, 5:31:32 PM8/11/11
to sy...@googlegroups.com
On Thu, Aug 11, 2011 at 5:22 PM, Jason Moore <moore...@gmail.com> wrote:
> Couple more thoughts on operator overloading.
>... The outer product

> doesn't really have a symbol on the keyboard. It often uses an x with a
> circle around it. Same with cross...the 'x' would be the best choice, but
> our language choice probably doesn't allow that.

Have you looked at naming functions with unicode characters? I don't
remember how restrictive the rules ended up being, but you might well
be able to use mathematical characters in function names. For most
people, that interface would require cut-and-paste, but if one of the
APIs just forwards to another, that shouldn't be a severe problem.

> The only reason not to support all methods, would be if there is a reason
> for maintainability sake. Otherwise, more options are better, because more
> folks will be comfortable with using the program.

It *may* matter depending on how important speed is. Given:

def add3(x):
return x+3

add3(x)

is still slower than

x+3

because of function call overhead.

-jJ

Matthew Rocklin

unread,
Aug 11, 2011, 5:48:06 PM8/11/11
to sy...@googlegroups.com, sports-bio...@googlegroups.com
I agree with Jason that coercing operators to perform unfamiliar tasks is probably a poor choice. I disagree however that operators have only a single meaning or that they should be avoided when their meaning might be ambiguous. 

Example of the first (operators have only a single meaning)
& also means set intersection in Python

Example of the second (using operators even when meaning is ambiguous)
In numpy * could mean elementwise multiplication or matrix multiplication. I don't think anyone is suggesting that we remove the * operator and just use elementwise_mult(A, B) for arrays. If this happened I would personally switch back to Matlab... *shudder*.

My personal opinion (and I hold zero sway here) is that the operators that Luke has proposed probably shouldn't be used as they are but that we should remain open to this idea. I'm hopeful that at least some of the operations he wants have nicely intuitive operator representations. 


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sympy/-/bH5GNLGSM7oJ.

Gilbert gede

unread,
Aug 11, 2011, 6:44:50 PM8/11/11
to sympy, sports-bio...@googlegroups.com
I chose the & to represent dot product specifically because of its
meaning of and/interection in Python; that is how I view the dot
product of two vectors, as the common component between two vectors. I
decided to only use * for scalar multiplication, to further reinforce
that operations between vectors are different, and use their own
symbols.

I do admit that there is less justification for the choice of ^ for
the cross product and | for the outer product.
Also, I've set things up to throw errors if non-permitted operations
happen; this is also part of the reason I chose not to extend SymPy's
Expr or Basic.

Do you have any suggestions for alternate operators to represent dot,
cross, and outer products besides &, ^, and |?

-Gilbert

Chris Smith

unread,
Aug 11, 2011, 6:50:39 PM8/11/11
to sy...@googlegroups.com
On Fri, Aug 12, 2011 at 4:29 AM, Gilbert gede <gilbe...@gmail.com> wrote:
> I chose the & to represent dot product specifically because of its
> meaning of and/interection in Python; that is how I view the dot
> product of two vectors, as the common component between two vectors. I
> decided to only use * for scalar multiplication, to further reinforce
> that operations between vectors are different, and use their own
> symbols.
>
> I do admit that there is less justification for the choice of ^ for
> the cross product and | for the outer product.

The mnemonic I would use is that the ^ looks like the bottom of an x
(for cross product) and the | is an outer key on the keyboard, also
indicative of the side of a matrix (which is the result of an outer
product). The & for "how much do this AND that have in common" works
for me.

Aaron Meurer

unread,
Aug 12, 2011, 2:30:04 AM8/12/11
to sy...@googlegroups.com
On Thu, Aug 11, 2011 at 3:31 PM, Jim Jewett <jimjj...@gmail.com> wrote:
> On Thu, Aug 11, 2011 at 5:22 PM, Jason Moore <moore...@gmail.com> wrote:
>> Couple more thoughts on operator overloading.
>>... The outer product
>> doesn't really have a symbol on the keyboard. It often uses an x with a
>> circle around it. Same with cross...the 'x' would be the best choice, but
>> our language choice probably doesn't allow that.
>
> Have you looked at naming functions with unicode characters?  I don't
> remember how restrictive the rules ended up being, but you might well
> be able to use mathematical characters in function names.  For most
> people, that interface would require cut-and-paste, but if one of the
> APIs just forwards to another, that shouldn't be a severe problem.

Unicode variable names are only supported in Python 3, and anyway,
this is a very bad idea (unless you can remember how to type • or × on
your keyboard).


>
>> The only reason not to support all methods, would be if there is a reason
>> for maintainability sake. Otherwise, more options are better, because more
>> folks will be comfortable with using the program.
>
> It *may* matter depending on how important speed is.  Given:
>
>    def add3(x):
>        return x+3
>
>    add3(x)
>
> is still slower than
>
>    x+3
>
> because of function call overhead.
>
> -jJ
>

I seriously doubt that this would be an issue. Also, it depends on
the implementation which one actually has more function calls. x + 3
calls x.__add__(3), which may then pass the logic onto something else.

But this should basically be of zero consideration.

Aaron Meurer

Aaron Meurer

unread,
Aug 12, 2011, 2:40:36 AM8/12/11
to sy...@googlegroups.com, sports-bio...@googlegroups.com, mhub...@ucdavis.edu, fo...@ucdavis.edu, espe...@gmail.com, esse...@gmail.com
Thanks for bringing up my argument. I would recommend against using
&, ^, and | because they have operator precedence much different than
what you would expect. Luke, did you tell your girlfriend that you
have to type x + (y & z) or else it will be interpreted as (x + y) &
z? I think she may change her mind on how clear this is with this
fact in mind, especially to unsuspecting new users.

The idea of using ** and // is perhaps a good one. Here's the Python
operator precedence table for reference
http://docs.python.org/reference/expressions.html#summary. There's
also % in there at the same level as *. Indeed, do you even use /
(single slash)?

Another idea that I briefly mentioned on the pull request is to use
__call__, __index__, and/or __getattr__. The following are all valid
syntactic sugars:

v1.v2
v1[v2]
v1(v2)

Unlike &, ^, and |, these have operator precedence that you would
expect. The first one will not work in a nested manner,
unfortunately. It also requires you to have a very consistant
variable naming scheme, or else some kind of namespace hacks. It
does look just like v1 dot v2, though.

Finally, I suggested using shorter method names, for less typing. So
instead of v1.dot(v2) or v1.cross(v2), you could have just v1.d(v2) or
v1.c(v2). This would probably be a bad idea to have functions like
this, as they would easily be clobbered, but for methods, it would
work fine.

And by the way, I don't see any reason why you can't have more than
one interface. We allow, e.g., both f(x).diff(x) and diff(f(x), x)
syntaxes in SymPy, and I don't think it leads to any confusion.

Aaron Meurer

Vinzent Steinberg

unread,
Aug 12, 2011, 3:05:41 PM8/12/11
to sympy
On 11 Aug., 21:31, Matthew Rocklin <mrock...@gmail.com> wrote:
> I personally like the functions, dot(a,b), etc... and would be sad to see
> them left out. If you had to kick one I would get rid of the object oriented
> syntax a.dot(b). This is just a personal preference though.

I like about dot(a, b) that it is the general, clean syntax. a.dot(b)
is nice for nested functions, so you can write a.dot(b).dot(c) instead
of dot(dot(a, b), c). (In this case it does not apply for the dot
product of course, but in general it might occur.)

Vinzent

Jim Jewett

unread,
Aug 15, 2011, 11:29:57 AM8/15/11
to sy...@googlegroups.com

An alternative is to write things like that as a function to take an
arbitrary number of elements, so that you could write:

intersection(a, b)
or
intersection(a,b,c,d,e)

That way people don't have to mentally verify that there isn't a
sneaky place in the middle where another function sneaks in, or
parentheses suggest a different order.

-jJ

Aaron Meurer

unread,
Aug 15, 2011, 1:27:34 PM8/15/11
to sy...@googlegroups.com
Well, an example where repeated operations would make sense is the
cross product. But I would recommend against making cross(a, b, c,
...). The reason is that the cross product is non-associative, so it's
not clear if cross(a, b, c) means cross(cross(a, b), c) or cross(a,
cross(b, c)).

I don't really know much about the outer product.

Aaron Meurer

Reply all
Reply to author
Forward
0 new messages