Repository: numpy/numpy

Issue:
np.negative not deterministic, returning incorrect results
I am using Xubuntu Linux 16.04 (python and numpy versions are included below). Running the following code:
```
import numpy as np
import sys

print('python:', sys.version)
print('numpy:', np.version.version)

b = np.array([1, 1, 1, 1, 1], dtype=float)
b_neg = (b < 0)

## uncomment the next line and it might behave
# print(b)
#

b = np.negative(b, where=(b_neg))

print(b)
```

It will print varying, similar, incorrect outputs, two of which from my computer are:
```
$ python main.py
python: 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19) 
[GCC 7.2.0]
numpy: 1.14.0
[0.00000000e+000 4.64016820e-310 5.08395840e-299 1.93322822e-260
 2.37151510e-322]

$ python main.py
python: 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19) 
[GCC 7.2.0]
numpy: 1.14.0
[0.00000000e+000 4.65820516e-310 5.08395840e-299 1.93322822e-260
 2.37151510e-322]
```

Uncommenting `print(b)` as noted in the code above fixes the problem in at least one version of python:
```
$ python main.py
python: 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19) 
[GCC 7.2.0]
numpy: 1.14.0
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
```

Other versions are still behaving strangely (even with the `print(b)` line uncommented):
```
$ python main.py
('python:', '2.7.13 |Anaconda custom (64-bit)| (default, Dec 20 2016, 23:09:15) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]')
('numpy:', '1.10.4')
[ 1.  1.  1.  1.  1.]
[ 0.  0.  0.  0.  0.]
```

```
$ python3 main.py
python: 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609]
numpy: 1.11.0
[ 1.  1.  1.  1.  1.]
[ 0.  0.  0.  0.  0.]
```

After updating to the latest version, it is still occurring:

With `print(b)` commented out:
```
$ python main.py
python: 3.6.5 |Anaconda custom (64-bit)| (default, Apr 29 2018, 16:14:56) 
[GCC 7.2.0]
numpy: 1.14.2
[ 4.68703220e-310 -8.44120994e-121  6.90939306e-310  6.90938300e-310
  5.34349229e+009]
```
With `print(b)` uncommented:
```
$ python main.py
python: 3.6.5 |Anaconda custom (64-bit)| (default, Apr 29 2018, 16:14:56) 
[GCC 7.2.0]
numpy: 1.14.2
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
```


Discussion:
@kevincmann - it is not `np.negative` that is odd; this happens with all functions. And even though the output looks strange, it is actually expected: with the `where` argument, the output *only* gets set when the boolean is `True`. Normally, this is used with an `out` argument that already is filled, but here you do not pass in an `out` and thus an empty array is created. Since `where` is `False` everywhere, effectively you just get back the empty array. Now, the reason that the different versions and the print statement influence the result is just that those affect which memory is used for the empty array; apparently, with the print statement, you get a piece of memory that was previously initialized.

Does this make sense? Can you think of a way in which we could document this more clearly, so that you would not have been confused?
Current docs are:

> ### where
> 
> > New in version 1.7.
> > 
> > Accepts a boolean array which is broadcast together with the operands. Values of True indicate to calculate the ufunc at that position, values of False indicate to leave the value in the output alone. This argument cannot be used for generalized ufuncs as those take non-scalar input.

I think it could be made clearer what `leave the value in the output alone` means here.

