Skip to content

Commit

Permalink
Trac #17443: abs(matrix) should not be a shortcut for det
Browse files Browse the repository at this point in the history
We have currently
{{{
sage: M = matrix([[-1]])
sage: abs(M)
-1
}}}
Because `matrix.__abs__` is a shortcut for determinant!!I

n scipy, `__abs__` applies the absolute value to each coefficient. But
it is not likely what we want to do in Sage. Instead we raise a
`TypeError` and inform the user about `matrix.norm(1)` and
`matrix.apply_map(abs)`.

Related discussion on sage-devel
[https://groups.google.com/forum/#!topic/sage-devel/pFI9y3YZIQQ]

URL: http://trac.sagemath.org/17443
Reported by: vdelecroix
Ticket author(s): Vincent Delecroix
Reviewer(s): Nathann Cohen
  • Loading branch information
Release Manager authored and vbraun committed Dec 12, 2014
2 parents 4e8629c + e6a4de8 commit 7961b2b
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 139 deletions.
177 changes: 175 additions & 2 deletions src/sage/matrix/matrix2.pyx
Expand Up @@ -1466,17 +1466,190 @@ cdef class Matrix(matrix1.Matrix):

def __abs__(self):
"""
Synonym for self.determinant(...).
Deprecated.

This function used to return the determinant. It is deprecated since
:trac:`17443`.

EXAMPLES::

sage: a = matrix(QQ, 2,2, [1,2,3,4]); a
[1 2]
[3 4]
sage: abs(a)
doctest:...: DeprecationWarning: abs(matrix) is deprecated. Use matrix.det()to
get the determinant.
See http://trac.sagemath.org/17443 for details.
-2
"""
return self.determinant()
#TODO: after expiration of the one year deprecation, we should return a
# TypeError with the following kind of message:
# absolute value is not defined on matrices. If you want the
# L^1-norm use m.norm(1) and if you want the matrix obtained by
# applying the absolute value to the coefficents use
# m.apply_map(abs).
from sage.misc.superseded import deprecation
deprecation(17443, "abs(matrix) is deprecated. Use matrix.det()"
"to get the determinant.")
return self.det()

def apply_morphism(self, phi):
"""
Apply the morphism phi to the coefficients of this dense matrix.

The resulting matrix is over the codomain of phi.

INPUT:


- ``phi`` - a morphism, so phi is callable and
phi.domain() and phi.codomain() are defined. The codomain must be a
ring.

OUTPUT: a matrix over the codomain of phi

EXAMPLES::

sage: m = matrix(ZZ, 3, range(9))
sage: phi = ZZ.hom(GF(5))
sage: m.apply_morphism(phi)
[0 1 2]
[3 4 0]
[1 2 3]
sage: parent(m.apply_morphism(phi))
Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 5

We apply a morphism to a matrix over a polynomial ring::

sage: R.<x,y> = QQ[]
sage: m = matrix(2, [x,x^2 + y, 2/3*y^2-x, x]); m
[ x x^2 + y]
[2/3*y^2 - x x]
sage: phi = R.hom([y,x])
sage: m.apply_morphism(phi)
[ y y^2 + x]
[2/3*x^2 - y y]
"""
M = self.parent().change_ring(phi.codomain())
if self.is_sparse():
values = {(i,j): phi(z) for (i,j),z in self.dict()}
else:
values = [phi(z) for z in self.list()]
image = M(values)
if self._subdivisions is not None:
image.subdivide(*self.subdivisions())
return image

def apply_map(self, phi, R=None, sparse=None):
"""
Apply the given map phi (an arbitrary Python function or callable
object) to this dense matrix. If R is not given, automatically
determine the base ring of the resulting matrix.

INPUT:

- ``sparse`` -- True to make the output a sparse matrix; default False

- ``phi`` - arbitrary Python function or callable object

- ``R`` - (optional) ring

OUTPUT: a matrix over R

EXAMPLES::

sage: m = matrix(ZZ, 3, range(9))
sage: k.<a> = GF(9)
sage: f = lambda x: k(x)
sage: n = m.apply_map(f); n
[0 1 2]
[0 1 2]
[0 1 2]
sage: n.parent()
Full MatrixSpace of 3 by 3 dense matrices over Finite Field in a of size 3^2

In this example, we explicitly specify the codomain.

::

sage: s = GF(3)
sage: f = lambda x: s(x)
sage: n = m.apply_map(f, k); n
[0 1 2]
[0 1 2]
[0 1 2]
sage: n.parent()
Full MatrixSpace of 3 by 3 dense matrices over Finite Field in a of size 3^2

If self is subdivided, the result will be as well::

sage: m = matrix(2, 2, srange(4))
sage: m.subdivide(None, 1); m
[0|1]
[2|3]
sage: m.apply_map(lambda x: x*x)
[0|1]
[4|9]

If the matrix is sparse, the result will be as well::

sage: m = matrix(ZZ,100,100,sparse=True)
sage: m[18,32] = -6
sage: m[1,83] = 19
sage: n = m.apply_map(abs, R=ZZ)
sage: n.dict()
{(1, 83): 19, (18, 32): 6}
sage: n.is_sparse()
True

If the map sends most of the matrix to zero, then it may be useful
to get the result as a sparse matrix.

::

sage: m = matrix(ZZ, 3, 3, range(1, 10))
sage: n = m.apply_map(lambda x: 1//x, sparse=True); n
[1 0 0]
[0 0 0]
[0 0 0]
sage: n.parent()
Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring

TESTS::

sage: m = matrix([])
sage: m.apply_map(lambda x: x*x) == m
True

sage: m.apply_map(lambda x: x*x, sparse=True).parent()
Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring
"""
if self._nrows == 0 or self._ncols == 0:
if sparse is None or self.is_sparse() is sparse:
return self.__copy__()
elif sparse:
return self.sparse_matrix()
else:
return self.dense_matrix()

if self.is_sparse():
values = {(i,j): phi(v) for (i,j),v in self.dict().iteritems()}
if R is None:
R = sage.structure.sequence.Sequence(values.values()).universe()
else:
values = [phi(v) for v in self.list()]
if R is None:
R = sage.structure.sequence.Sequence(values).universe()

if sparse is None or sparse is self.is_sparse():
M = self.parent().change_ring(R)
else:
M = sage.matrix.matrix_space.MatrixSpace(R, self._nrows,
self._ncols, sparse=sparse)
image = M(values)
if self._subdivisions is not None:
image.subdivide(*self.subdivisions())
return image

def characteristic_polynomial(self, *args, **kwds):
"""
Expand Down
137 changes: 0 additions & 137 deletions src/sage/matrix/matrix_dense.pyx
Expand Up @@ -266,143 +266,6 @@ cdef class Matrix_dense(matrix.Matrix):
prod.set_unsafe(r,c,entry)
return prod

def apply_morphism(self, phi):
"""
Apply the morphism phi to the coefficients of this dense matrix.
The resulting matrix is over the codomain of phi.
INPUT:
- ``phi`` - a morphism, so phi is callable and
phi.domain() and phi.codomain() are defined. The codomain must be a
ring.
OUTPUT: a matrix over the codomain of phi
EXAMPLES::
sage: m = matrix(ZZ, 3, range(9))
sage: phi = ZZ.hom(GF(5))
sage: m.apply_morphism(phi)
[0 1 2]
[3 4 0]
[1 2 3]
sage: parent(m.apply_morphism(phi))
Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 5
We apply a morphism to a matrix over a polynomial ring::
sage: R.<x,y> = QQ[]
sage: m = matrix(2, [x,x^2 + y, 2/3*y^2-x, x]); m
[ x x^2 + y]
[2/3*y^2 - x x]
sage: phi = R.hom([y,x])
sage: m.apply_morphism(phi)
[ y y^2 + x]
[2/3*x^2 - y y]
"""
R = phi.codomain()
M = sage.matrix.matrix_space.MatrixSpace(R, self._nrows,
self._ncols, sparse=False)
image = M([phi(z) for z in self.list()])
if self._subdivisions is not None:
image.subdivide(*self.subdivisions())
return image

def apply_map(self, phi, R=None, sparse=False):
"""
Apply the given map phi (an arbitrary Python function or callable
object) to this dense matrix. If R is not given, automatically
determine the base ring of the resulting matrix.
INPUT:
sparse -- True to make the output a sparse matrix; default False
- ``phi`` - arbitrary Python function or callable
object
- ``R`` - (optional) ring
OUTPUT: a matrix over R
EXAMPLES::
sage: m = matrix(ZZ, 3, range(9))
sage: k.<a> = GF(9)
sage: f = lambda x: k(x)
sage: n = m.apply_map(f); n
[0 1 2]
[0 1 2]
[0 1 2]
sage: n.parent()
Full MatrixSpace of 3 by 3 dense matrices over Finite Field in a of size 3^2
In this example, we explicitly specify the codomain.
::
sage: s = GF(3)
sage: f = lambda x: s(x)
sage: n = m.apply_map(f, k); n
[0 1 2]
[0 1 2]
[0 1 2]
sage: n.parent()
Full MatrixSpace of 3 by 3 dense matrices over Finite Field in a of size 3^2
If self is subdivided, the result will be as well::
sage: m = matrix(2, 2, srange(4))
sage: m.subdivide(None, 1); m
[0|1]
[2|3]
sage: m.apply_map(lambda x: x*x)
[0|1]
[4|9]
If the map sends most of the matrix to zero, then it may be useful
to get the result as a sparse matrix.
::
sage: m = matrix(ZZ, 3, 3, range(1, 10))
sage: n = m.apply_map(lambda x: 1//x, sparse=True); n
[1 0 0]
[0 0 0]
[0 0 0]
sage: n.parent()
Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring
TESTS::
sage: m = matrix([])
sage: m.apply_map(lambda x: x*x) == m
True
sage: m.apply_map(lambda x: x*x, sparse=True).parent()
Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring
"""
if self._nrows==0 or self._ncols==0:
if sparse:
return self.sparse_matrix()
else:
return self.__copy__()
v = [phi(z) for z in self.list()]
if R is None:
v = sage.structure.sequence.Sequence(v)
R = v.universe()
M = sage.matrix.matrix_space.MatrixSpace(R, self._nrows,
self._ncols, sparse=sparse)
image = M(v)
if self._subdivisions is not None:
image.subdivide(*self.subdivisions())
return image

def _derivative(self, var=None, R=None):
"""
Differentiate with respect to var by differentiating each element
Expand Down

0 comments on commit 7961b2b

Please sign in to comment.