Repository: scipy/scipy

Issue:
affine_transform complains about output_shape when output array is provided
This works

``` python
>>> affine_transform(np.array([1., 2., 3.]), (0.5,), output_shape=(5,), order=1)
array([ 1. ,  1.5,  2., 2.5, 3. ])
```

This does not.

``` python
>>> out = np.zeros(5)
>>> affine_transform(np.array([1.,2.,3.]), (0.5,), output=out, order=1)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-44-d53637a517ad> in <module>()
----> 1 affine_transform(np.array([1.,2.,3.]), (0.5,), output=out, order=1)

.../lib/python2.7/site-packages/scipy/ndimage/interpolation.pyc in affine_transform(input, matrix, offset, output_shape, output, order, mode, cval, prefilter)
    396         filtered = input
    397     output, return_value = _ni_support._get_output(output, input,
--> 398                                                    shape=output_shape)
    399     matrix = numpy.asarray(matrix, dtype=numpy.float64)
    400     if matrix.ndim not in [1, 2] or matrix.shape[0] < 1:

.../lib/python2.7/site-packages/scipy/ndimage/_ni_support.pyc in _get_output(output, input, shape)
     83     else:
     84         if output.shape != shape:
---> 85             raise RuntimeError("output shape not correct")
     86         return_value = None
     87     return output, return_value

RuntimeError: output shape not correct
```

If one provides an output to affine_transform, then shouldn't the output_shape be derived from output.shape?



Discussion:
I agree that the current behavior is counter-intuitive and also hard to debug. Is this going to be addressed?
This probably isn't high prio for core devs, but contributions are very welcome of course. It's unclear to me from reading this issue what the expected behavior is. `affine_transform(np.array([1., 2., 3.]), (0.5,), order=1)` gives `array([1. , 1.5, 2. ])`, so the requesting an output of shape (5,) is already different from the regular `out=` numpy behavior (there shapes have to match). 
> This probably isn't high prio for core devs, but contributions are very welcome of course.

Makes sense.

> It's unclear to me from reading this issue what the expected behavior is.

I cannot speak for @dadkins but for me the expected behavior is:
 1. None of `output_shape`, `output` specified: current behavior
 2. `output_shape` specified but `output` not specified: current behavior
 3. `output` specified: ignore `output_shape` (or set to `output.shape`). Do not fail with `output shape not correct`. `output_shape` is implicitly provided through `output` and `output` should take precedence.

If there is consensus that this is desired in general, I will try to change `affine_transform` accordingly. But maybe the current behavior is preferred, in general.
I would expect the output shape to come from:

* `output_shape` if specified, otherwise
* `output.shape` if `output` specified, otherwise
* `input.shape`.

I suppose I'd expect an error if `output_shape` is specified, and does not match `output.shape`.

Resolving patch:
diff --git a/scipy/ndimage/interpolation.py b/scipy/ndimage/interpolation.py
--- a/scipy/ndimage/interpolation.py
+++ b/scipy/ndimage/interpolation.py
@@ -543,7 +543,10 @@ def affine_transform(input, matrix, offset=0.0, output_shape=None,
         raise RuntimeError('spline order not supported')
     input = numpy.asarray(input)
     if output_shape is None:
-        output_shape = input.shape
+        if isinstance(output, numpy.ndarray):
+            output_shape = output.shape
+        else:
+            output_shape = input.shape
     if input.ndim < 1 or len(output_shape) < 1:
         raise RuntimeError('input and output rank must be > 0')
     complex_output = numpy.iscomplexobj(input)


Test patch:
diff --git a/scipy/ndimage/tests/test_interpolation.py b/scipy/ndimage/tests/test_interpolation.py
--- a/scipy/ndimage/tests/test_interpolation.py
+++ b/scipy/ndimage/tests/test_interpolation.py
@@ -851,6 +851,22 @@ def test_affine_transform_multi_d_endianness_with_output_parameter(self):
             result = out if returned is None else returned
             assert_array_almost_equal(result, [1])
 
+
+    def test_affine_transform_output_shape(self):
+        # don't require output_shape when out of a different size is given
+        data = numpy.arange(8, dtype=numpy.float64)
+        out = numpy.ones((16,))
+        oshape = out.shape
+
+        ndimage.affine_transform(data, [[1]], output=out)
+        assert_array_almost_equal(out[:8], data)
+
+        # mismatched output shape raises an error
+        with pytest.raises(RuntimeError):
+            ndimage.affine_transform(
+                data, [[1]], output=out, output_shape=(12,))
+
+
     def test_affine_transform_with_string_output(self):
         data = numpy.array([1])
         out = ndimage.affine_transform(data, [[1]], output='f')