<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Sep 16, 2014 at 4:56 PM, Jaime Fernández del Río <span dir="ltr"><<a href="mailto:jaime.frio@gmail.com" target="_blank">jaime.frio@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div class="h5">On Tue, Sep 16, 2014 at 3:26 PM, Charles R Harris <span dir="ltr"><<a href="mailto:charlesr.harris@gmail.com" target="_blank">charlesr.harris@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote"><div><div>On Tue, Sep 16, 2014 at 2:51 PM, Nathaniel Smith <span dir="ltr"><<a href="mailto:njs@pobox.com" target="_blank">njs@pobox.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span>On Tue, Sep 16, 2014 at 4:31 PM, Jaime Fernández del Río<br>
<<a href="mailto:jaime.frio@gmail.com" target="_blank">jaime.frio@gmail.com</a>> wrote:<br>
> If it is a bug, it is an extended one, because it is the same behavior of<br>
> einsum:<br>
><br>
>>>> np.einsum('i,i', [1,1,1], [1])<br>
> 3<br>
>>>> np.einsum('i,i', [1,1,1], [1,1])<br>
> Traceback (most recent call last):<br>
>   File "<stdin>", line 1, in <module><br>
> ValueError: operands could not be broadcast together with remapped shapes<br>
> [origi<br>
> nal->remapped]: (3,)->(3,) (2,)->(2,)<br>
><br>
> And I think it is a conscious design decision, there is a comment about<br>
> broadcasting missing core dimensions here:<br>
><br>
> <a href="https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/ufunc_object.c#L1940" target="_blank">https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/ufunc_object.c#L1940</a><br>
<br>
</span>"intentional" and "sensible" are not always the same thing :-). That<br>
said, it isn't totally obvious to me what the correct behaviour for<br>
einsum is in this case.<br>
<span><br>
> and the code makes it very explicit that input argument dimensions with the<br>
> same label are broadcast to a common shape, see here:<br>
><br>
> <a href="https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/ufunc_object.c#L1956" target="_blank">https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/ufunc_object.c#L1956</a><br>
><br>
> I kind of expect numpy to broadcast whenever possible, so this doesn't feel<br>
> wrong to me.<br>
<br>
</span>The case Chuck is talking about is like if we allowed matrix<br>
multiplication between an array with shape (n, 1) with an array with<br>
(k, m), because (n, 1) can be broadcast to (n, k). This feels VERY<br>
wrong to me, will certainly hide many bugs, and is definitely not how<br>
it works right now (for np.dot, anyway; apparently it does work that<br>
way for the brand-new gufunc np.linalg.matrix_multiply, but this must<br>
be an accident).<br>
<span><br>
> That said, it is hard to come up with convincing examples of how this<br>
> behavior would be useful in any practical context. But changing something<br>
> that has been working like that for so long seems like a risky thing. And I<br>
> cannot come with a convincing example of why it would be harmful either.<br>
<br>
</span>gufuncs are very new.<br>
<div><div><br></div></div></blockquote><div><br></div></div></div><div>Or at least newly used. They've been sitting around for years with little use and less testing. This is probably (easily?) fixable as the shape of the operands is available.<br><br>In [22]: [d.shape for d in nditer([[1,1,1], [[1,1,1]]*3]).operands]<br>Out[22]: [(3,), (3, 3)]<br><br>In [23]: [d.shape for d in nditer([[[1,1,1]], [[1,1,1]]*3]).operands]<br>Out[23]: [(1, 3), (3, 3)]<br><br></div></div></div></div></blockquote><div><br></div></div></div><div>If we agree that it is broken, which I still am not fully sure of, then yes, it is very easy to fix. I have been looking into that code quite a bit lately, so I could patch something up pretty quick.</div></div></div></div></blockquote><div><br></div><div>That would be nice... I've been starting to look through the code and didn't relish it. <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>Are we OK with the appending of size 1 dimensions to complete the core dimensions? That is, should matrix_multiply([1,1,1], [[1],[1],[1]]) work, or should it complain about the first argument having less dimensions than the core dimensions in the signature?</div></div></div></div></blockquote><div><br></div><div>Yes, I think we need to keep that part. It is even essential ;) <br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>Lastly, there is an interesting side effect of the way this broadcasting is handled: if a gufunc specifies a core dimension in an output argument only, and an `out` kwarg is not passed in, then the output array will have that core dimension set to be of size 1, e.g. if the signature of `f` is '(),()->(a)', then f(1, 2).shape is (1,). This has always felt funny to me, and I think that an unspecified dimension in an output array should either be specified by a passed out array, or raise an error about an unspecified core dimension or something like that. Does this sound right?</div></div></div></div></blockquote><div><br></div><div>Uh, I need to get my head around that before commenting.<br><br></div><div>Chuck <br></div></div></div></div>