On Sun, Mar 9, 2014 at 8:44 PM, William Stein <
wst...@gmail.com> wrote:
> On Sun, Mar 9, 2014 at 12:40 PM, <
n...@vorpus.org> wrote:
>> On Sunday, March 9, 2014 7:20:50 PM UTC, Jeroen Demeyer wrote:
>>>
>>> I think the following piece should be made more clear, I don't
>>> understand what you're trying to say here:
>>>
>>> The problem is that the presence of two different duck-types for numeric
>>> data -- one where * means matrix multiply, and one where * means
>>> elementwise multiplication -- make it impossible to write generic
>>> functions that can operate on arbitrary data.
>>
>>
>> Indeed, it's clear from everyone's responses here that I at least need to
>> add a new section talking about these things explicitly, and also about why
>> elementwise-* is actually used so often in practice in numeric computation
>> (as opposed dto symbolic comptuation), and why np.matrix is so loathed. (It
>> really is though, I didn't just make that up :-) I doubt you can find a
>> single developer of any numpy-using package who has anything good to say
>> about it.) I'll try to get to write such a section over the next few hours;
>> at least it might make a better basis for discussion.
>>
>> In brief, the issue is that elementwise-* is a fine convention and you can
>> use it to write useful code, and matrix-multiply-* is a fine convention and
>
> I'm not convinced * is a fine convention for element-wise
> multiplication of matrices, since there is no software I know of that
> does that. Even Matlab uses * to mean matrix multiplication:
>
http://www.math.utah.edu/~eyre/computing/matlab-intro/math.html
R alsos use * for elementwise mul. And really, it does work fine and
is useful in many application areas, I promise! :-) I've sent similar
heads-up emails to 13 projects now, and so far you guys are the only
ones who have blinked at this part; numpy has ~25x more PyPI downloads
than sympy (a terrible metric I know, but perhaps it's at least some
vague noisy estimate of relative user bases) and I guess those users
are pretty much all happy with elementwise-*. My point isn't that the
majority is always right or anything like that. I'm just saying, if
you think elementwise-* is not just sub-optimal but also bizarre and
unthinkable, then you might be missing something :-) (just like I was
missing something when I thought that elementwise-* was so obvious it
needed no justification).
It may well be that the answer is that sage (and perhaps sympy) is
just not in the target audience for this PEP, because you only have
one multiplication operation that matters and the status quo works
fine for you. If so that's great, though it'd still be good to
coordinate to the extent it makes sense, and the feedback is still
really useful.
> But * is set in stone for numpy. The motivation for your proposal is
> to make your example code involving matrix multiplication of numpy
> arrays easier to read/write. I wonder if there is any way to do this
> using a with statement, e.g.,
>
>
> c = a*b # element-wise multiplication
>
> with numpy.mul_as_dot():
> c = a*b # matrix multiplication
>
>
> Then the exact part of the code that involves some expressions with
> matrix multiplies of numpy arrays can be cleanly written with *
> instead of of numpy.dot, and without having to introduce another
> operator. Is this at least technically possible? If so, it is worth
> addressing in the PEP. It would solve the problem of coding up
> certain algorithms cleanly, and it is extremely clear and explicit
> what is going on when you just jump into the code.
This is a really interesting suggestion, and it is technically
possible. Unfortunately, though, after thinking for a bit I see some
problems that strike me as insurmountable -- here's a potential
discussion in PEP-ese (to be slotted into the list of "rejected
alternatives"):
**Use ``with`` to switch the meaning of ``*`` within a single code
block**: E.g., numpy could define a special context object so that
we'd have::
c = a * b # element-wise multiplication
with numpy.mul_as_dot:
c = a * b # matrix multiplication
However, this has two serious problems: first, it requires that every
matrix-like object ``__mul__`` method know how to check some global
state (``numpy.mul_is_currently_dot`` or whatever). This is fine if
``a`` and ``b`` are numpy objects, but the world contains many
non-numpy matrix-like objects. So this either requires non-local
coupling -- every numpy competitor library has to import numpy and
then check ``numpy.mul_is_currently_dot`` on every operation -- or
else it breaks duck-typing, with the above code doing radically
different things depending on whether ``a`` and ``b`` are numpy
objects or some other sort of object. Second, and worse, ``with``
blocks are dynamically scoped, not lexically scoped; i.e., any
function that gets called inside the ``with`` block will suddenly find
itself executing inside the mul_as_dot world, and crash and burn
horribly (if you're lucky). So this is a construct that could only be
used safely in rather limited cases (no function calls), and which
makes it very easy to shoot yourself in the foot without warning.
What do you think?
> Another issue -- imagine jumping into code and seeing @'s everywhere
> -- you would have to google and figure out what is going on, which
> could be hard for "@ operator". For example, I use the @ operator
> all the time in CoffeeScript, so it's a useful thing, but googling "@
> operator" yields basically nothing useful about anything.
I'm not really that worried about this, because it's the same problem
that *every* builtin operator faces. How do you know what << or // or
^ or | or % or ** do when you first see them? You flip to the table at
the back/front of your Python reference that lists all the operators,
and it tells you what they are. If this PEP is accepted, then @ will
be listed there right next to the rest of them. Even * is pretty
arbitrary the first time you see it -- real mathematical notation uses
\cdot or occasionally \times, not \star -- but we forget this because
we already learned it a while ago :-).
-n