提交 a3f52604 authored 作者: Nicolas Bouchard's avatar Nicolas Bouchard 提交者: Frederic

Add hstack and vstack for sparse

上级 ca670ce6
...@@ -21,7 +21,6 @@ import theano.tests.unittest_tools as utt ...@@ -21,7 +21,6 @@ import theano.tests.unittest_tools as utt
sparse_formats = ['csc', 'csr'] sparse_formats = ['csc', 'csr']
#TODO: move this decorator to the compile submodule #TODO: move this decorator to the compile submodule
def register_specialize(lopt, *tags, **kwargs): def register_specialize(lopt, *tags, **kwargs):
compile.optdb['specialize'].register((kwargs and kwargs.pop('name')) or compile.optdb['specialize'].register((kwargs and kwargs.pop('name')) or
...@@ -493,6 +492,14 @@ csr_dmatrix = SparseType(format='csr', dtype='float64') ...@@ -493,6 +492,14 @@ csr_dmatrix = SparseType(format='csr', dtype='float64')
csc_fmatrix = SparseType(format='csc', dtype='float32') csc_fmatrix = SparseType(format='csc', dtype='float32')
csr_fmatrix = SparseType(format='csr', dtype='float32') csr_fmatrix = SparseType(format='csr', dtype='float32')
all_dtypes = SparseType.dtype_set
complex_dtypes = [t for t in all_dtypes if t[:7] == 'complex']
float_dtypes = [t for t in all_dtypes if t[:5] == 'float']
int_dtypes = [t for t in all_dtypes if t[:3] == 'int']
uint_dtypes = [t for t in all_dtypes if t[:4] == 'uint']
continuous_dtypes = complex_dtypes + float_dtypes
discrete_dtypes = int_dtypes + uint_dtypes
# CONSTRUCTION # CONSTRUCTION
class CSMProperties(gof.Op): class CSMProperties(gof.Op):
......
...@@ -2,7 +2,7 @@ import theano ...@@ -2,7 +2,7 @@ import theano
import numpy import numpy
import scipy.sparse import scipy.sparse
from theano import gof, tensor, scalar from theano import gof, tensor, scalar, sparse
from theano.tensor import blas from theano.tensor import blas
from theano.sparse.basic import ( from theano.sparse.basic import (
...@@ -93,6 +93,160 @@ def dcast(x): ...@@ -93,6 +93,160 @@ def dcast(x):
return Cast('float64')(x) return Cast('float64')(x)
class HStack(gof.op.Op):
"""Stack sparse matrices horizontally (column wise).
This wrap the method hstack from scipy.
:Parameters:
- `blocks`: Sequence of sparse array of compatible shape
- `format`: String representing the output format
- `dtype`: Output dtype
:return: the concatenation of the sparse arrays column wise.
The number of line of the sparse matrix must agree.
"""
def __init__(self, format=None, dtype=None):
if format is None:
self.format = 'csc'
else:
self.format = format
if dtype is None:
self.dtype = theano.config.floatX
else:
self.dtype = dtype
def __eq__(self, other):
return (type(self) == type(other) and
self.format == other.format and
self.dtype == other.dtype)
def __hash__(self):
return hash(type(self)) ^ hash(self.format) ^ hash(self.dtype)
def make_node(self, *mat):
if not mat:
raise ValueError('Cannot join an empty list of sparses.')
var = [as_sparse_variable(x) for x in mat]
return gof.Apply(
self, var,
[SparseType(dtype=self.dtype, format=self.format).make_variable()])
def perform(self, node, block, (out, )):
for b in block:
assert _is_sparse(b)
out[0] = scipy.sparse.hstack(block, format=self.format,
dtype=self.dtype)
def grad(self, inputs, (gz, )):
is_continuous = [(inputs[i].dtype in tensor.continuous_dtypes)
for i in range(len(inputs))]
if all(is_continuous):
if _is_sparse_variable(gz):
gz = sparse.DenseFromSparse()(gz)
split = tensor.Split(len(inputs))(gz, 1,
tensor.stack(
*[x.shape[1]
for x in inputs]))
if not isinstance(split, list):
split = [split]
return [sparse.SparseFromDense(self.format)(s) for s in split]
else:
return [None] * len(inputs)
def infer_shape(self, node, ins_shapes):
def _get(l):
return l[1]
d = sum(map(_get, ins_shapes))
return [(ins_shapes[0][0], d)]
def __str__(self):
return "%s(%s,%s)" % (self.__class__.__name__, self.format, self.dtype)
def hstack(blocks, format=None, dtype=None):
"""Stack sparse matrices horizontally (column wise).
This wrap the method hstack from scipy.
:Parameters:
- `blocks`: Sequence of sparse array of compatible shape
- `format`: String representing the output format
- `dtype`: Output dtype
:return: the concatenation of the sparse array column wise.
The number of line of the sparse matrix must agree.
"""
return HStack(format=format, dtype=dtype)(*blocks)
class VStack(HStack):
"""Stack sparse matrices vertically (row wise).
This wrap the method vstack from scipy.
:Parameters:
- `blocks`: Sequence of sparse array of compatible shape
- `format`: String representing the output format
- `dtype`: Output dtype
:return: the concatenation of the sparse arrays row wise.
The number of column of the sparse matrix must agree.
"""
def perform(self, node, block, (out, )):
for b in block:
assert _is_sparse(b)
out[0] = scipy.sparse.vstack(block, format=self.format,
dtype=self.dtype)
def grad(self, inputs, (gz, )):
is_continuous = [(inputs[i].dtype in tensor.continuous_dtypes)
for i in range(len(inputs))]
if all(is_continuous):
if _is_sparse_variable(gz):
gz = sparse.DenseFromSparse()(gz)
split = tensor.Split(len(inputs))(gz, 0,
tensor.stack(
*[x.shape[0]
for x in inputs]))
if not isinstance(split, list):
split = [split]
return [sparse.SparseFromDense(self.format)(s) for s in split]
else:
return [None] * len(inputs)
def infer_shape(self, node, ins_shapes):
def _get(l):
return l[0]
d = sum(map(_get, ins_shapes))
return [(d, ins_shapes[0][1])]
def hstack(blocks, format=None, dtype=None):
"""Stack sparse matrices vertically (row wise).
This wrap the method vstack from scipy.
:Parameters:
- `blocks`: Sequence of sparse array of compatible shape
- `format`: String representing the output format
- `dtype`: Output dtype
:return: the concatenation of the sparse array row wise.
The number of column of the sparse matrix must agree.
"""
return VStack(format=format, dtype=dtype)(*blocks)
class AddSSData(gof.op.Op): class AddSSData(gof.op.Op):
'''Add two sparse matrices assuming they have the same sparsity '''Add two sparse matrices assuming they have the same sparsity
pattern. ''' pattern. '''
......
...@@ -118,6 +118,71 @@ class TestCast(utt.InferShapeTester): ...@@ -118,6 +118,71 @@ class TestCast(utt.InferShapeTester):
verify_grad_sparse(S2.Cast('float64'), [a]) verify_grad_sparse(S2.Cast('float64'), [a])
class HVStackTester(utt.InferShapeTester):
nb = 3 # Number of sparse matrix to stack
x = {}
mat = {}
for format in sparse.sparse_formats:
variable = getattr(theano.sparse, format + '_matrix')
spa = getattr(sp, format + '_matrix')
x[format] = [variable() for t in range(nb)]
mat[format] = [spa(np.random.random_integers(5, size=(3, 4)) - 1,
dtype=theano.config.floatX)
for t in range(nb)]
def test_op(self):
for format in sparse.sparse_formats:
for out_f in sparse.sparse_formats:
for dtype in sparse.all_dtypes:
blocks = self.mat[format]
f = theano.function(
self.x[format],
self.op_class(
format=out_f, dtype=dtype)(*self.x[format]),
allow_input_downcast=True)
tested = f(*blocks)
expected = self.expected_f(blocks, format=out_f, dtype=dtype)
assert np.allclose(tested.toarray(), expected.toarray())
assert tested.format == expected.format
assert tested.dtype == expected.dtype
def test_infer_shape(self):
for format in sparse.sparse_formats:
self._compile_and_check(self.x[format],
[self.op_class()(*self.x[format])],
self.mat[format],
self.op_class)
def test_grad(self):
for format in sparse.sparse_formats:
for out_f in sparse.sparse_formats:
for dtype in ['float64']: # sparse.float_dtypes:
verify_grad_sparse(
self.op_class(format=out_f, dtype=dtype),
self.mat[format],
structured=False)
def _hv_switch(op, expected_function):
class XStackTester(HVStackTester):
op_class = op
def expected_f(self, a, format=None, dtype=None):
return expected_function(a, format, dtype)
def setUp(self):
super(XStackTester, self).setUp()
return XStackTester
HStackTester = _hv_switch(S2.HStack, sp.hstack)
VStackTester = _hv_switch(S2.VStack, sp.vstack)
class test_structured_add_s_v(unittest.TestCase): class test_structured_add_s_v(unittest.TestCase):
def setUp(self): def setUp(self):
utt.seed_rng() utt.seed_rng()
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论