8000 BUG: Ensure out is returned in einsum. by mattip · Pull Request #11406 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: Ensure out is returned in einsum. #11406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 29, 2018
Merged

Conversation

mattip
Copy link
Member
@mattip mattip commented Jun 21, 2018

In einsum, if out is given, it should be assigned to ret.

@charris charris added this to the 1.15.0 release milestone Jun 21, 2018
@charris
Copy link
Member
charris commented Jun 21, 2018

Is this in #11345?

@mattip
Copy link
Member Author
mattip commented Jun 22, 2018

IMO it is orthogonal to #11345

@@ -2769,8 +2769,11 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop,

/* Initialize the output to all zeros and reset the iterator */
ret = NpyIter_GetOperandArray(iter)[nop];
Py_INCREF(ret);
PyArray_AssignZero(ret, NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you convince me why this line should before the if and not after it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing PyArray_AssignZero on out will fail since out, with activated writeback semantics, will have its READONLY flag set. We must only write to the operand ndarray, the one whose base is out and has extra memory allocated for the writeback buffer. ret is subsequently reassigned to out since that is what must be returned after wrtieback resolution which resets the READONLY flag to its previous value.

@mattip mattip force-pushed the einsum-out-is-res branch from 96cd478 to 4f5ada2 Compare June 24, 2018 22:43
@mattip
Copy link
Member Author
mattip commented Jun 25, 2018

CircleCI failed and I cannot restart the job, closing and reopening to trip CI

@mattip mattip closed this Jun 25, 2018
@mattip mattip reopened this Jun 25, 2018
if (PyArray_AssignZero(ret, NULL) < 0) {
goto fail;
}
if (out != NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fishy, why do anything to ret if it is simply set to out?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the output operand has writeback semantics, NpyIter_GetOperandArray(iter)[nop] is the temporary ndarray created with out as it's base ndarray. Then we zero the temporary data rather than changing out itself. See issue #10956 for the python-level equivalent discussion about what is meant by iter.operand[i] - the temporary ndarray with writeback semantics or the underlying user-created input-ouput ndarray.

Lines 2775-2777 ensure that ret and out point to the same ndarray, as required when using an out kwarg.

@eric-wieser
Copy link
Member

Can you move the ret = out line to the very end of the function? It sounds like it's not valid to touch out until the iterator is closed, and it would be nice therefore if ret did not point to it until that point

@mattip
Copy link
Member Author
mattip commented Jun 27, 2018

Moving ret = out to the end turned into a nice cleanup

}
}

finish:
if (out != NULL) {
ret = out;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiousity, a couple of questions.

  • The iterator iterates over out if present?
  • The writeback to out or the new output array happens in iter deallocation?
  • I assume deallocation also decrefs the out or new array?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to incref the output before NpyIterDeallocate so it stays alive through that call.

  • The assignment op[nop] = out near the beginning ensures out, if present is used in iteration, possibly with writeback semantics. Note that nop is the number of inputs, but actually op uses nop +1 entries, one additional for the output.
  • The writeback happens in NpyIterDeallocate, which also decrefs all operands including the output (which were increfed in NpyIter_AdvancedNew.
  • In addition, einsum itself increfed all the inputs while deciding whether to create views or not, around get_combined_dims_view. So whether fail or finish, at the end the function decrefs the inputs, (not the output).

@charris charris merged commit c4924b0 into numpy:master Jun 29, 2018
@charris
Copy link
Member
charris commented Jun 29, 2018

Thanks Matti.

@charris charris removed the 09 - Backport-Candidate PRs tagged should be backported label Jul 3, 2018
@charris charris removed this from the 1.15.0 release milestone Jul 3, 2018
@charris charris changed the title BUG: ensure ret is out in einsum BUG: Ensure out is returned in einsum. Jul 3, 2018
@mattip mattip deleted the einsum-out-is-res branch October 9, 2018 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
163D
0