Repository: numpy/numpy

Issue:
diag returns ndarray (Trac #1417)
_Original ticket http://projects.scipy.org/numpy/ticket/1417 on 2010-02-28 by trac user uhrm, assigned to @pierregm._

When called with a `matrix` object, `diag` return an `ndarray`.

Example:

```
>>> B = numpy.matrix("1.7 2.2 0.3; 1.2 0.2 0.0; 1.0 1.0 0.7; 0.3 0.3 0.2")
>>> B
matrix([[ 1.7,  2.2,  0.3],
        [ 1.2,  0.2,  0. ],
        [ 1. ,  1. ,  0.7],
        [ 0.3,  0.3,  0.2]])
>>> d = numpy.diag(B)
>>> d
array([ 1.7,  0.2,  0.7])
```

If I understand correctly, the return type should also be a `matrix`. Since `matrix` is always 2 dimensional, question is whether it's a column or row vector (I'd prefer a column vector).

diag returns ndarray (Trac #1417)
_Original ticket http://projects.scipy.org/numpy/ticket/1417 on 2010-02-28 by trac user uhrm, assigned to @pierregm._

When called with a `matrix` object, `diag` return an `ndarray`.

Example:

```
>>> B = numpy.matrix("1.7 2.2 0.3; 1.2 0.2 0.0; 1.0 1.0 0.7; 0.3 0.3 0.2")
>>> B
matrix([[ 1.7,  2.2,  0.3],
        [ 1.2,  0.2,  0. ],
        [ 1. ,  1. ,  0.7],
        [ 0.3,  0.3,  0.2]])
>>> d = numpy.diag(B)
>>> d
array([ 1.7,  0.2,  0.7])
```

If I understand correctly, the return type should also be a `matrix`. Since `matrix` is always 2 dimensional, question is whether it's a column or row vector (I'd prefer a column vector).

diag returns ndarray (Trac #1417)
_Original ticket http://projects.scipy.org/numpy/ticket/1417 on 2010-02-28 by trac user uhrm, assigned to @pierregm._

When called with a `matrix` object, `diag` return an `ndarray`.

Example:

```
>>> B = numpy.matrix("1.7 2.2 0.3; 1.2 0.2 0.0; 1.0 1.0 0.7; 0.3 0.3 0.2")
>>> B
matrix([[ 1.7,  2.2,  0.3],
        [ 1.2,  0.2,  0. ],
        [ 1. ,  1. ,  0.7],
        [ 0.3,  0.3,  0.2]])
>>> d = numpy.diag(B)
>>> d
array([ 1.7,  0.2,  0.7])
```

If I understand correctly, the return type should also be a `matrix`. Since `matrix` is always 2 dimensional, question is whether it's a column or row vector (I'd prefer a column vector).



Discussion:
This is not a masked array issue, so it should not have the `component: numpy.ma` tag.

Should be fixable. Matlab uses a column vector for the result, so that is as good a choice as any.

I'm trying to tackle this bug, but I'm stuck.  My idea was to edit `numpy/core/fromnumeric.py` and change

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    return asarray(a).diagonal(offset, axis1, axis2)
```

to

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    if isinstance(a, matrix):
        return asmatrix(a).diagonal(offset, axis1, axis2)
    else:
        return asarray(a).diagonal(offset, axis1, axis2)
```

but that would require adding `from numpy.matrixlib.defmatrix import matrix, asmatrix` to fromnumeric.py which would create a problematic cyclic import.  But, without having access to `matrix`, I can't see how to fix this bug.

Any ideas?

Cleanest would probably be to change the diagonal function to look like the
other global functions that have matching array methods that they defer to
-- look at how e.g. np.sum calls arr.sum.

(Plus make sure the matrix.diagonal method works properly, of course.)
On 9 Dec 2014 02:05, "Garrett-R" notifications@github.com wrote:

> I'm trying to tackle this bug, but I'm stuck. My idea was to edit
> numpy/core/fromnumeric.py and change
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     return asarray(a).diagonal(offset, axis1, axis2)
> 
> to
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     if isinstance(a, matrix):
>         return asmatrix(a).diagonal(offset, axis1, axis2)
>     else:
>         return asarray(a).diagonal(offset, axis1, axis2)
> 
> but that would require adding from numpy.matrixlib.defmatrix import
> matrix, asmatrix to fromnumeric.py which would create a problematic
> cyclic import. But, without having access to matrix, I can't see how to
> fix this bug.
> 
> Any ideas?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/numpy/numpy/issues/2015#issuecomment-66223504.

