"""Some sparse matrix utilities missing in scipy."""
from __future__ import absolute_import
import numpy as nm
import scipy.sparse as sp
from sfepy.base.base import assert_
from six.moves import range
[docs]
def save_sparse_txt(filename, mtx, fmt='%d %d %f\n'):
"""Save a CSR/CSC sparse matrix into a text file"""
fd = open(filename, 'w')
fd.write('%d %d\n' % mtx.shape)
fd.write('%d\n' % mtx.size)
if mtx.format == 'csr':
indptr, indices, data = mtx.indptr, mtx.indices, mtx.data
for ir in range(mtx.shape[0]):
for ii in range(indptr[ir], indptr[ir+1]):
fd.write(fmt % (ir, indices[ii], data[ii]))
elif mtx.format == 'csc':
indptr, indices, data = mtx.indptr, mtx.indices, mtx.data
for ic in range(mtx.shape[0]):
for ii in range(indptr[ir], indptr[ir+1]):
fd.write(fmt % (indices[ii], ic, data[ii]))
else:
raise ValueError('matrix format not supported! (%s)' % mtx.format)
[docs]
def insert_sparse_to_csr(mtx1, mtx2, irs, ics):
"""
Insert a sparse matrix `mtx2` into a CSR sparse matrix `mtx1` at
rows `irs` and columns `ics`. The submatrix `mtx1[irs,ics]` must
already be preallocated and have the same structure as `mtx2`.
"""
import sfepy.discrete.common.extmods.assemble as asm
if isinstance(irs, slice):
irs = nm.arange(irs.start, irs.stop, irs.step, dtype=nm.int32)
if isinstance(ics, slice):
ics = nm.arange(ics.start, ics.stop, ics.step, dtype=nm.int32)
n_row, n_col = mtx1.shape
assert_((irs.min() >= 0) and (irs.max() < n_row))
assert_((ics.min() >= 0) and (ics.max() < n_col))
aux = mtx2.tocoo()
data = nm.ascontiguousarray(aux.data[:,None,None,None])
rows = irs[aux.row[:,None]]
cols = ics[aux.col[:,None]]
iels = nm.arange(rows.shape[0], dtype=nm.int32)
asm.assemble_matrix(mtx1.data, mtx1.indptr, mtx1.indices, data,
iels, 1.0, rows, cols)
def _normalize_sizes(sizes):
"""
Checks whether all the sizes are either slices or not. Transforms
slices into their sizes.
"""
out = []
ns = 0
for size in sizes:
if isinstance(size, slice):
size = size.stop - size.start
ns += 1
else:
size = int(size)
out.append(size)
if ns:
if ns != len(sizes):
raise ValueError('cannot mix sizes with slices! (%s)' % (sizes,))
is_slice = True
else:
is_slice = False
return out, is_slice
[docs]
def compose_sparse(blocks, row_sizes=None, col_sizes=None):
"""
Compose sparse matrices into a global sparse matrix.
Parameters
----------
blocks : sequence of sequences
The sequence of sequences of equal lengths - the individual
sparse matrix blocks. The integer 0 can be used to mark an all-zero
block, if its size can be determined from the other blocks.
row_sizes : sequence, optional
The required row sizes of the blocks. It can be either a
sequence of non-negative integers, or a sequence of slices with
non-negative limits. In any case the sizes have to be compatible
with the true block sizes. This allows to extend the matrix
shape as needed and to specify sizes of all-zero blocks.
col_sizes : sequence, optional
The required column sizes of the blocks. See `row_sizes`.
Returns
-------
mtx : coo_matrix
The sparse matrix (COO format) composed from the given blocks.
Examples
--------
Stokes-like problem matrix.
>>> import scipy.sparse as sp
>>> A = sp.csr_matrix([[1, 0], [0, 1]])
>>> B = sp.coo_matrix([[1, 1]])
>>> K = compose_sparse([[A, B.T], [B, 0]])
>>> print K.todense()
[[1 0 1]
[0 1 1]
[1 1 0]]
"""
if not len(blocks):
raise ValueError('no matrix blocks!')
if row_sizes is None:
row_sizes = nm.array([-1] * len(blocks))
else:
assert_(len(row_sizes) == len(blocks))
if col_sizes is None:
col_sizes = nm.array([-1] * len(blocks[0]))
else:
assert_(len(col_sizes) == len(blocks[0]))
rs, is_slice_r = _normalize_sizes(row_sizes)
cs, is_slice_c = _normalize_sizes(col_sizes)
for ir, row in enumerate(blocks):
for ic, mtx in enumerate(row):
if isinstance(mtx, int) and (mtx == 0):
continue
if ic >= len(col_sizes):
raise ValueError('invalid row size at (%d, %d)!' % (ir, ic))
if rs[ir] == -1:
rs[ir] = mtx.shape[0]
elif rs[ir] != mtx.shape[0]:
msg = 'incompatible matrix block row size at (%d, %d)!' \
% (ir, ic)
raise ValueError(msg)
if cs[ic] == -1:
cs[ic] = mtx.shape[1]
elif cs[ic] != mtx.shape[1]:
msg = 'incompatible matrix block column size at (%d, %d)!' \
% (ic, ic)
raise ValueError(msg)
if nm.any(rs == -1):
raise ValueError('incomplete row block sizes! (%s)' % row_sizes)
if nm.any(cs == -1):
raise ValueError('incomplete column block sizes! (%s)' % col_sizes)
if is_slice_r:
n_row = row_sizes[-1].stop
row_offsets = nm.r_[[ii.start for ii in row_sizes], n_row]
else:
row_offsets = nm.cumsum(nm.r_[0, rs])
n_row = row_offsets[-1]
if is_slice_c:
n_col = col_sizes[-1].stop
col_offsets = nm.r_[[ii.start for ii in col_sizes], n_col]
else:
col_offsets = nm.cumsum(nm.r_[0, cs])
n_col = col_offsets[-1]
rows = []
cols = []
datas = []
for ir, row in enumerate(blocks):
for ic, mtx in enumerate(row):
if isinstance(mtx, int) and (mtx == 0):
continue
aux = sp.coo_matrix(mtx)
rows.append(aux.row + row_offsets[ir])
cols.append(aux.col + col_offsets[ic])
datas.append(aux.data)
rows = nm.concatenate(rows)
cols = nm.concatenate(cols)
datas = nm.concatenate(datas)
mtx = sp.coo_matrix((datas, (rows, cols)), shape=(n_row, n_col))
return mtx
[docs]
def infinity_norm(mtx):
"""
Infinity norm of a sparse matrix (maximum absolute row sum).
Parameters
----------
mtx : spmatrix or array
The sparse matrix.
Returns
-------
norm : float
Infinity norm of the matrix.
Notes
-----
- This serves as an upper bound on spectral radius.
- CSR and CSC avoid copying `indices` and `indptr` arrays.
- inspired by PyAMG
See Also
--------
scipy.linalg.norm : dense matrix norms
"""
ones = nm.ones(mtx.shape[1], dtype=mtx.dtype)
if sp.isspmatrix_csr(mtx) or sp.isspmatrix_csc(mtx):
# Avoid copying index and ptr arrays.
abs_mtx = mtx.__class__((nm.abs(mtx.data), mtx.indices ,mtx.indptr),
shape=mtx.shape)
norm = (abs_mtx * ones).max()
elif sp.isspmatrix(mtx):
norm = (abs(mtx) * ones).max()
else:
norm = nm.dot(nm.abs(mtx), ones).max()
return norm