[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