[Python-ideas] [RFC] draft PEP: Dedicated infix operators for matrix multiplication and matrix power
Nathaniel Smith
njs at pobox.com
Fri Mar 14 17:41:36 CET 2014
On Fri, Mar 14, 2014 at 2:53 PM, Paul Moore <p.f.moore at gmail.com> wrote:
> One genuine question though - when the PEP was developed, were
> multi-character operators like .* or <*> considered? A "rejected
> alternative operator symbols" would be a useful addition to the PEP
> (although it'd rob all us non-experts of the opportunity to bikeshed
> :-))
>
> On a related note, the @@ operator is visually dreadful (far too
> heavy). While I see the */** analogy, and I appreciate that there's
> few good options, I'd definitely like to see some evidence that it's
> "the best of a bad lot" in the PEP.
It's worth noting that @@ will probably see marginal use -- matrix
power and matrix inversion are not common operations in number
crunching code. Matrix inversion is very common in math formulas, but
on a computer you almost always want to use a fused invert+multiply
operation instead of inversion itself. My original draft didn't even
allow '@@ -1'; I added it at the request of the symbolic math guys. In
practice the main use case for @@ will probably be as a crutch for
beginners before they learn about better tools like numpy.linalg.solve
(which implements fused invert+multiply).
Anyway, I wrote some more text for the PEP, see what you think:
Rationale for specification details
===================================
Choice of operator
------------------
Why ``@`` instead of some other spelling? There isn't any consensus
across other programming languages about how this operator should be
named [#matmul-other-langs]_; here we discuss the various options.
Restricting ourselves only to symbols present on US English keyboards,
the punctuation characters that don't already have a meaning in Python
expression context are: ``@``, backtick, ``$``, ``!``, and ``?``. Of
these options, ``@`` is clearly the best; ``!`` and ``?`` are already
heavily freighted with inapplicable meanings in the programming
context, backtick has been banned from Python by BDFL pronouncement
(see PEP 3099), and ``$`` is uglier, even more dissimilar to ``*`` and
:math:`\cdot`, and has Perl/PHP baggage. ``$`` is probably the
second-best option of these, though.
Symbols which are not present on US English keyboards start at a
significant disadvantage (having to spend 5 minutes at the beginning
of every numeric Python tutorial just going over keyboard layouts is
not a hassle anyone really wants). Plus, even if we somehow overcame
the typing problem, it's not clear there are any that are actually
better than ``@``. Some options that have been suggested include:
* U+00D7 MULTIPLICATION SIGN: ``A × B``
* U+22C5 DOT OPERATOR: ``A ⋅ B``
* U+2297 CIRCLED TIMES: ``A ⊗ B``
* U+00B0 DEGREE: ``A ° B``
What we need, though, is an operator that means "matrix
multiplication, as opposed to scalar/elementwise multiplication".
There is no conventional symbol for these in mathematics or
programming, where these operations are usually distinguished by
context. (And U+2297 CIRCLED TIMES is actually used conventionally to
mean exactly the opposite: elementwise multiplication -- the "Hadamard
product" -- as opposed to matrix multiplication). ``@`` at least has
the virtue that it *looks* like a funny non-commutative operator; a
naive user who knows maths but not programming couldn't look at ``A *
B`` versus ``A × B``, or ``A * B`` versus ``A ⋅ B``, or ``A * B``
versus ``A ° B`` and guess which one is the usual multiplication, and
which one is the special case.
Finally, there is the option of using multi-character tokens. Some
options:
* Matlab uses a ``.*`` operator. Aside from being visually confusable
with ``*``, this would be a terrible choice for us because in
Matlab, ``*`` means matrix multiplication and ``.*`` means
elementwise multiplication, so using ``.*`` for matrix
multiplication would make us exactly backwards from what Matlab
users expect.
* APL apparently used ``+.×``, which by combining a multi-character
token, confusing attribute-access-like . syntax, and a unicode
character, ranks somewhere below U+2603 SNOWMAN on our candidate
list. If we like the idea of combining addition and multiplication
operators as being evocative of how matrix multiplication actually
works, then something like ``+*`` could be used -- though this may
be too easy to confuse with ``*+``, which is just multiplication
combined with the unary ``+`` operator.
* PEP 211 suggested ``~*`` and ``~**``. This has the downside that it
sort of suggests that there is a unary ``*`` operator that is being
combined with unary ``~``, but it could work.
* R uses ``%*%`` for matrix multiplication. In R this forms part of a
general extensible infix system in which all tokens of the form
``%foo%`` are user-defined binary operators. We could steal the
token without stealing the system.
* Some other plausible candidates that have been suggested: ``><`` (=
ascii drawing of the multiplication sign ×); the footnote operators
``[*]`` and ``[**]`` or ``|*|`` and ``|**|`` (but when used in
context, the use of vertical grouping symbols tends to recreate the
nested parentheses visual clutter that was noted as one of the major
downsides of the function syntax we're trying to get away from);
``^*`` and ``^^``.
So, it doesn't matter much, but ``@`` seems as good or better than any
of the alternatives:
* It's a friendly character that Pythoneers are already used to typing
in decorators, but the decorator usage and the math expression
usage are sufficiently dissimilar that it would be hard to confuse
them in practice.
* It's widely accessible across keyboard layouts (and thanks to its
use in email addresses, this is true even of weird keyboards like
those in phones).
* It's round like ``*`` and :math:`\cdot`.
* The mATrices mnemonic is cute.
* The use of a single-character token reduces the line-noise effect,
and makes ``@@`` possible, which is a nice bonus.
* The swirly shape is reminiscent of the simultaneous sweeps over rows
and columns that define matrix multiplication
* Its asymmetry is evocative of its non-commutative nature.
--
Nathaniel J. Smith
Postdoctoral researcher - Informatics - University of Edinburgh
http://vorpus.org
More information about the Python-ideas
mailing list