<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Tue, Sep 16, 2014 at 4:32 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-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">On Tue, Sep 16, 2014 at 6:56 PM, Jaime Fernández del Río<br>
<div><div class="h5"><<a href="mailto:jaime.frio@gmail.com">jaime.frio@gmail.com</a>> wrote:<br>
> On Tue, Sep 16, 2014 at 3:26 PM, Charles R Harris<br>
> <<a href="mailto:charlesr.harris@gmail.com">charlesr.harris@gmail.com</a>> wrote:<br>
>><br>
>> On Tue, Sep 16, 2014 at 2:51 PM, Nathaniel Smith <<a href="mailto:njs@pobox.com">njs@pobox.com</a>> wrote:<br>
>>><br>
>>> On Tue, Sep 16, 2014 at 4:31 PM, Jaime Fernández del Río<br>
>>> <<a href="mailto:jaime.frio@gmail.com">jaime.frio@gmail.com</a>> wrote:<br>
>>> > If it is a bug, it is an extended one, because it is the same behavior<br>
>>> > 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<br>
>>> > 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>
>>> ><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>
>>> "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>
>>><br>
>>> > and the code makes it very explicit that input argument dimensions with<br>
>>> > the<br>
>>> > same label are broadcast to a common shape, see here:<br>
>>> ><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<br>
>>> > feel<br>
>>> > wrong to me.<br>
>>><br>
>>> 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>
>>><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<br>
>>> > something<br>
>>> > that has been working like that for so long seems like a risky thing.<br>
>>> > And I<br>
>>> > cannot come with a convincing example of why it would be harmful<br>
>>> > either.<br>
>>><br>
>>> gufuncs are very new.<br>
>>><br>
>><br>
>> Or at least newly used. They've been sitting around for years with little<br>
>> use and less testing. This is probably (easily?) fixable as the shape of the<br>
>> 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>
><br>
> If we agree that it is broken, which I still am not fully sure of, then yes,<br>
> it is very easy to fix. I have been looking into that code quite a bit<br>
> lately, so I could patch something up pretty quick.<br>
><br>
> Are we OK with the appending of size 1 dimensions to complete the core<br>
> dimensions? That is, should matrix_multiply([1,1,1], [[1],[1],[1]]) work, or<br>
> should it complain about the first argument having less dimensions than the<br>
> core dimensions in the signature?<br>
<br>
</div></div>I think that by default, gufuncs should definitely *not* allow this.<br></blockquote><div><br></div><div>Too late! ;-)</div><div><br></div><div>I just put together some working code and sent a PR implementing the behavior that Charles asked for:</div><div><br></div><div><a href="https://github.com/numpy/numpy/pull/5077">https://github.com/numpy/numpy/pull/5077</a><br></div><div><br></div><div>Should we keep the discussion here, or take it over there?</div><div><br></div><div>Jaime</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<br>
Example case 1: qr can be applied equally well to a (1, n) array or an<br>
(n, 1) array, but with different results. If the user passes in an<br>
(n,) array, then how do we know which one they wanted?<br>
<br>
Example case 2: matrix multiplication, as you know :-), is a case<br>
where I do think we should allow for a bit more cleverness with the<br>
core dimensions... but the appropriate cleverness is much more subtle<br>
than just "prepend size 1 dimensions until things fit". Instead, for<br>
the first argument you need to prepend, for the second argument you<br>
need to append, and then you need to remove the corresponding<br>
dimensions from the output. Specific cases:<br>
<br>
# Your version gives:<br>
matmul([1, 1, 1], [[1], [1], [1]]).shape == (1, 1)<br>
# But this should be (1,) (try it with np.dot)<br>
<br>
# Your version gives:<br>
matmul([[1, 1, 1]], [1, 1, 1]) -> error, (1, 3) and (1, 3) are not conformable<br>
# But this should work (second argument should be treated as (3, 1), not (1, 3))<br>
<br>
So the default should be to be strict about core dimensions, unless<br>
explicitly requested otherwise by the person defining the gufunc.<br>
<span class=""><br>
> Lastly, there is an interesting side effect of the way this broadcasting is<br>
> handled: if a gufunc specifies a core dimension in an output argument only,<br>
> and an `out` kwarg is not passed in, then the output array will have that<br>
> core dimension set to be of size 1, e.g. if the signature of `f` is<br>
> '(),()->(a)', then f(1, 2).shape is (1,). This has always felt funny to me,<br>
> and I think that an unspecified dimension in an output array should either<br>
> be specified by a passed out array, or raise an error about an unspecified<br>
> core dimension or something like that. Does this sound right?<br>
<br>
</span>Does this have any use cases? My vote is that we simply disallow this<br>
until we have concrete uses and can decide how to do it properly. That<br>
way there won't be any backcompat concerns to deal with later.<br>
<div class=""><div class="h5"><br>
-n<br>
<br>
--<br>
Nathaniel J. Smith<br>
Postdoctoral researcher - Informatics - University of Edinburgh<br>
<a href="http://vorpus.org" target="_blank">http://vorpus.org</a><br>
_______________________________________________<br>
NumPy-Discussion mailing list<br>
<a href="mailto:NumPy-Discussion@scipy.org">NumPy-Discussion@scipy.org</a><br>
<a href="http://mail.scipy.org/mailman/listinfo/numpy-discussion" target="_blank">http://mail.scipy.org/mailman/listinfo/numpy-discussion</a><br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br>(\__/)<br>( O.o)<br>( > <) Este es Conejo. Copia a Conejo en tu firma y ayúdale en sus planes de dominación mundial.
</div></div>