r"""
Base class for dense matrices
TESTS::
sage: R.<a,b> = QQ[]
sage: m = matrix(R,2,[0,a,b,b^2])
sage: TestSuite(m).run()
"""
cimport matrix
from sage.structure.element cimport Element
import sage.matrix.matrix_space
import sage.structure.sequence
include '../ext/cdefs.pxi'
include '../ext/stdsage.pxi'
cdef class Matrix_dense(matrix.Matrix):
cdef bint is_sparse_c(self):
return 0
cdef bint is_dense_c(self):
return 1
def __copy__(self):
"""
Return a copy of this matrix. Changing the entries of the copy will
not change the entries of this matrix.
"""
A = self.new_matrix(entries=self.list(), coerce=False, copy=False)
if self._subdivisions is not None:
A.subdivide(*self.subdivisions())
return A
def __hash__(self):
"""
Return the hash of this matrix.
Equal matrices should have equal hashes, even if one is sparse and
the other is dense.
EXAMPLES::
sage: m = matrix(2, range(24), sparse=True)
sage: m.set_immutable()
sage: hash(m)
976
::
sage: d = m.dense_matrix()
sage: d.set_immutable()
sage: hash(d)
976
::
sage: hash(m) == hash(d)
True
"""
return self._hash()
cdef long _hash(self) except -1:
x = self.fetch('hash')
if not x is None: return x
if not self._mutability._is_immutable:
raise TypeError, "mutable matrices are unhashable"
v = self._list()
cdef Py_ssize_t i
cdef long h = 0
for i from 0 <= i < len(v):
h = h ^ (i * hash(v[i]))
if h == -1:
h = -2
self.cache('hash', h)
return h
cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value):
self[i][j] = value
def _pickle(self):
version = -1
data = self._list()
return data, version
def _unpickle_generic(self, data, int version):
cdef Py_ssize_t i, j, k
if version == -1:
k = 0
for i from 0 <= i < self._nrows:
for j from 0 <= j < self._ncols:
self.set_unsafe(i, j, data[k])
k = k + 1
else:
raise RuntimeError, "unknown matrix version (=%s)"%version
cdef int _cmp_c_impl(self, Element right) except -2:
"""
EXAMPLES::
sage: P.<x> = QQ[]
sage: m = matrix([[x,x+1],[1,x]])
sage: n = matrix([[x+1,x],[1,x]])
sage: o = matrix([[x,x],[1,x]])
sage: m.__cmp__(n)
-1
sage: m.__cmp__(m)
0
sage: n.__cmp__(m)
1
sage: m.__cmp__(o)
1
"""
cdef Py_ssize_t i, j
for i from 0 <= i < self._nrows:
for j from 0 <= j < self._ncols:
res = cmp( self[i,j], right[i,j] )
if res != 0:
return res
return 0
def transpose(self):
"""
Returns the transpose of self, without changing self.
EXAMPLES: We create a matrix, compute its transpose, and note that
the original matrix is not changed.
::
sage: M = MatrixSpace(QQ, 2)
sage: A = M([1,2,3,4])
sage: B = A.transpose()
sage: print B
[1 3]
[2 4]
sage: print A
[1 2]
[3 4]
``.T`` is a convenient shortcut for the transpose::
sage: A.T
[1 3]
[2 4]
::
sage: A.subdivide(None, 1); A
[1|2]
[3|4]
sage: A.transpose()
[1 3]
[---]
[2 4]
"""
(nc, nr) = (self.ncols(), self.nrows())
cdef Matrix_dense trans
trans = self.new_matrix(nrows = nc, ncols = nr,
copy=False, coerce=False)
cdef Py_ssize_t i, j
for j from 0<= j < nc:
for i from 0<= i < nr:
trans.set_unsafe(j,i,self.get_unsafe(i,j))
if self._subdivisions is not None:
row_divs, col_divs = self.subdivisions()
trans.subdivide(col_divs, row_divs)
return trans
def antitranspose(self):
"""
Returns the antitranspose of self, without changing self.
EXAMPLES::
sage: A = matrix(2,3,range(6)); A
[0 1 2]
[3 4 5]
sage: A.antitranspose()
[5 2]
[4 1]
[3 0]
::
sage: A.subdivide(1,2); A
[0 1|2]
[---+-]
[3 4|5]
sage: A.antitranspose()
[5|2]
[-+-]
[4|1]
[3|0]
"""
(nc, nr) = (self.ncols(), self.nrows())
cdef Matrix_dense atrans
atrans = self.new_matrix(nrows = nc, ncols = nr,
copy=False, coerce=False)
cdef Py_ssize_t i,j
cdef Py_ssize_t ri,rj
rj = nc
for j from 0 <= j < nc:
ri = nr
rj = rj-1
for i from 0 <= i < nr:
ri = ri-1
atrans.set_unsafe(j , i, self.get_unsafe(ri,rj))
if self._subdivisions is not None:
row_divs, col_divs = self.subdivisions()
atrans.subdivide([nc - t for t in reversed(col_divs)],
[nr - t for t in reversed(row_divs)])
return atrans
def _elementwise_product(self, right):
r"""
Returns the elementwise product of two dense
matrices with identical base rings.
This routine assumes that ``self`` and ``right``
are both matrices, both dense, with identical
sizes and with identical base rings. It is
"unsafe" in the sense that these conditions
are not checked and no sensible errors are
raised.
This routine is meant to be called from the
meth:`~sage.matrix.matrix2.Matrix.elementwise_product`
method, which will ensure that this routine receives
proper input. More thorough documentation is provided
there.
EXAMPLE::
sage: A = matrix(ZZ, 2, range(6), sparse=False)
sage: B = matrix(ZZ, 2, [1,0,2,0,3,0], sparse=False)
sage: A._elementwise_product(B)
[ 0 0 4]
[ 0 12 0]
AUTHOR:
- Rob Beezer (2009-07-14)
"""
cdef Py_ssize_t r, c
cdef Matrix_dense other, prod
nc, nr = self.ncols(), self.nrows()
other = right
prod = self.new_matrix(nr, nc, copy=False, coerce=False)
for r in range(nr):
for c in range(nc):
entry = self.get_unsafe(r,c)*other.get_unsafe(r,c)
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
with respect to var.
.. seealso::
:meth:`derivative`
EXAMPLES::
sage: m = matrix(2, [x^i for i in range(4)])
sage: m._derivative(x)
[ 0 1]
[ 2*x 3*x^2]
"""
if self._nrows==0 or self._ncols==0:
return self.__copy__()
v = [z.derivative(var) 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=False)
image = M(v)
if self._subdivisions is not None:
image.subdivide(*self.subdivisions())
return image