I think that what you are trying to do is exactly what `asanyarray` does:

```
>>> a = np.matrix(np.eye(5))
>>> np.asanyarray(a).diagonal()
matrix([[ 1.,  1.,  1.,  1.,  1.]])
```

Right, thanks.  I'll use `asanyarray`.

This is not a masked array issue, so it should not have the `component: numpy.ma` tag.

Should be fixable. Matlab uses a column vector for the result, so that is as good a choice as any.

I'm trying to tackle this bug, but I'm stuck.  My idea was to edit `numpy/core/fromnumeric.py` and change

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    return asarray(a).diagonal(offset, axis1, axis2)
```

to

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    if isinstance(a, matrix):
        return asmatrix(a).diagonal(offset, axis1, axis2)
    else:
        return asarray(a).diagonal(offset, axis1, axis2)
```

but that would require adding `from numpy.matrixlib.defmatrix import matrix, asmatrix` to fromnumeric.py which would create a problematic cyclic import.  But, without having access to `matrix`, I can't see how to fix this bug.

Any ideas?

Cleanest would probably be to change the diagonal function to look like the
other global functions that have matching array methods that they defer to
-- look at how e.g. np.sum calls arr.sum.

(Plus make sure the matrix.diagonal method works properly, of course.)
On 9 Dec 2014 02:05, "Garrett-R" notifications@github.com wrote:

> I'm trying to tackle this bug, but I'm stuck. My idea was to edit
> numpy/core/fromnumeric.py and change
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     return asarray(a).diagonal(offset, axis1, axis2)
> 
> to
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     if isinstance(a, matrix):
>         return asmatrix(a).diagonal(offset, axis1, axis2)
>     else:
>         return asarray(a).diagonal(offset, axis1, axis2)
> 
> but that would require adding from numpy.matrixlib.defmatrix import
> matrix, asmatrix to fromnumeric.py which would create a problematic
> cyclic import. But, without having access to matrix, I can't see how to
> fix this bug.
> 
> Any ideas?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/numpy/numpy/issues/2015#issuecomment-66223504.

I think that what you are trying to do is exactly what `asanyarray` does:

```
>>> a = np.matrix(np.eye(5))
>>> np.asanyarray(a).diagonal()
matrix([[ 1.,  1.,  1.,  1.,  1.]])
```

Right, thanks.  I'll use `asanyarray`.

This is not a masked array issue, so it should not have the `component: numpy.ma` tag.

Should be fixable. Matlab uses a column vector for the result, so that is as good a choice as any.

I'm trying to tackle this bug, but I'm stuck.  My idea was to edit `numpy/core/fromnumeric.py` and change

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    return asarray(a).diagonal(offset, axis1, axis2)
```

to

```
def diagonal(a, offset=0, axis1=0, axis2=1):
    if isinstance(a, matrix):
        return asmatrix(a).diagonal(offset, axis1, axis2)
    else:
        return asarray(a).diagonal(offset, axis1, axis2)
```

but that would require adding `from numpy.matrixlib.defmatrix import matrix, asmatrix` to fromnumeric.py which would create a problematic cyclic import.  But, without having access to `matrix`, I can't see how to fix this bug.

Any ideas?

Cleanest would probably be to change the diagonal function to look like the
other global functions that have matching array methods that they defer to
-- look at how e.g. np.sum calls arr.sum.

(Plus make sure the matrix.diagonal method works properly, of course.)
On 9 Dec 2014 02:05, "Garrett-R" notifications@github.com wrote:

> I'm trying to tackle this bug, but I'm stuck. My idea was to edit
> numpy/core/fromnumeric.py and change
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     return asarray(a).diagonal(offset, axis1, axis2)
> 
> to
> 
> def diagonal(a, offset=0, axis1=0, axis2=1):
>     if isinstance(a, matrix):
>         return asmatrix(a).diagonal(offset, axis1, axis2)
>     else:
>         return asarray(a).diagonal(offset, axis1, axis2)
> 
> but that would require adding from numpy.matrixlib.defmatrix import
> matrix, asmatrix to fromnumeric.py which would create a problematic
> cyclic import. But, without having access to matrix, I can't see how to
> fix this bug.
> 
> Any ideas?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/numpy/numpy/issues/2015#issuecomment-66223504.

I think that what you are trying to do is exactly what `asanyarray` does:

```
>>> a = np.matrix(np.eye(5))
>>> np.asanyarray(a).diagonal()
matrix([[ 1.,  1.,  1.,  1.,  1.]])
```

Right, thanks.  I'll use `asanyarray`.


Resolving patch:
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -18,14 +18,14 @@
 
 # functions that are methods
 __all__ = [
-        'alen', 'all', 'alltrue', 'amax', 'amin', 'any', 'argmax',
-        'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip',
-        'compress', 'cumprod', 'cumproduct', 'cumsum', 'diagonal', 'mean',
-        'ndim', 'nonzero', 'partition', 'prod', 'product', 'ptp', 'put',
-        'rank', 'ravel', 'repeat', 'reshape', 'resize', 'round_',
-        'searchsorted', 'shape', 'size', 'sometrue', 'sort', 'squeeze',
-        'std', 'sum', 'swapaxes', 'take', 'trace', 'transpose', 'var',
-        ]
+    'alen', 'all', 'alltrue', 'amax', 'amin', 'any', 'argmax',
+    'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip',
+    'compress', 'cumprod', 'cumproduct', 'cumsum', 'diagonal', 'mean',
+    'ndim', 'nonzero', 'partition', 'prod', 'product', 'ptp', 'put',
+    'rank', 'ravel', 'repeat', 'reshape', 'resize', 'round_',
+    'searchsorted', 'shape', 'size', 'sometrue', 'sort', 'squeeze',
+    'std', 'sum', 'swapaxes', 'take', 'trace', 'transpose', 'var',
+    ]
 
 
 try:
@@ -36,6 +36,7 @@
 # save away Python sum
 _sum_ = sum
 
+
 # functions that are now methods
 def _wrapit(obj, method, *args, **kwds):
     try:
@@ -138,14 +139,15 @@ def reshape(a, newshape, order='C'):
     order : {'C', 'F', 'A'}, optional
         Read the elements of `a` using this index order, and place the elements
         into the reshaped array using this index order.  'C' means to
-        read / write the elements using C-like index order, with the last axis index
-        changing fastest, back to the first axis index changing slowest.  'F'
-        means to read / write the elements using Fortran-like index order, with
-        the first index changing fastest, and the last index changing slowest.
+        read / write the elements using C-like index order, with the last axis
+        index changing fastest, back to the first axis index changing slowest.
+        'F' means to read / write the elements using Fortran-like index order,
+        with the first index changing fastest, and the last index changing
+        slowest.
         Note that the 'C' and 'F' options take no account of the memory layout
         of the underlying array, and only refer to the order of indexing.  'A'
-        means to read / write the elements in Fortran-like index order if `a` is
-        Fortran *contiguous* in memory, C-like order otherwise.
+        means to read / write the elements in Fortran-like index order if `a`
+        is Fortran *contiguous* in memory, C-like order otherwise.
 
     Returns
     -------
@@ -167,15 +169,15 @@ def reshape(a, newshape, order='C'):
      >>> a = np.zeros((10, 2))
      # A transpose make the array non-contiguous
      >>> b = a.T
-     # Taking a view makes it possible to modify the shape without modifying the
-     # initial object.
+     # Taking a view makes it possible to modify the shape without modifying
+     # the initial object.
      >>> c = b.view()
      >>> c.shape = (20)
      AttributeError: incompatible shape for a non-contiguous array
 
     The `order` keyword gives the index ordering both for *fetching* the values
-    from `a`, and then *placing* the values into the output array.  For example,
-    let's say you have an array:
+    from `a`, and then *placing* the values into the output array.
+    For example, let's say you have an array:
 
     >>> a = np.arange(6).reshape((3, 2))
     >>> a
@@ -1102,7 +1104,8 @@ def resize(a, new_shape):
         new_shape = (new_shape,)
     a = ravel(a)
     Na = len(a)
-    if not Na: return mu.zeros(new_shape, a.dtype.char)
+    if not Na:
+        return mu.zeros(new_shape, a.dtype.char)
     total_size = um.multiply.reduce(new_shape)
     n_copies = int(total_size / Na)
     extra = total_size % Na
@@ -1114,7 +1117,7 @@ def resize(a, new_shape):
         n_copies = n_copies+1
         extra = Na-extra
 
-    a = concatenate( (a,)*n_copies)
+    a = concatenate((a,)*n_copies)
     if extra > 0:
         a = a[:-extra]
 
@@ -1268,7 +1271,7 @@ def diagonal(a, offset=0, axis1=0, axis2=1):
            [5, 7]])
 
     """
-    return asarray(a).diagonal(offset, axis1, axis2)
+    return asanyarray(a).diagonal(offset, axis1, axis2)
 
 
 def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None):
@@ -1329,6 +1332,7 @@ def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None):
     """
     return asarray(a).trace(offset, axis1, axis2, dtype, out)
 
+
 def ravel(a, order='C'):
     """
     Return a flattened array.
@@ -1346,13 +1350,14 @@ def ravel(a, order='C'):
         index the elements in C-like order, with the last axis index changing
         fastest, back to the first axis index changing slowest.   'F' means to
         index the elements in Fortran-like index order, with the first index
-        changing fastest, and the last index changing slowest. Note that the 'C'
-        and 'F' options take no account of the memory layout of the underlying
-        array, and only refer to the order of axis indexing.  'A' means to read
-        the elements in Fortran-like index order if `a` is Fortran *contiguous*
-        in memory, C-like order otherwise.  'K' means to read the elements in
-        the order they occur in memory, except for reversing the data when
-        strides are negative.  By default, 'C' index order is used.
+        changing fastest, and the last index changing slowest. Note that the
+        'C' and 'F' options take no account of the memory layout of the
+        underlying array, and only refer to the order of axis indexing.
+        'A' means to read the elements in Fortran-like index order if `a` is
+        Fortran *contiguous* in memory, C-like order otherwise.  'K' means to
+        read the elements in the order they occur in memory, except for
+        reversing the data when strides are negative.  By default, 'C' index
+        order is used.
 
     Returns
     -------
@@ -1757,14 +1762,15 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False):
             sum = a.sum
         except AttributeError:
             return _methods._sum(a, axis=axis, dtype=dtype,
-                                out=out, keepdims=keepdims)
+                                 out=out, keepdims=keepdims)
         # NOTE: Dropping the keepdims parameters here...
         return sum(axis=axis, dtype=dtype, out=out)
     else:
         return _methods._sum(a, axis=axis, dtype=dtype,
-                            out=out, keepdims=keepdims)
+                             out=out, keepdims=keepdims)
+
 
-def product (a, axis=None, dtype=None, out=None, keepdims=False):
+def product(a, axis=None, dtype=None, out=None, keepdims=False):
     """
     Return the product of array elements over a given axis.
 
@@ -1773,7 +1779,8 @@ def product (a, axis=None, dtype=None, out=None, keepdims=False):
     prod : equivalent function; see for details.
 
     """
-    return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims)
+    return um.multiply.reduce(a, axis=axis, dtype=dtype,
+                              out=out, keepdims=keepdims)
 
 
 def sometrue(a, axis=None, out=None, keepdims=False):
@@ -1794,7 +1801,8 @@ def sometrue(a, axis=None, out=None, keepdims=False):
     except TypeError:
         return arr.any(axis=axis, out=out)
 
-def alltrue (a, axis=None, out=None, keepdims=False):
+
+def alltrue(a, axis=None, out=None, keepdims=False):
     """
     Check if all elements of input array are true.
 
@@ -1810,6 +1818,7 @@ def alltrue (a, axis=None, out=None, keepdims=False):
     except TypeError:
         return arr.all(axis=axis, out=out)
 
+
 def any(a, axis=None, out=None, keepdims=False):
     """
     Test whether any array element along a given axis evaluates to True.
@@ -1890,6 +1899,7 @@ def any(a, axis=None, out=None, keepdims=False):
     except TypeError:
         return arr.any(axis=axis, out=out)
 
+
 def all(a, axis=None, out=None, keepdims=False):
     """
     Test whether all array elements along a given axis evaluate to True.
@@ -1963,7 +1973,8 @@ def all(a, axis=None, out=None, keepdims=False):
     except TypeError:
         return arr.all(axis=axis, out=out)
 
-def cumsum (a, axis=None, dtype=None, out=None):
+
+def cumsum(a, axis=None, dtype=None, out=None):
     """
     Return the cumulative sum of the elements along a given axis.
 
@@ -2179,12 +2190,13 @@ def amax(a, axis=None, out=None, keepdims=False):
             amax = a.max
         except AttributeError:
             return _methods._amax(a, axis=axis,
-                                out=out, keepdims=keepdims)
+                                  out=out, keepdims=keepdims)
         # NOTE: Dropping the keepdims parameter
         return amax(axis=axis, out=out)
     else:
         return _methods._amax(a, axis=axis,
-                            out=out, keepdims=keepdims)
+                              out=out, keepdims=keepdims)
+
 
 def amin(a, axis=None, out=None, keepdims=False):
     """
@@ -2269,12 +2281,13 @@ def amin(a, axis=None, out=None, keepdims=False):
             amin = a.min
         except AttributeError:
             return _methods._amin(a, axis=axis,
-                                out=out, keepdims=keepdims)
+                                  out=out, keepdims=keepdims)
         # NOTE: Dropping the keepdims parameter
         return amin(axis=axis, out=out)
     else:
         return _methods._amin(a, axis=axis,
-                            out=out, keepdims=keepdims)
+                              out=out, keepdims=keepdims)
+
 
 def alen(a):
     """
@@ -2399,11 +2412,12 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False):
             prod = a.prod
         except AttributeError:
             return _methods._prod(a, axis=axis, dtype=dtype,
-                                out=out, keepdims=keepdims)
+                                  out=out, keepdims=keepdims)
         return prod(axis=axis, dtype=dtype, out=out)
     else:
         return _methods._prod(a, axis=axis, dtype=dtype,
-                            out=out, keepdims=keepdims)
+                              out=out, keepdims=keepdims)
+
 
 def cumprod(a, axis=None, dtype=None, out=None):
     """
@@ -2790,7 +2804,8 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False):
             pass
 
     return _methods._mean(a, axis=axis, dtype=dtype,
-                            out=out, keepdims=keepdims)
+                          out=out, keepdims=keepdims)
+
 
 def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):
     """
@@ -2896,10 +2911,11 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False):
             pass
 
     return _methods._std(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
-                                keepdims=keepdims)
+                         keepdims=keepdims)
+
 
 def var(a, axis=None, dtype=None, out=None, ddof=0,
-                            keepdims=False):
+        keepdims=False):
     """
     Compute the variance along the specified axis.
 
@@ -3003,4 +3019,4 @@ def var(a, axis=None, dtype=None, out=None, ddof=0,
             pass
 
     return _methods._var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
-                                keepdims=keepdims)
+                         keepdims=keepdims)
diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py
--- a/numpy/lib/twodim_base.py
+++ b/numpy/lib/twodim_base.py
@@ -19,6 +19,8 @@
 i1 = iinfo(int8)
 i2 = iinfo(int16)
 i4 = iinfo(int32)
+
+
 def _min_int(low, high):
     """ get small int that fits the range """
     if high <= i1.max and low >= i1.min:
@@ -293,7 +295,7 @@ def diag(v, k=0):
            [0, 0, 8]])
 
     """
-    v = asarray(v)
+    v = asanyarray(v)
     s = v.shape
     if len(s) == 1:
         n = s[0]+abs(k)


Test patch:
diff --git a/numpy/matrixlib/tests/test_numeric.py b/numpy/matrixlib/tests/test_numeric.py
--- a/numpy/matrixlib/tests/test_numeric.py
+++ b/numpy/matrixlib/tests/test_numeric.py
@@ -2,12 +2,22 @@
 
 from numpy.testing import assert_equal, TestCase, run_module_suite
 from numpy.core import ones
-from numpy import matrix
+from numpy import matrix, diagonal, diag
 
 class TestDot(TestCase):
     def test_matscalar(self):
         b1 = matrix(ones((3, 3), dtype=complex))
         assert_equal(b1*1.0, b1)
 
+
+def test_diagonal():
+    b1 = matrix([[1,2],[3,4]])
+    diag_b1 = matrix([[1, 4]])
+
+    assert_equal(b1.diagonal(), diag_b1)
+    assert_equal(diagonal(b1), diag_b1)
+    assert_equal(diag(b1), diag_b1)
+
+
 if __name__ == "__main__":
     run_module_suite()