# [Edu-sig] computer algebra

Guido van Rossum guido at python.org
Thu Dec 11 05:27:17 CET 2008

```On Wed, Dec 10, 2008 at 6:47 PM, kirby urner <kirby.urner at gmail.com> wrote:
> So I've been yakking with Ian (tizard.stanford.edu) re the new
> fractions.py, installed in Standard Library per 2.6, saw it demoed at
> a recent user group meeting (PPUG).
>
> Python's __div__ is similar to Mathematica's computer algebra notion
> of division in that you're free to divide any type by any type,
> providing this makes any algebraic sense, using a kind of liberal duck
> typing.
>
> What I mean by that is __div__ by itself doesn't pre-specify anything,
> so if there's a meaningful way to deploy the division operator between
> arguments A, B, then go ahead and do it, write you code accordingly.

Or unmeaningful! Unlike (I presume) Mathematica, Python doesn't mind
if you define a/b as multiplication. Your users might mind though. :-)

> In Java, we could write __div__ in all different ways depending on
> stricter typing at write time means you've gotta post "guards at the
> gate" in your methods).  Python, with late binding, duck typing, won't
> post guards, but you'll still need to write algorithms capable of
> sorting out the possibilities.  Maybe the user throws you a matrix?
> Has an inverse.  OK, so __div__ makes some sense...
>
> fractions.py in contrast, implements the narrow Q type, the rational
> number, defined as p / q where p, q are members of the set integers.
>
> One could imagine a Fraction class that eats two complex numbers, or
> two Decimals.  Computer algebra attaches meaning here, as in both sets
> we're able to define a multiplicative identity such that A / B means A
> * B**(-1) i.e. A * pow(B, -1) i.e. A * (1/B).

It's not so easy though. The specific purpose of the Fraction class is
to always reduce the fraction to a canonical representation using the
GCD algorithm (e.g. 15/12 becomes 5/4), which only applies to
integers.

> So the results of this operation, Fraction(A, B), might be some object
> holding the Decimal or Complex result.  In generic algebra,
> everything's a duck, although conversion between types is possible
> (yes, that sounds nonsensical).
>
> fractions.Fraction, on the other hand, barfs on anything but integers,
> isn't trying to be all divisions to all possible types, isn't
> pretending this is Mathematica or a generic CAS.

where it is sufficient if *one* of the operands knows how to deal with
the other. So Fraction(3, 4) * 2j happily returns 1.5j. You don't have
to teach Fraction about Matrix if you can teach Matrix about Fraction.

> Note that I'm not criticizing fractions.py in any way, am so far quite
> happy with it.  I'm simply drawing attention to some fine points.
>
> Related:
>
> When I went to all the trouble to compose two functions, f and g,
> using __mul__, I'd get comments like:  but the "open oh" (another
> symbol) is the "composition operator" i.e. "you're only using *
> because ASCII doesn't include the 'open oh'".
>
> However, I was making a different point:  that in group theory math
> texts, we're proud to use "regular" multiplication and division
> operators for such operations as "compositions of functions" because
> we're thinking __mul__ and __div__ have that generic meaning -- we
> neither need, nor want, the proliferation of symbols the "open oh"
> people think we must need.

think pride comes into it. Inventing and using specialized symbols is
often useful in math because it provides more context. If you write "f
* g" the reader would need to know in advance that f and g are
functions or else they wouldn't know what was meant. But if you write
"f o g" then the reader can *infer* that f and g are functions. Python
Python's predecessor ABC used the latter (type inferencing based on
operators). Neither is necessarily better than the other.

There are also fields of mathematics where both are used, with a
different meaning; e.g. f o g would mean functional composition while
f * g could mean the function you get by multiplying f(x) and g(x). In
Python:

def open_oh(f, g):
return lambda x: f(g(x))

def star(f, g):
return lambda x: f(x) * g(x)

> Note that by "open oh" I'm not talking about "big oh", a different
> notation that I don't think is redundant, agree with Knuth that if
> your calculus book doesn't include it, you're probably in one of those
> computer illiterate schools (ETS slave, whatever).

I think that comment is a little out of line. BTW big Oh is not part
of calculus, it's part of complexity theory, a totally different field
(more relevant to computers than calculus though).

--