Yes, I agree. @kevincmann - could you think what would have helped *you*? Ideally, it is just a few words, and I fear that if @eric-wieser or I write them they would clarify it for us but not for someone who has not been developing numpy! (If you're up to it, even make a PR!)
Frankly I can't quite figure out what the function is returning conditional on what parameters are provided, but I do know that the current `return` notes are incomplete. The code I have run indicates:

- The `where` parameter will affect if a newly created array is returned or not.
- The `out` parameter will affect if a newly created array is returned or not.

```
import numpy as np
import sys

print('python:', sys.version)
print('numpy:', np.version.version)

b = np.array([1, 1, -1, 1, 1], dtype=float) # note the negative value
b_neg = (b < 0)
print('1a', np.negative(b, where=(b_neg)))
print('1b',b)

b = np.array([1, 1, -1, 1, 1], dtype=float)
b_neg = (b < 0)
print('2a', np.negative(b, where=(b_neg), out=b))
print('2b', b)

b = np.array([1, 1, -1, 1, 1], dtype=float)
b_neg = (b < 0)
print('3a', np.negative(b))
print('3b', b)

b = np.array([1, 1, -1, 1, 1], dtype=float)
b_neg = (b < 0)
print('4a', np.negative(b, out=b))
print('4b', b)
```
Outputs:
```
$ python main.py
python: 3.6.5 |Anaconda custom (64-bit)| (default, Apr 29 2018, 16:14:56) 
[GCC 7.2.0]
numpy: 1.14.2
1a [6.95028944e-310 6.95028944e-310 1.00000000e+000 2.11756878e+214
 1.02281887e-259]
1b [ 1.  1. -1.  1.  1.]
2a [1. 1. 1. 1. 1.]
2b [1. 1. 1. 1. 1.]
3a [-1. -1.  1. -1. -1.]
3b [ 1.  1. -1.  1.  1.]
4a [-1. -1.  1. -1. -1.]
4b [-1. -1.  1. -1. -1.]
```

Based on these observations, I have difficulty knowing what exactly belongs in the documentation for the `return` value, but I do know that the current notes here are quite lacking.

> Returns: y : ndarray or scalarReturned array or scalar: y = -x.

It seems:
- If `where` is used, it will create a new array, unless `out` is also used, and then it will not create a new array.

The documentation for `out` includes notes for creating a new array, but it is also documenting its effect on the `return` value, and we can't rely on users looking in the documentation for other parameters to find documentation for the one they are interested in. It also doesn't mention what happens whether `where` is used or not, and that is not mentioned in the `where` documentation, either.

> out : ndarray, None, or tuple of ndarray and None, optional
> A location into which the result is stored. If provided, it must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs.

I would suggest moving the "If [`out` is] not provided or None, a freshly-allocated array is returned." to the `returns` documentation, and also adding a statement in `returns` to the effect of "If `where` is provided, a new array is not created."

In total:
> If `out` is not used but `where` is, a new array is created with noninitialized values, the `ufunc` is applied where the `where` argument is true, and the modified new array is returned. However, if both `out` and `where` are used, no new array is created, the `ufunc` is applied to where the `where` argument is true, and the modified input array is returned.

I think that's the most accurate statement that I can determine from its behavior.
@kevincmann - this is useful! Though you are still somewhat confused, because a new array is always created when no `out` is given; that has nothing to do with `where` -- `where` just determines which elements of the output array get written to.

It may help to give a brief outline of what actually happens internally:
1. Find inputs and determine what they broadcast to (for `np.negative`, that's just the shape of the single input array);
2. If `out` is given, check that it has the correct shape/dtype and raise error if that is not the case; if `out` is not given, create a new array (allocating memory but not filling it, so can be random numbers or old stuff from a previous array).
3. Set up an iterator that processes the inputs in blocks, and calculate results for those blocks.
4. For each block, copy the results to the output array if `where` is True (by default, everywhere).

Perhaps

> If out is missing or None, a return value is created with uninitialized values.  The `ufunc` is applied, if `where` is used the `ufunc` will be applied where `where` is `True`. 

> If `out` is supplied and not None, the `ufunc` modifies it inplace, and the modified `out` array is returned. Note that the return value may have uninitialized values for `False` values in `where`
@mattip It is close.

I think if the second sentence in the second note were to be moved to the first note, then both would be accurate. 

In the first note, in the case when `where` is used, the second sentence of the second note applies, viz. there will be random values wherever `where` is not `True`. And in the case when `where` is not used, all (uninitialized) values are modified to correspond with the input with the expected changes (i.e. y = -x), so there will not be uninitialized values returned.

In the second note, when `out` is supplied and not `None`, there have not been uninitialized values returned in my testing. The `return` value of the function has been identical to the supplied `out` argument.

The code snippet from my previous comment shows the pertinent permutations that I have been using to form conclusions. `1` and `3` are relevant to your first note, and `2` and `4` are relevant to your second note.
> And in the case when where is not used,

I don't think we need to call this out. `where` defaults to `True`, so this is already covered by the phrase "wherever where is not True"
@kevin: Expressing mhvk's summary in code, the code path you care about is equivalent to (but involves fewer copies than):

```python
if out is None:
    # empty produces random memory
    out = np.empty(...)  # with correct type / shape etc
out[where] = operation(x, y)[where]
return out
```

Resolving patch:
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -5647,10 +5647,13 @@ def luf(lamdaexpr, *args, **kwargs):
         Alternate array object(s) in which to put the result; if provided, it
         must have a shape that the inputs broadcast to. A tuple of arrays
         (possible only as a keyword argument) must have length equal to the
-        number of outputs; use `None` for outputs to be allocated by the ufunc.
+        number of outputs; use `None` for uninitialized outputs to be
+        allocated by the ufunc.
     where : array_like, optional
         Values of True indicate to calculate the ufunc at that position, values
-        of False indicate to leave the value in the output alone.
+        of False indicate to leave the value in the output alone.  Note that if
+        an uninitialized return array is created via the default ``out=None``,
+        then the elements where the values are False will remain uninitialized.
     **kwargs
         For other keyword-only arguments, see the :ref:`ufunc docs <ufuncs.kwargs>`.
 
@@ -5658,7 +5661,8 @@ def luf(lamdaexpr, *args, **kwargs):
     -------
     r : ndarray or tuple of ndarray
         `r` will have the shape that the arrays in `x` broadcast to; if `out` is
-        provided, `r` will be equal to `out`. If the function has more than one
+        provided, it will be returned. If not, `r` will be allocated and
+        may contain uninitialized values. If the function has more than one
         output, then the result will be a tuple of arrays.
 
     """)


Test patch: