8000 Added an 'elementary' function. by mttpgn · Pull Request #4573 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

Added an 'elementary' function. #4573

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

Closed
wants to merge 10 commits into from
Next Next commit
Added an 'elementary' function.
  • Loading branch information
Matt Pagan committed Mar 31, 2014
commit f9cb6cb7305b6e62ad0d2e3f21815fd2b8676bff
116 changes: 116 additions & 0 deletions numpy/lib/twodim_base.py
10000
Original file line number Diff line number Diff line change
Expand Up @@ -998,3 +998,119 @@ def triu_indices_from(arr, k=0):
if arr.ndim != 2:
raise ValueError("input array must be 2-d")
return triu_indices(arr.shape[-2], k=k, m=arr.shape[-1])


def elementary(N, i, j=None, t=None, dtype=float):
"""
Return an array arranged like an elementary matrix.

Parameters
----------
N : int
The size of the NxN array to be returned. Elementary matrices
are be square.
Copy link
Contributor

Choose a reason for hiding this comment

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

"are square by definition", perhaps?

i : int
The index of the first row on which operations are to be
performed.
j : int
If set, the index of the second row on which operations are to
be performed.
t : scalar
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, I'd go for more descriptive parameter names. Maybe t -> scale_factor, or something.

If set, the factor by which a given row will be multiplied.

Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason not to document the dtype arg?

Returns
-------
m : ndarray of shape (NxN)
An identity array that has had a single row operation performed
on it.

See also
--------
eye, identity

Examples
-------
To swap the the first and third rows of a 4x4 identity matirx:

>>> L = elementary(4, 0, 2)
>>> L
array([[ 0., 0., 1., 0.],
[ 0., 1., 0., 0.],
[ 1., 0., 0., 0.],
[ 0., 0., 0., 1.]])

When the elementary array is multiplied by a given matrix, the
result is the matrix with its first and third rows swapped.

>>> H = np.matrix([[ 2, 3, 5, 7],
Copy link
Contributor

Choose a reason for hiding this comment

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

numpy.matrix is on the way out. Use ndarray in documentation examples.

Copy link
Author

Choose a reason for hiding this comment

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

Why is that? Link, please?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure what the official link is, but the 8000 re's precedent here: scipy/scipy#3516
Perhaps @larsmans would know if there's a definitive decision for numpy/scipy.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not a NumPy or SciPy core dev and I haven't seen any definitive decision, but I'll cite @njsmith in PEP 465 (#4351):

The result is a strong consensus among both numpy developers and developers of downstream packages that numpy.matrix should essentially never be used

which I wholeheartedly second. Arrays should be preferred because they are more flexible, and when arrays are used, using matrix as well can only cause confusion. (After working with NumPy for several years and contributing quite a few patches, I still have no idea what A * B does when one of A, B is an array and the other a matrix.)

Copy link
Author

Choose a reason for hiding this comment

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

This function's main usefulness is in the case of matrix multiplication. I can't think of any good uses for elementary() without matrix multiplication. I've added a note to the documentation clarifying that. Also an example I chose for my documentation makes clear what happens in A * B when one of A, B is an array and the other a matrix, but I take your point, that it can be confusing. Until PEP 465 gets accepted, is there a better way to do matrix multiplication than np.matrix? Should I write documentation assuming that PEP 465 has already been incorporated?

Copy link
Contributor

Choose a reason for hiding this comment

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

With arrays, the multiplication is written L.dot(H) or np.dot(L, H).

Copy link
Author

Choose a reason for hiding this comment

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

Oh, awesome. Thanks.

[11, 13, 17, 19],
[23, 29, 31, 37],
[41, 43, 47, 53]])
>>> L*H
matrix([[ 23., 29., 31., 37.],
[ 11., 13., 17., 19.],
[ 2., 3., 5., 7.],
[ 41., 43., 47., 53.]])

If the matrix is multiplied by the elementary array (i.e., the
multiplication takes place in reverse order), the result is the
given matrix with its first and third columns swapped.

>>> H*L
matrix([[ 5., 3., 2., 7.],
[ 17., 13., 11., 19.],
[ 31., 29., 23., 37.],
[ 47., 43., 41., 53.]])

To add a multiple of a matrix's second row to the same matrix's
fourth row:

>>> H
matrix([[ 2, 3, 5, 7],
[11, 13, 17, 19],
[23, 29, 31, 37],
[41, 43, 47, 53]])
>> M=elementary(4, 1, 3, 10000, int)
>> M
array([[ 1, 0, 0, 0],
[ 0, 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 10000, 0, 1]])
>> M*H
matrix([[ 2, 3, 5, 7],
[ 11, 13, 17, 19],
[ 23, 29, 31, 37],
[110041, 130043, 170047, 190053]])

To multiply only the last column of a matrix by a scalar:

>>> H
matrix([[ 2, 3, 5, 7],
[11, 13, 17, 19],
[23, 29, 31, 37],
[41, 43, 47, 53]])
>>> H*elementary(len(H), len(H)-1, t=2)
matrix([[ 2., 3., 5., 14.],
[ 11., 13., 17., 38.],
[ 23., 29., 31., 74.],
[ 41., 43., 47., 106.]])

Any nonsingular matrix can be formed by taking the product of an array
of elementary matrices.

"""
m=eye(N, dtype=dtype)
if j==None and t==None:
Copy link
Contributor

Choose a reason for hiding this comment

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

PEP8 wants spaces around operators. Also checking for None should be done with is.

raise ValueError("One or more of {0} and {1} must be set.".format( \
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the format really required here? You're interpolating string literals.

Copy link
Contributor

Choose a reason for hiding this comment

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

No, the format is not needed. It's better to report explicitly that both arguments were None.

Copy link
Author

Choose a reason for hiding this comment

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

@larsmans Good point.

'j', 't'))
elif t==None:
swap = array(m[i])
m[i] = m[j]
m[j] = swap
Copy link
Contributor

Choose a reason for hiding this comment

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

m[i], m[j] = m[j], m[i] -- no need for the temporary.

Copy link
Author

Choose a reason for hiding this comment

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

With m[i], m[j] = m[j], m[i], row i becomes row j, and then row j becomes row i which already became row j, so row j stays row j. This does not swap rows i and j by my tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, right. But what about m[[i, j]] = m[[j, i]]? That seems to work.

return m
elif j==None:
m[i] *= t
return m
else:
m[j] += (t * m[i])
return m
0