1from __future__ import absolute_import, print_function, division
2from itertools import product
3import time
4import unittest
5
6from nose.plugins.skip import SkipTest
7import numpy as np
8from six.moves import xrange
9try:
10    import scipy.sparse as sp
11    import scipy.sparse
12    from scipy.sparse import csr_matrix
13except ImportError:
14    pass  # The variable enable_sparse will be used to disable the test file.
15
16import theano
17from theano import tensor
18from theano import sparse
19from theano import compile, config, gof
20from theano.sparse import enable_sparse
21from theano.tensor.basic import _allclose
22from theano.tests.unittest_tools import attr
23
24if not enable_sparse:
25    raise SkipTest('Optional package SciPy not installed')
26
27from theano.sparse.basic import _is_dense, _is_sparse, _mtypes
28from theano.sparse.basic import _is_dense_variable, _is_sparse_variable
29from theano.sparse import (
30    as_sparse_variable, as_sparse_or_tensor_variable,
31    CSR, CSC, CSM, CSMProperties, csm_properties,
32    DenseFromSparse,
33    SparseType, CSMGrad,
34    StructuredDot,
35    StructuredDotGradCSC, StructuredDotGradCSR,
36    AddSS, AddSD, MulSS, MulSD, Transpose, Neg, Remove0,
37    add, mul, structured_dot, transpose,
38    csc_from_dense, csr_from_dense, dense_from_sparse,
39    Dot, Usmm, sp_ones_like, GetItemScalar, GetItemList, GetItem2Lists,
40    SparseFromDense,
41    Cast, cast, HStack, VStack, AddSSData, add_s_s_data,
42    structured_minimum, structured_maximum, structured_add,
43    mul_s_v, structured_add_s_v,
44    SamplingDot, sampling_dot,
45    Diag, diag, SquareDiagonal, square_diagonal,
46    EnsureSortedIndices, ensure_sorted_indices, clean,
47    ConstructSparseFromList, construct_sparse_from_list,
48    TrueDot, true_dot, eq, neq, le, ge, gt, lt)
49
50# Probability distributions are currently tested in test_sp2.py
51# from theano.sparse import (
52#    Poisson, poisson, Binomial, Multinomial, multinomial)
53
54from theano.sparse.opt import (StructuredDotCSC, UsmmCscDense, CSMGradC)
55
56from theano.tests import unittest_tools as utt
57
58
59def as_sparse_format(data, format):
60    if format == 'csc':
61        return scipy.sparse.csc_matrix(data)
62    elif format == 'csr':
63        return scipy.sparse.csr_matrix(data)
64    else:
65        raise NotImplementedError()
66
67
68def eval_outputs(outputs):
69    return compile.function([], outputs)()[0]
70
71
72# scipy 0.17 will return sparse values in all cases while previous
73# version sometimes wouldn't.  This will make everything dense so that
74# we can use assert_allclose.
75def as_ndarray(val):
76    if hasattr(val, 'toarray'):
77        return val.toarray()
78    return val
79
80
81def random_lil(shape, dtype, nnz):
82    rval = sp.lil_matrix(shape, dtype=dtype)
83    huge = 2 ** 30
84    for k in range(nnz):
85        # set non-zeros in random locations (row x, col y)
86        idx = np.random.randint(1, huge+1, size=2) % shape
87        value = np.random.rand()
88        # if dtype *int*, value will always be zeros!
89        if dtype in theano.sparse.integer_dtypes:
90            value = int(value * 100)
91        # The call to tuple is needed as scipy 0.13.1 do not support
92        # ndarray with length 2 as idx tuple.
93        rval.__setitem__(
94            tuple(idx),
95            value)
96    return rval
97
98
99def sparse_random_inputs(format, shape, n=1, out_dtype=None, p=0.5, gap=None,
100                         explicit_zero=False, unsorted_indices=False):
101    """
102    Return a tuple containing everything needed to perform a test.
103
104    If `out_dtype` is `None`, theano.config.floatX is used.
105
106    :param format: Sparse format.
107    :param shape: Shape of data.
108    :param n: Number of variable.
109    :param out_dtype: dtype of output.
110    :param p: Sparsity proportion.
111    :param gap: Tuple for the range of the random sample. When
112                length is 1, it is assumed to be the exclusive
113                max, when `gap` = (`a`, `b`) it provide a sample
114                from [a, b[. If `None` is used, it provide [0, 1]
115                for float dtypes and [0, 50[ for integer dtypes.
116    :param explicit_zero: When True, we add explicit zero in the
117                          returned sparse matrix
118    :param unsorted_indices: when True, we make sure there is
119                             unsorted indices in the returned
120                             sparse matrix.
121    :return: (variable, data) where both `variable` and `data` are list.
122
123    :note: explicit_zero and unsorted_indices was added in Theano 0.6rc4
124    """
125
126    if out_dtype is None:
127        out_dtype = theano.config.floatX
128
129    assert 0 <= p <= 1
130    assert len(shape) == 2
131    assert out_dtype in sparse.all_dtypes
132    assert gap is None or isinstance(gap, (tuple, list))
133    if gap is not None and out_dtype.startswith('u'):
134        assert gap[0] >= 0
135
136    def _rand():
137        where = np.random.binomial(1, p, size=shape).astype('int8')
138
139        if out_dtype in sparse.discrete_dtypes:
140            if not gap:
141                value = np.random.randint(50, size=shape)
142            elif len(gap) == 2:
143                value = np.random.randint(gap[0], gap[1], size=shape)
144            else:
145                value = np.random.randint(gap[0], size=shape)
146        else:
147            if not gap:
148                value = np.random.random(shape)
149            elif len(gap) == 2:
150                a, b = gap
151                value = a + np.random.random(shape) * (b - a)
152            else:
153                value = np.random.random(shape) * gap[0]
154        return (where * value).astype(out_dtype)
155
156    variable = [getattr(theano.sparse, format + '_matrix')(dtype=out_dtype)
157                for k in range(n)]
158    data = [getattr(scipy.sparse, format + '_matrix')(_rand(), dtype=out_dtype)
159            for k in range(n)]
160    if unsorted_indices:
161        for idx in range(n):
162            d = data[idx]
163            # these flip the matrix, but it's random anyway
164            if format == 'csr':
165                d = scipy.sparse.csr_matrix(
166                    (d.data, d.shape[1] - 1 - d.indices, d.indptr),
167                    shape=d.shape)
168            if format == 'csc':
169                d = scipy.sparse.csc_matrix(
170                    (d.data, d.shape[0] - 1 - d.indices, d.indptr),
171                    shape=d.shape)
172            assert not d.has_sorted_indices
173            data[idx] = d
174    if explicit_zero:
175        for idx in range(n):
176            assert data[idx].nnz > 1, (
177                "can't make a sparse matrix with explicit 0")
178            d_idx = np.random.randint(data[idx].nnz)
179            data[idx].data[d_idx] = 0
180
181    # numpy 1.5.0 with scipy 0.9.0 have scipy.sparse.XXX_matrix return
182    # typenum 10(ulonglong) instead of 8(uint64) event if they are the same!
183    # Theano don't like ulonglong type_num
184    dtype = np.dtype(out_dtype)  # Convert into dtype object.
185    if data[0].dtype.num != dtype.num and dtype.str == data[0].dtype.str:
186        data[0].data = theano._asarray(data[0].data, out_dtype)
187    assert data[0].dtype.num == dtype.num
188    return (variable, data)
189
190
191def verify_grad_sparse(op, pt, structured=False, *args, **kwargs):
192    """
193    Wrapper for theano.test.unittest_tools.py:verify_grad which
194    converts sparse variables back and forth.
195
196    Parameters
197    ----------
198    op
199        Op to check.
200    pt
201        List of inputs to realize the tests.
202    structured
203        True to tests with a structured grad, False otherwise.
204    args
205        Other `verify_grad` parameters if any.
206    kwargs
207        Other `verify_grad` keywords if any.
208
209    Returns
210    -------
211    None
212    """
213
214    def conv_none(x):
215        return x
216
217    def conv_csr(ind, indptr, shp):
218        def f(spdata):
219            return CSR(spdata, ind, indptr, shp)
220        return f
221
222    def conv_csc(ind, indptr, shp):
223        def f(spdata):
224            return CSC(spdata, ind, indptr, shp)
225        return f
226
227    iconv = []
228    dpt = []
229
230    for p in pt:
231        if _is_sparse(p):
232            if structured:
233                dpt.append(p.data)
234            else:
235                dpt.append(p.toarray())
236            if p.format == 'csr':
237                if structured:
238                    iconv.append(conv_csr(p.indices[:p.size], p.indptr,
239                                          p.shape))
240                else:
241                    iconv.append(csr_from_dense)
242            elif p.format == 'csc':
243                if structured:
244                    iconv.append(conv_csc(p.indices[:p.size], p.indptr,
245                                          p.shape))
246                else:
247                    iconv.append(csc_from_dense)
248            else:
249                raise NotImplementedError("No conv for %s" % (p.format,))
250        else:
251            dpt.append(p)
252            iconv.append(conv_none)
253    output = op(*[as_sparse_or_tensor_variable(p) for p in pt])
254    if isinstance(output, (list, tuple)):
255        raise NotImplementedError("verify_grad can't deal with "
256                                  "multiple outputs")
257    if _is_sparse_variable(output):
258        oconv = DenseFromSparse(structured=structured)
259    else:
260        oconv = conv_none
261
262    def conv_op(*inputs):
263        ipt = [conv(i) for i, conv in zip(inputs, iconv)]
264        out = op(*ipt)
265        return oconv(out)
266
267    return utt.verify_grad(conv_op, dpt, *args, **kwargs)
268verify_grad_sparse.E_grad = utt.verify_grad.E_grad
269
270
271class T_verify_grad_sparse(unittest.TestCase):
272    class FailOp(gof.op.Op):
273        def __init__(self, structured):
274            self.structured = structured
275
276        def __eq__(self, other):
277            return (type(self) == type(other)) and \
278                self.structured == other.structured
279
280        def __hash__(self):
281            return hash(type(self)) ^ hash(self.structured)
282
283        def make_node(self, x):
284            x = as_sparse_variable(x)
285            return gof.Apply(self, [x], [x.type()])
286
287        def perform(self, node, inputs, outputs):
288            (x,) = inputs
289            (out,) = outputs
290            assert _is_sparse(x)
291            out[0] = -x
292
293        def grad(self, inputs, gout):
294            (x,) = inputs
295            (gz,) = gout
296            assert _is_sparse_variable(x) and _is_sparse_variable(gz)
297            if self.structured:
298                return sp_ones_like(x) * dense_from_sparse(gz),
299            else:
300                return gz,
301
302        def infer_shape(self, node, shapes):
303            return [shapes[0]]
304
305    def test_grad_fail(self):
306        self.assertRaises(verify_grad_sparse.E_grad,
307                          verify_grad_sparse,
308                          self.FailOp(structured=False),
309                          [sp.csr_matrix(random_lil((10, 40),
310                                                    config.floatX, 3))])
311
312        self.assertRaises(verify_grad_sparse.E_grad,
313                          verify_grad_sparse,
314                          self.FailOp(structured=True),
315                          [sp.csr_matrix(random_lil((10, 40),
316                                                    config.floatX, 3))])
317
318
319class T_transpose(unittest.TestCase):
320    def setUp(self):
321        utt.seed_rng()
322
323    def test_transpose_csc(self):
324        sp = scipy.sparse.csc_matrix(scipy.sparse.eye(5, 3))
325        a = as_sparse_variable(sp)
326        self.assertFalse(a.data is sp)
327        self.assertTrue(a.data.shape == (5, 3))
328        self.assertTrue(a.type.dtype == 'float64', a.type.dtype)
329        self.assertTrue(a.type.format == 'csc', a.type.format)
330        ta = transpose(a)
331        self.assertTrue(ta.type.dtype == 'float64', ta.type.dtype)
332        self.assertTrue(ta.type.format == 'csr', ta.type.format)
333
334        vta = eval_outputs([ta])
335        self.assertTrue(vta.shape == (3, 5))
336
337    def test_transpose_csr(self):
338        a = as_sparse_variable(scipy.sparse.csr_matrix(scipy.sparse.eye(5, 3)))
339        self.assertTrue(a.data.shape == (5, 3))
340        self.assertTrue(a.type.dtype == 'float64')
341        self.assertTrue(a.type.format == 'csr')
342        ta = transpose(a)
343        self.assertTrue(ta.type.dtype == 'float64', ta.type.dtype)
344        self.assertTrue(ta.type.format == 'csc', ta.type.format)
345
346        vta = eval_outputs([ta])
347        self.assertTrue(vta.shape == (3, 5))
348
349
350class SparseInferShapeTester(utt.InferShapeTester):
351    def test_getitem_2d(self):
352        raise SkipTest('infer_shape not implemented for GetItem2d yet')
353
354    def test_getitem_scalar(self):
355        x = SparseType('csr', dtype=config.floatX)()
356        self._compile_and_check([x],
357                                [x[2, 2]],
358                                [sp.csr_matrix(random_lil((10, 40),
359                                               config.floatX, 3))],
360                                GetItemScalar)
361
362    def test_csm(self):
363        for sparsetype in ('csr', 'csc'):
364            x = tensor.vector()
365            y = tensor.ivector()
366            z = tensor.ivector()
367            s = tensor.ivector()
368            call = getattr(sp, sparsetype + '_matrix')
369            spm = call(random_lil((300, 400), config.floatX, 5))
370            out = CSM(sparsetype)(x, y, z, s)
371            self._compile_and_check([x, y, z, s],
372                                    [out],
373                                    [spm.data, spm.indices, spm.indptr,
374                                     spm.shape],
375                                    CSM
376            )
377
378    def test_csm_grad(self):
379        for sparsetype in ('csr', 'csc'):
380            x = tensor.vector()
381            y = tensor.ivector()
382            z = tensor.ivector()
383            s = tensor.ivector()
384            call = getattr(sp, sparsetype + '_matrix')
385            spm = call(random_lil((300, 400), config.floatX, 5))
386            out = tensor.grad(dense_from_sparse(
387                CSM(sparsetype)(x, y, z, s)
388            ).sum(), x)
389            self._compile_and_check([x, y, z, s],
390                                    [out],
391                                    [spm.data, spm.indices, spm.indptr,
392                                     spm.shape],
393                                    (CSMGrad, CSMGradC)
394                                   )
395
396    def test_transpose(self):
397        x = SparseType('csr', dtype=config.floatX)()
398        self._compile_and_check([x],
399                                [x.T],
400                                [sp.csr_matrix(random_lil((10, 40),
401                                               config.floatX, 3))],
402                                Transpose)
403
404    def test_neg(self):
405        x = SparseType('csr', dtype=config.floatX)()
406        self._compile_and_check([x],
407                                [-x],
408                                [sp.csr_matrix(random_lil((10, 40),
409                                               config.floatX, 3))],
410                                Neg)
411
412    def test_add_ss(self):
413        x = SparseType('csr', dtype=config.floatX)()
414        y = SparseType('csr', dtype=config.floatX)()
415        self._compile_and_check([x, y],
416                                [x + y],
417                                [sp.csr_matrix(random_lil((10, 40),
418                                               config.floatX, 3)),
419                                 sp.csr_matrix(random_lil((10, 40),
420                                               config.floatX, 3))],
421                                AddSS)
422
423    def test_add_sd(self):
424        x = SparseType('csr', dtype=config.floatX)()
425        y = tensor.matrix()
426        self._compile_and_check(
427                [x, y],
428                [x + y],
429                [sp.csr_matrix(random_lil((10, 40),
430                               config.floatX, 3)),
431                 np.random.randn(10, 40).astype(config.floatX)],
432                (AddSD, sparse.opt.AddSD_ccode))
433
434    def test_mul_ss(self):
435        x = SparseType('csr', dtype=config.floatX)()
436        y = SparseType('csr', dtype=config.floatX)()
437        self._compile_and_check([x, y],
438                                [x * y],
439                                [sp.csr_matrix(random_lil((10, 40),
440                                               config.floatX, 3)),
441                                ] * 2,
442                                MulSS)
443
444    def test_mul_sd(self):
445        x = SparseType('csr', dtype=config.floatX)()
446        y = tensor.matrix()
447        self._compile_and_check(
448                [x, y],
449                [x * y],
450                [sp.csr_matrix(random_lil((10, 40),
451                               config.floatX, 3)),
452                 np.random.randn(10, 40).astype(config.floatX)],
453                MulSD, excluding=["local_mul_s_d"])
454
455    def test_remove0(self):
456        x = SparseType('csr', dtype=config.floatX)()
457        self._compile_and_check([x],
458                                [Remove0()(x)],
459                                [sp.csr_matrix(random_lil((10, 40),
460                                               config.floatX, 3))],
461                                Remove0)
462
463    def test_dot(self):
464        x = SparseType('csc', dtype=config.floatX)()
465        y = SparseType('csc', dtype=config.floatX)()
466        self._compile_and_check(
467                [x, y],
468                [Dot()(x, y)],
469                [sp.csc_matrix(random_lil((4, 5),
470                               config.floatX, 3)),
471                 sp.csc_matrix(random_lil((5, 3),
472                               config.floatX, 3))],
473                Dot)
474
475    def test_dot_broadcast(self):
476        for x, y in [
477                (SparseType('csr', 'float32')(), tensor.vector()[:, None]),
478                (SparseType('csr', 'float32')(), tensor.vector()[None, :]),
479                (SparseType('csr', 'float32')(), tensor.matrix()),
480                (tensor.vector()[:, None], SparseType('csr', 'float32')()),
481                (tensor.vector()[None, :], SparseType('csr', 'float32')()),
482                (tensor.matrix(), SparseType('csr', 'float32')())]:
483
484            sparse_out = theano.dot(x, y)
485            if isinstance(x, sparse.SparseVariable):
486                x = tensor.matrix()
487            if isinstance(y, sparse.SparseVariable):
488                y = tensor.matrix()
489            dense_out = tensor.dot(x, y)
490            assert dense_out.broadcastable == sparse_out.broadcastable
491
492    def test_structured_dot(self):
493        x = SparseType('csc', dtype=config.floatX)()
494        y = SparseType('csc', dtype=config.floatX)()
495        self._compile_and_check(
496                [x, y],
497                [structured_dot(x, y)],
498                [sp.csc_matrix(random_lil((4, 5),
499                               config.floatX, 3)),
500                 sp.csc_matrix(random_lil((5, 3),
501                               config.floatX, 3))],
502                StructuredDot)
503
504    def test_structured_dot_grad(self):
505        # We also need the grad of CSM to be implemetned.
506        raise SkipTest('infer_shape not implemented for the grad'
507                       ' of structured_dot')
508        for format, op in [('csc', StructuredDotGradCSC),
509                           ('csr', StructuredDotGradCSR)]:
510            x = SparseType(format, dtype=config.floatX)()
511            y = SparseType(format, dtype=config.floatX)()
512            grads = tensor.grad(dense_from_sparse(structured_dot(x, y)).sum(),
513                                [x, y])
514            self._compile_and_check(
515                    [x, y],
516                    [grads[0]],
517                    [as_sparse_format(random_lil((4, 5),
518                                                 config.floatX, 3), format),
519                     as_sparse_format(random_lil((5, 3),
520                                                 config.floatX, 3), format)],
521                    op)
522            self._compile_and_check(
523                    [x, y],
524                    [grads[1]],
525                    [as_sparse_format(random_lil((4, 5),
526                                                 config.floatX, 3), format),
527                     as_sparse_format(random_lil((5, 3),
528                                                 config.floatX, 3), format)],
529                    op)
530
531    def test_dense_from_sparse(self):
532        x = SparseType('csr', dtype=config.floatX)()
533        self._compile_and_check([x],
534                                [dense_from_sparse(x)],
535                                [sp.csr_matrix(random_lil((10, 40),
536                                               config.floatX, 3))],
537                                dense_from_sparse.__class__)
538
539    def test_sparse_from_dense(self):
540        x = tensor.matrix()
541        self._compile_and_check([x],
542                                [csc_from_dense(x)],
543                                [np.random.randn(10, 40).astype(
544                                    config.floatX)],
545                                csc_from_dense.__class__)
546
547    def test_sparse_from_list(self):
548        x = tensor.matrix('x')
549        vals = tensor.matrix('vals')
550        ilist = tensor.lvector('ilist')
551
552        out = construct_sparse_from_list(x, vals, ilist)
553        self._compile_and_check(
554                [x, vals, ilist],
555                [out],
556                [np.zeros((40, 10), dtype=config.floatX),
557                 np.random.randn(12, 10).astype(config.floatX),
558                 np.random.randint(low=0, high=40, size=(12,))],
559                ConstructSparseFromList
560                )
561
562
563class TestConstructSparseFromList(unittest.TestCase):
564    def test_adv_sub1_sparse_grad(self):
565        v = theano.tensor.ivector()
566
567        # Assert we don't create a sparse grad by default
568        m = theano.tensor.matrix()
569        sub = m[v]
570        g = theano.grad(sub.sum(), m)
571        assert isinstance(g.owner.op, tensor.AdvancedIncSubtensor1)
572
573        # Test that we create a sparse grad when asked
574        # USER INTERFACE
575        m = theano.tensor.matrix()
576        v = theano.tensor.ivector()
577        sub = theano.sparse_grad(m[v])
578        g = theano.grad(sub.sum(), m)
579        assert isinstance(g.owner.op, ConstructSparseFromList)
580
581        # Test that we create a sparse grad when asked
582        # Op INTERFACE
583        m = theano.tensor.matrix()
584        v = theano.tensor.ivector()
585        sub = theano.tensor.AdvancedSubtensor1(sparse_grad=True)(m, v)
586        g = theano.grad(sub.sum(), m)
587        assert isinstance(g.owner.op, ConstructSparseFromList)
588
589        # Test the sparse grad
590        valm = np.random.rand(5, 4).astype(config.floatX)
591        valv = np.random.randint(0, 5, 10)
592        m = theano.tensor.matrix()
593        shared_v = theano.shared(valv)
594
595        def fn(m):
596            return theano.sparse_grad(m[shared_v])
597        verify_grad_sparse(fn, [valm])
598
599    def test_err(self):
600        for ndim in [1, 3]:
601            t = theano.tensor.TensorType(dtype=config.floatX,
602                                         broadcastable=(False,) * ndim)()
603            v = theano.tensor.ivector()
604            sub = t[v]
605
606            # Assert we don't create a sparse grad by default
607            g = theano.grad(sub.sum(), t)
608            assert isinstance(g.owner.op, tensor.AdvancedIncSubtensor1)
609
610            # Test that we raise an error, as we can't create a sparse
611            # grad from tensors that don't have 2 dimensions.
612            sub = theano.sparse_grad(sub)
613            self.assertRaises(TypeError, theano.grad, sub.sum(), t)
614
615
616class T_AddMul(unittest.TestCase):
617    def testAddSS(self):
618        self._testSS(add)
619
620    def testAddSD(self):
621        self._testSD(add)
622
623    def testAddDS(self):
624        self._testDS(add)
625
626    def testMulSS(self):
627        self._testSS(mul,
628                     np.array([[1., 0], [3, 0], [0, 6]]),
629                     np.array([[1., 2], [3, 0], [0, 6]]))
630
631    def testMulSD(self):
632        self._testSD(mul,
633                     np.array([[1., 0], [3, 0], [0, 6]]),
634                     np.array([[1., 2], [3, 0], [0, 6]]))
635
636    def testMulDS(self):
637        self._testDS(mul,
638                     np.array([[1., 0], [3, 0], [0, 6]]),
639                     np.array([[1., 2], [3, 0], [0, 6]]))
640
641    def _testSS(self, op, array1=np.array([[1., 0], [3, 0], [0, 6]]),
642                array2=np.asarray([[0, 2.], [0, 4], [5, 0]])):
643        for mtype1, mtype2 in product(_mtypes, _mtypes):
644            for dtype1, dtype2 in [('float64', 'int8'),
645                                   ('int8', 'float64'),
646                                   ('float64', 'float64'),
647                               ]:
648                a = mtype1(array1).astype(dtype1)
649                aR = as_sparse_variable(a)
650                self.assertFalse(aR.data is a)
651                self.assertTrue(_is_sparse(a))
652                self.assertTrue(_is_sparse_variable(aR))
653
654                b = mtype2(array2).astype(dtype2)
655                bR = as_sparse_variable(b)
656                self.assertFalse(bR.data is b)
657                self.assertTrue(_is_sparse(b))
658                self.assertTrue(_is_sparse_variable(bR))
659
660                apb = op(aR, bR)
661                self.assertTrue(_is_sparse_variable(apb))
662
663                self.assertTrue(apb.type.format == aR.type.format, apb.type.format)
664
665                val = eval_outputs([apb])
666                self.assertTrue(val.shape == (3, 2))
667                if op is add:
668                    self.assertTrue(np.all(val.todense() == (array1 + array2)))
669                    if dtype1.startswith('float') and dtype2.startswith('float'):
670                        verify_grad_sparse(op, [a, b], structured=False)
671                elif op is mul:
672                    self.assertTrue(np.all(val.todense()
673                                              == (array1 * array2)))
674                    if dtype1.startswith('float') and dtype2.startswith('float'):
675                        verify_grad_sparse(op, [a, b], structured=False)
676
677    def _testSD(self, op, array1=np.array([[1., 0], [3, 0], [0, 6]]),
678                array2=np.asarray([[0, 2.], [0, 4], [5, 0]])):
679        for mtype in _mtypes:
680            for a in [np.array(array1), tensor.as_tensor_variable(array1),
681                      theano.shared(array1)]:
682                for dtype1, dtype2 in [('float64', 'int8'),
683                                       ('int8', 'float64'),
684                                       # Needed to test the grad
685                                       ('float32', 'float64'),
686                                   ]:
687                    a = a.astype(dtype1)
688                    b = mtype(array2).astype(dtype2)
689                    bR = as_sparse_variable(b)
690                    self.assertFalse(bR.data is b)  # constants are copied
691                    self.assertTrue(_is_sparse(b))
692                    self.assertTrue(_is_sparse_variable(bR))
693
694                    apb = op(a, bR)
695
696                    val = eval_outputs([apb])
697                    self.assertTrue(val.shape == (3, 2))
698                    if op is add:
699                        self.assertTrue(_is_dense_variable(apb))
700                        self.assertTrue(np.all(val == (array1 + b)))
701                        ans = np.array([[1., 2], [3, 4], [5, 6]])
702                        self.assertTrue(np.all(val == ans))
703                        if isinstance(a, theano.Constant):
704                            a = a.data
705                        if getattr(a, 'owner', None):
706                            continue
707                        if dtype1.startswith('float') and dtype2.startswith('float'):
708                            verify_grad_sparse(op, [a, b], structured=True)
709                    elif op is mul:
710                        self.assertTrue(_is_sparse_variable(apb))
711                        self.assertTrue(np.all(val.todense() == (b.multiply(array1))))
712                        self.assertTrue(np.all(val.todense() == np.array(
713                            [[1, 0], [9, 0], [0, 36]])))
714                        if isinstance(a, theano.Constant):
715                            a = a.data
716                        if getattr(a, 'owner', None):
717                            continue
718                        if dtype1.startswith('float') and dtype2.startswith('float'):
719                            verify_grad_sparse(op, [a, b], structured=False)
720
721    def _testDS(self, op, array1=np.array([[1., 0], [3, 0], [0, 6]]),
722                array2=np.asarray([[0, 2.], [0, 4], [5, 0]])):
723        for mtype in _mtypes:
724            for b in [np.asarray(array2),
725                      tensor.as_tensor_variable(array2),
726                      theano.shared(array2)]:
727                for dtype1, dtype2 in [('float64', 'int8'),
728                                       ('int8', 'float64'),
729                                   ]:
730                    a = mtype(array1).astype(dtype1)
731                    aR = as_sparse_variable(a)
732                    self.assertFalse(aR.data is a)
733                    self.assertTrue(_is_sparse(a))
734                    self.assertTrue(_is_sparse_variable(aR))
735                    b = b.astype(dtype2)
736
737                    apb = op(aR, b)
738
739                    val = eval_outputs([apb])
740                    self.assertTrue(val.shape == (3, 2))
741                    if op is add:
742                        self.assertTrue(_is_dense_variable(apb))
743                        self.assertTrue(np.all(val == (a + array2)))
744                        ans = np.array([[1., 2], [3, 4], [5, 6]])
745                        self.assertTrue(np.all(val == ans))
746                        if isinstance(b, theano.Constant):
747                            b = b.data
748                        if dtype1.startswith('float') and dtype2.startswith('float'):
749                            verify_grad_sparse(op, [a, b], structured=True)
750                    elif op is mul:
751                        self.assertTrue(_is_sparse_variable(apb))
752                        ans = np.array([[1, 0], [9, 0], [0, 36]])
753                        self.assertTrue(np.all(val.todense() == (a.multiply(array2))))
754                        self.assertTrue(np.all(val.todense() == ans))
755                        if isinstance(b, theano.Constant):
756                            b = b.data
757                        if dtype1.startswith('float') and dtype2.startswith('float'):
758                            verify_grad_sparse(op, [a, b], structured=False)
759
760
761class test_comparison(unittest.TestCase):
762    def setUp(self):
763        utt.seed_rng()
764
765    # took from tensor basic_test.py
766    def _rand_ranged(self, min, max, shape):
767        return np.asarray(np.random.rand(*shape) * (max - min) + min,
768                         dtype=config.floatX)
769
770    tests = [lambda x, y: x > y, lambda x, y: x < y,
771                lambda x, y: x >= y, lambda x, y: x <= y]
772
773    testsDic = {gt: lambda x, y: x > y, lt: lambda x, y: x < y,
774                ge: lambda x, y: x >= y, le: lambda x, y: x <= y}
775
776    def __generalized_ss_test(self, theanop, symbolicType, testOp, scipyType):
777
778        scipy_ver = [int(n) for n in scipy.__version__.split('.')[:2]]
779
780        if (bool(scipy_ver < [0, 13])):
781            raise SkipTest("comparison operators need newer release of scipy")
782
783        x = symbolicType()
784        y = symbolicType()
785
786        op = theanop(x, y)
787
788        f = theano.function([x, y], op)
789
790        m1 = scipyType(random_lil((10, 40), config.floatX, 3))
791        m2 = scipyType(random_lil((10, 40), config.floatX, 3))
792
793        self.assertTrue(np.array_equal(f(m1, m2).data, testOp(m1, m2).data))
794
795    def __generalized_sd_test(self, theanop, symbolicType, testOp, scipyType):
796
797        scipy_ver = [int(n) for n in scipy.__version__.split('.')[:2]]
798
799        if (bool(scipy_ver < [0, 13])):
800            raise SkipTest("comparison operators need newer release of scipy")
801
802        x = symbolicType()
803        y = theano.tensor.matrix()
804
805        op = theanop(x, y)
806
807        f = theano.function([x, y], op)
808
809        m1 = scipyType(random_lil((10, 40), config.floatX, 3))
810        m2 = self._rand_ranged(1000, -1000, [10, 40])
811
812        self.assertTrue(np.array_equal(f(m1, m2).data, testOp(m1, m2).data))
813
814    def __generalized_ds_test(self, theanop, symbolicType, testOp, scipyType):
815
816        scipy_ver = [int(n) for n in scipy.__version__.split('.')[:2]]
817
818        if (bool(scipy_ver < [0, 13])):
819            raise SkipTest("comparison operators need newer release of scipy")
820
821        x = symbolicType()
822        y = theano.tensor.matrix()
823
824        op = theanop(y, x)
825
826        f = theano.function([y, x], op)
827
828        m1 = scipyType(random_lil((10, 40), config.floatX, 3))
829        m2 = self._rand_ranged(1000, -1000, [10, 40])
830
831        self.assertTrue(np.array_equal(f(m2, m1).data, testOp(m2, m1).data))
832
833    def test_ss_csr_comparison(self):
834
835        for op in self.tests:
836            self.__generalized_ss_test(op, sparse.csr_matrix,
837                              op, sp.csr_matrix)
838
839    def test_ss_csc_comparison(self):
840
841        for op in self.tests:
842            self.__generalized_ss_test(op, sparse.csc_matrix,
843                              op, sp.csc_matrix)
844
845    def test_sd_csr_comparison(self):
846
847        for op in self.tests:
848            self.__generalized_sd_test(op, sparse.csr_matrix,
849                              op, sp.csr_matrix)
850
851    def test_sd_csc_comparison(self):
852
853        for op in self.tests:
854            self.__generalized_sd_test(op, sparse.csc_matrix,
855                              op, sp.csc_matrix)
856
857    def test_ds_csc_comparison(self):
858
859        for op in self.testsDic:
860            self.__generalized_ds_test(op, sparse.csc_matrix,
861                              self.testsDic[op], sp.csc_matrix)
862
863    def test_ds_csr_comparison(self):
864
865        for op in self.testsDic:
866            self.__generalized_ds_test(op, sparse.csr_matrix,
867                              self.testsDic[op], sp.csr_matrix)
868
869    def test_equality_case(self):
870        # Test assuring normal behaviour when values
871        # in the matrices are equal
872
873        scipy_ver = [int(n) for n in scipy.__version__.split('.')[:2]]
874
875        if (bool(scipy_ver < [0, 13])):
876            raise SkipTest("comparison operators need newer release of scipy")
877
878        x = sparse.csc_matrix()
879        y = theano.tensor.matrix()
880
881        m1 = sp.csc_matrix((2, 2), dtype=theano.config.floatX)
882        m2 = np.asarray([[0, 0], [0, 0]], dtype=theano.config.floatX)
883
884        for func in self.testsDic:
885
886            op = func(y, x)
887            f = theano.function([y, x], op)
888
889            self.assertTrue(np.array_equal(f(m2, m1),
890                                              self.testsDic[func](m2, m1)))
891
892
893class T_conversion(unittest.TestCase):
894    def setUp(self):
895        utt.seed_rng()
896
897    if 0:
898        def test0(self):
899            a = tensor.as_tensor_variable(np.random.rand(5))
900            s = csc_from_dense(a)
901            val = eval_outputs([s])
902            self.assertTrue(str(val.dtype) == 'float64')
903            self.assertTrue(val.format == 'csc')
904
905    if 0:
906        def test1(self):
907            a = tensor.as_tensor_variable(np.random.rand(5))
908            s = csr_from_dense(a)
909            val = eval_outputs([s])
910            self.assertTrue(str(val.dtype) == 'float64')
911            self.assertTrue(val.format == 'csr')
912
913    def test_dense_from_sparse(self):
914        # call dense_from_sparse
915        for t in _mtypes:
916            s = t(scipy.sparse.identity(5))
917            s = as_sparse_variable(s)
918            d = dense_from_sparse(s)
919            val = eval_outputs([d])
920            self.assertTrue(str(val.dtype) == s.dtype)
921            self.assertTrue(np.all(val[0] == [1, 0, 0, 0, 0]))
922
923    def test_todense(self):
924        # call sparse_var.todense()
925        for t in _mtypes:
926            s = t(scipy.sparse.identity(5))
927            s = as_sparse_variable(s)
928            d = s.toarray()
929            val = eval_outputs([d])
930            self.assertTrue(str(val.dtype) == s.dtype)
931            self.assertTrue(np.all(val[0] == [1, 0, 0, 0, 0]))
932
933    @staticmethod
934    def check_format_ndim(format, ndim):
935        x = tensor.tensor(
936                dtype=config.floatX,
937                broadcastable=([False] * ndim),
938                name='x')
939
940        s = SparseFromDense(format)(x)
941        s_m = - s
942        d = dense_from_sparse(s_m)
943        c = d.sum()
944        g = tensor.grad(c, x)
945        f = theano.function([x], [s, g])
946        f(np.array(0, dtype=config.floatX, ndmin=ndim))
947        f(np.array(7, dtype=config.floatX, ndmin=ndim))
948
949    def test_format_ndim(self):
950        for format in 'csc', 'csr':
951            for ndim in 0, 1, 2:
952                self.check_format_ndim(format, ndim)
953
954            self.assertRaises(TypeError, self.check_format_ndim, format, 3)
955            self.assertRaises(TypeError, self.check_format_ndim, format, 4)
956
957
958class test_csm_properties(unittest.TestCase):
959    def setUp(self):
960        utt.seed_rng()
961
962    def test_csm_properties_grad(self):
963        sp_types = {'csc': sp.csc_matrix,
964                    'csr': sp.csr_matrix}
965
966        for format in ['csc', 'csr']:
967            for dtype in ['float32', 'float64']:
968                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
969
970                verify_grad_sparse(lambda *x: CSMProperties()(*x)[0], [spmat],
971                                   structured=True)
972
973                verify_grad_sparse(lambda *x: CSMProperties()(*x)[1], [spmat],
974                                   structured=True)
975
976                verify_grad_sparse(lambda *x: CSMProperties()(*x)[2], [spmat],
977                                   structured=True)
978
979                verify_grad_sparse(lambda *x: CSMProperties()(*x)[2], [spmat],
980                                   structured=True)
981
982    def test_csm_properties(self):
983        sp_types = {'csc': sp.csc_matrix,
984                    'csr': sp.csr_matrix}
985
986        for format in ['csc', 'csr']:
987            for dtype in ['float32', 'float64']:
988                x = SparseType(format, dtype=dtype)()
989                f = theano.function([x], csm_properties(x))
990
991                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
992
993                data, indices, indptr, shape = f(spmat)
994
995                assert np.all(data == spmat.data)
996                assert np.all(indices == spmat.indices)
997                assert np.all(indptr == spmat.indptr)
998                assert np.all(shape == spmat.shape)
999
1000
1001class test_csm(unittest.TestCase):
1002    def setUp(self):
1003        utt.seed_rng()
1004
1005    def test_csm_grad(self):
1006        sp_types = {'csc': sp.csc_matrix,
1007                    'csr': sp.csr_matrix}
1008
1009        for format in ['csc', 'csr']:
1010            for dtype in ['float32', 'float64']:
1011                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
1012
1013                verify_grad_sparse(lambda x: CSM(format)(x, spmat.indices,
1014                    spmat.indptr, np.asarray(spmat.shape, 'int32')),
1015                    [spmat.data], structured=True)
1016
1017    def test_csm_sparser(self):
1018        # Test support for gradients sparser than the input.
1019
1020        sp_types = {'csc': sp.csc_matrix,
1021                    'csr': sp.csr_matrix}
1022
1023        for format in ['csc', 'csr']:
1024            for dtype in ['float32', 'float64']:
1025                x = tensor.tensor(dtype=dtype, broadcastable=(False,))
1026                y = tensor.ivector()
1027                z = tensor.ivector()
1028                s = tensor.ivector()
1029
1030                a = as_sparse_variable(sp_types[format](random_lil((4, 3),
1031                                                                   dtype, 1)))
1032
1033                f = theano.function([x, y, z, s],
1034                                    tensor.grad(dense_from_sparse(
1035                                        a * CSM(format)(x, y, z, s)).sum(), x))
1036
1037                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
1038
1039                res = f(spmat.data, spmat.indices, spmat.indptr,
1040                        np.asarray(spmat.shape, 'int32'))
1041
1042                assert len(spmat.data) == len(res)
1043
1044    def test_csm_unsorted(self):
1045        # Test support for gradients of unsorted inputs.
1046
1047        sp_types = {'csc': sp.csc_matrix,
1048                    'csr': sp.csr_matrix}
1049
1050        for format in ['csr', 'csc', ]:
1051            for dtype in ['float32', 'float64']:
1052                x = tensor.tensor(dtype=dtype, broadcastable=(False,))
1053                y = tensor.ivector()
1054                z = tensor.ivector()
1055                s = tensor.ivector()
1056                # Sparse advanced indexing produces unsorted sparse matrices
1057                a = sparse_random_inputs(format, (8, 6), out_dtype=dtype,
1058                                         unsorted_indices=True)[1][0]
1059                # Make sure it's unsorted
1060                assert not a.has_sorted_indices
1061                def my_op(x):
1062                    y = tensor.constant(a.indices)
1063                    z = tensor.constant(a.indptr)
1064                    s = tensor.constant(a.shape)
1065                    return tensor.sum(
1066                        dense_from_sparse(CSM(format)(x, y, z, s) * a))
1067                verify_grad_sparse(my_op, [a.data])
1068
1069    def test_csm(self):
1070        sp_types = {'csc': sp.csc_matrix,
1071                    'csr': sp.csr_matrix}
1072
1073        for format in ['csc', 'csr']:
1074            for dtype in ['float32', 'float64']:
1075                x = tensor.tensor(dtype=dtype, broadcastable=(False,))
1076                y = tensor.ivector()
1077                z = tensor.ivector()
1078                s = tensor.ivector()
1079                f = theano.function([x, y, z, s], CSM(format)(x, y, z, s))
1080
1081                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
1082
1083                res = f(spmat.data, spmat.indices, spmat.indptr,
1084                        np.asarray(spmat.shape, 'int32'))
1085
1086                assert np.all(res.data == spmat.data)
1087                assert np.all(res.indices == spmat.indices)
1088                assert np.all(res.indptr == spmat.indptr)
1089                assert np.all(res.shape == spmat.shape)
1090
1091
1092class test_structureddot(unittest.TestCase):
1093    def setUp(self):
1094        utt.seed_rng()
1095
1096    def test_structureddot_csc_grad(self):
1097
1098        # shortcut: testing csc in float32, testing csr in float64
1099
1100        # allocate a random sparse matrix
1101        spmat = sp.csc_matrix(random_lil((4, 3), 'float32', 3))
1102
1103        mat = np.asarray(np.random.randn(3, 2), 'float32')
1104
1105        verify_grad_sparse(structured_dot, [spmat, mat], structured=True)
1106
1107        def buildgraph_T(spmat, mat):
1108            return structured_dot(mat.T, spmat.T)
1109
1110        verify_grad_sparse(buildgraph_T, [spmat, mat], structured=True)
1111
1112    def test_structureddot_csr_grad(self):
1113
1114        # shortcut: testing csc in float32, testing csr in float64
1115
1116        # allocate a random sparse matrix
1117        spmat = sp.csr_matrix(random_lil((4, 3), 'float64', 3))
1118
1119        mat = np.asarray(np.random.randn(3, 2), 'float64')
1120
1121        verify_grad_sparse(structured_dot, [spmat, mat], structured=True)
1122
1123        def buildgraph_T(spmat, mat):
1124            return structured_dot(mat.T, spmat.T)
1125
1126        verify_grad_sparse(buildgraph_T, [spmat, mat], structured=True)
1127
1128    def test_upcast(self):
1129
1130        typenames = ('float32', 'int64', 'int8', 'int32',
1131                     'int16', 'float64', 'complex64', 'complex128')
1132        for dense_dtype in typenames:
1133            for sparse_dtype in typenames:
1134                correct_dtype = theano.scalar.upcast(sparse_dtype, dense_dtype)
1135                a = SparseType('csc', dtype=sparse_dtype)()
1136                b = tensor.matrix(dtype=dense_dtype)
1137                d = structured_dot(a, b)
1138                assert d.type.dtype == correct_dtype
1139
1140                # compile and run a function
1141
1142                f = theano.function([a, b], d)
1143
1144                M, N, K, nnz = (4, 3, 5, 3)
1145                spmat = sp.csc_matrix(random_lil((M, N), sparse_dtype, nnz))
1146                # the following madness is necessary to workaround
1147                # an intc vs. int32 bug.
1148                # The lil makes an intc on my computer when sparse_dtype
1149                # is int32.
1150                spmat.dtype = np.dtype(sparse_dtype)
1151                mat = np.asarray(np.random.randn(N, K) * 9,
1152                                    dtype=dense_dtype)
1153                # print 'DTYPES', sparse_dtype, dense_dtype
1154                # print 'sym types', a.type, b.type
1155                # print 'dtype strings', spmat.dtype, mat.dtype
1156                # print 'numpy dtype num', mat.dtype.num
1157                # print 'scipy dtype num', spmat.data.dtype.num
1158                theano_result = f(spmat, mat)
1159                scipy_result = spmat * mat
1160                assert theano_result.shape == scipy_result.shape
1161                assert theano_result.dtype == scipy_result.dtype
1162                utt.assert_allclose(scipy_result, theano_result)
1163
1164    def test_opt_unpack(self):
1165        #
1166        # Test that a graph involving
1167        # structured_dot(assembled_csc_matrix) is optimized to be just
1168        # a structured_dot_csc Op and no assembly of a csc_matrix.
1169        #
1170        # The optimization from structured_dot -> structured_dot_csc
1171        # is currently disabled, So this test is not expected to pass
1172
1173        return
1174        #
1175        kerns = tensor.Tensor(dtype='int64', broadcastable=[False])('kerns')
1176        spmat = sp.lil_matrix((4, 6), dtype='int64')
1177        for i in range(5):
1178            # set non-zeros in random locations (row x, col y)
1179            x = np.floor(np.random.rand() * spmat.shape[0])
1180            y = np.floor(np.random.rand() * spmat.shape[1])
1181            spmat[x, y] = np.random.rand() * 10
1182        spmat = sp.csc_matrix(spmat)
1183
1184        images = tensor.Tensor(dtype='float32',
1185                               broadcastable=[False, False])('images')
1186
1187        cscmat = CSC(kerns, spmat.indices[:spmat.size],
1188                     spmat.indptr, spmat.shape)
1189        f = theano.function([kerns, images], structured_dot(cscmat, images.T))
1190
1191        sdcscpresent = False
1192        for node in f.maker.fgraph.toposort():
1193            # print node.op
1194            assert not isinstance(node.op, CSM)
1195            assert not isinstance(node.op, CSMProperties)
1196            if isinstance(f.maker.fgraph.toposort()[1].op, StructuredDotCSC):
1197                sdcscpresent = True
1198        assert sdcscpresent
1199
1200        kernvals = np.array(spmat.data[:spmat.size])
1201        # print 'kdtype', kernvals.dtype, kernvals.shape,
1202        # print kernvals.ndim, kernvals.dtype.num
1203        # print 'type of kernvals = ', kernvals.dtype
1204        bsize = 3
1205        imvals = 1.0 * np.array(np.arange(bsize * spmat.shape[1]).\
1206                                   reshape(bsize, spmat.shape[1]),
1207                                   dtype='float32')
1208        outvals = f(kernvals, imvals)
1209        # print outvals
1210
1211    def test_dot_sparse_sparse(self):
1212        # test dot for 2 input sparse matrix
1213        sparse_dtype = 'float64'
1214        sp_mat = {'csc': sp.csc_matrix,
1215                  'csr': sp.csr_matrix,
1216                  'bsr': sp.csr_matrix}
1217
1218        for sparse_format_a in ['csc', 'csr', 'bsr']:
1219            for sparse_format_b in ['csc', 'csr', 'bsr']:
1220                a = SparseType(sparse_format_a, dtype=sparse_dtype)()
1221                b = SparseType(sparse_format_b, dtype=sparse_dtype)()
1222                d = theano.dot(a, b)
1223                f = theano.function([a, b], theano.Out(d, borrow=True))
1224                topo = f.maker.fgraph.toposort()
1225                for M, N, K, nnz in [(4, 3, 2, 3),
1226                                     (40, 30, 20, 3),
1227                                     (40, 30, 20, 30),
1228                                     (400, 3000, 200, 6000),
1229                                 ]:
1230                    a_val = sp_mat[sparse_format_a](
1231                        random_lil((M, N), sparse_dtype, nnz))
1232                    b_val = sp_mat[sparse_format_b](
1233                        random_lil((N, K), sparse_dtype, nnz))
1234                    f(a_val, b_val)
1235
1236    def test_csc_correct_output_faster_than_scipy(self):
1237        sparse_dtype = 'float64'
1238        dense_dtype = 'float64'
1239
1240        a = SparseType('csc', dtype=sparse_dtype)()
1241        b = tensor.matrix(dtype=dense_dtype)
1242        d = theano.dot(a, b)
1243        f = theano.function([a, b], theano.Out(d, borrow=True))
1244
1245        for M, N, K, nnz in [(4, 3, 2, 3),
1246                             (40, 30, 20, 3),
1247                             (40, 30, 20, 30),
1248                             (400, 3000, 200, 6000),
1249                         ]:
1250            spmat = sp.csc_matrix(random_lil((M, N), sparse_dtype, nnz))
1251            mat = np.asarray(np.random.randn(N, K), dense_dtype)
1252            theano_times = []
1253            scipy_times = []
1254            for i in xrange(5):
1255                t0 = time.time()
1256                theano_result = f(spmat, mat)
1257                t1 = time.time()
1258                scipy_result = spmat * mat
1259                t2 = time.time()
1260
1261                theano_times.append(t1 - t0)
1262                scipy_times.append(t2 - t1)
1263
1264            theano_time = np.min(theano_times)
1265            scipy_time = np.min(scipy_times)
1266
1267            speedup = scipy_time / theano_time
1268            # print scipy_times
1269            # print theano_times
1270            # print ('M=%(M)s N=%(N)s K=%(K)s nnz=%(nnz)s theano_time'
1271            #       '=%(theano_time)s speedup=%(speedup)s') % locals()
1272
1273            # fail if Theano is slower than scipy by more than a certain amount
1274            overhead_tol = 0.003  # seconds overall
1275            overhead_rtol = 1.2  # times as long
1276            utt.assert_allclose(scipy_result, theano_result)
1277            if (theano.config.mode == "FAST_RUN" and
1278                theano.config.cxx):
1279                self.assertFalse(theano_time > overhead_rtol * scipy_time +
1280                                 overhead_tol)
1281
1282    def test_csr_correct_output_faster_than_scipy(self):
1283
1284        # contrast with test_grad, we put csr in float32, csc in float64
1285
1286        sparse_dtype = 'float32'
1287        dense_dtype = 'float32'
1288
1289        a = SparseType('csr', dtype=sparse_dtype)()
1290        b = tensor.matrix(dtype=dense_dtype)
1291        d = theano.dot(a, b)
1292        f = theano.function([a, b], d)
1293
1294        for M, N, K, nnz in [(4, 3, 2, 3),
1295                             (40, 30, 20, 3),
1296                             (40, 30, 20, 30),
1297                             (400, 3000, 200, 6000),
1298                         ]:
1299            spmat = sp.csr_matrix(random_lil((M, N), sparse_dtype, nnz))
1300            mat = np.asarray(np.random.randn(N, K), dense_dtype)
1301            t0 = time.time()
1302            theano_result = f(spmat, mat)
1303            t1 = time.time()
1304            scipy_result = spmat * mat
1305            t2 = time.time()
1306
1307            theano_time = t1 - t0
1308            scipy_time = t2 - t1
1309            # print 'theano took', theano_time,
1310            # print 'scipy took', scipy_time
1311            overhead_tol = 0.002  # seconds
1312            overhead_rtol = 1.1  # times as long
1313            utt.assert_allclose(scipy_result, theano_result)
1314            if (theano.config.mode == "FAST_RUN" and
1315                theano.config.cxx):
1316                    self.assertFalse(
1317                        theano_time > overhead_rtol * scipy_time + overhead_tol,
1318                        (theano_time,
1319                         overhead_rtol * scipy_time + overhead_tol,
1320                         scipy_time, overhead_rtol, overhead_tol))
1321
1322
1323class DotTests(utt.InferShapeTester):
1324    def setUp(self):
1325        super(DotTests, self).setUp()
1326        x_size = (10, 100)
1327        y_size = (100, 1000)
1328        utt.seed_rng()
1329
1330        self.x_csr = scipy.sparse.csr_matrix(
1331            np.random.binomial(1, 0.5, x_size), dtype=theano.config.floatX)
1332        self.x_csc = scipy.sparse.csc_matrix(
1333            np.random.binomial(1, 0.5, x_size), dtype=theano.config.floatX)
1334        self.y = np.asarray(np.random.uniform(-1, 1, y_size),
1335                               dtype=theano.config.floatX)
1336        self.y_csr = scipy.sparse.csr_matrix(
1337            np.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX)
1338        self.y_csc = scipy.sparse.csc_matrix(
1339            np.random.binomial(1, 0.5, y_size), dtype=theano.config.floatX)
1340        self.v_10 = np.asarray(np.random.uniform(-1, 1, 10),
1341                                  dtype=theano.config.floatX)
1342        self.v_100 = np.asarray(np.random.uniform(-1, 1, 100),
1343                                   dtype=theano.config.floatX)
1344
1345    def test_csr_dense(self):
1346        x = theano.sparse.csr_matrix('x')
1347        y = theano.tensor.matrix('y')
1348        v = theano.tensor.vector('v')
1349
1350        for (x, y, x_v, y_v) in [(x, y, self.x_csr, self.y),
1351                                 (x, v, self.x_csr, self.v_100),
1352                                 (v, x, self.v_10, self.x_csr)]:
1353            f_a = theano.function([x, y], theano.sparse.dot(x, y))
1354            f_b = lambda x, y: x * y
1355
1356            utt.assert_allclose(f_a(x_v, y_v), f_b(x_v, y_v))
1357
1358            # Test infer_shape
1359            self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
1360                                    [x_v, y_v],
1361                                    (Dot, Usmm, UsmmCscDense))
1362
1363    def test_csc_dense(self):
1364        x = theano.sparse.csc_matrix('x')
1365        y = theano.tensor.matrix('y')
1366        v = theano.tensor.vector('v')
1367
1368        for (x, y, x_v, y_v) in [(x, y, self.x_csc, self.y),
1369                                 (x, v, self.x_csc, self.v_100),
1370                                 (v, x, self.v_10, self.x_csc)]:
1371
1372            f_a = theano.function([x, y], theano.sparse.dot(x, y))
1373            f_b = lambda x, y: x * y
1374
1375            utt.assert_allclose(f_a(x_v, y_v), f_b(x_v, y_v))
1376
1377            # Test infer_shape
1378            self._compile_and_check([x, y], [theano.sparse.dot(x, y)],
1379                                    [x_v, y_v],
1380                                    (Dot, Usmm, UsmmCscDense))
1381
1382    def test_sparse_sparse(self):
1383        for d1, d2 in [('float32', 'float32'),
1384                       ('float32', 'float64'),
1385                       ('float64', 'float32'),
1386                       ('float64', 'float64'),
1387                       ('float32', 'int16'),
1388                       ('float32', 'complex64'),
1389                       ]:
1390            for x_f, y_f in [('csc', 'csc'),
1391                             ('csc', 'csr'),
1392                             ('csr', 'csc'),
1393                             ('csr', 'csr'),
1394                             ]:
1395                x = theano.sparse.SparseType(format=x_f, dtype=d1)('x')
1396                y = theano.sparse.SparseType(format=x_f, dtype=d2)('x')
1397
1398                f_a = lambda x, y: x * y
1399                f_b = theano.function([x, y], theano.sparse.dot(x, y))
1400
1401                vx = getattr(self, 'x_' + x_f).astype(d1)
1402                vy = getattr(self, 'y_' + y_f).astype(d2)
1403                utt.assert_allclose(f_a(vx, vy).toarray(), f_b(vx, vy))
1404
1405                # Test infer_shape
1406                f_a = theano.function([x, y], theano.sparse.dot(x, y).shape)
1407                f_b = lambda x, y: (x * y).shape
1408                assert np.all(f_a(vx, vy) == f_b(vx, vy))
1409                topo = f_a.maker.fgraph.toposort()
1410                if theano.config.mode != 'FAST_COMPILE':
1411                    nb = 0
1412                else:
1413                    nb = 1
1414                assert sum([isinstance(node.op, (Dot, Usmm, UsmmCscDense))
1415                            for node in topo]) == nb
1416
1417    def test_int32_dtype(self):
1418        # Reported on the theano-user mailing-list:
1419        # https://groups.google.com/d/msg/theano-users/MT9ui8LtTsY/rwatwEF9zWAJ
1420        size = 9
1421        intX = 'int32'
1422
1423        C = tensor.matrix('C', dtype=intX)
1424        I = tensor.matrix('I', dtype=intX)
1425
1426        fI = I.flatten()
1427        data = tensor.ones_like(fI)
1428        indptr = tensor.arange(data.shape[0] + 1, dtype='int32')
1429
1430        m1 = sparse.CSR(data, fI, indptr, (8, size))
1431        m2 = sparse.dot(m1, C)
1432        y = m2.reshape(shape=(2, 4, 9), ndim=3)
1433
1434        f = theano.function(inputs=[I, C], outputs=y)
1435        i = np.asarray([[4, 3, 7, 7], [2, 8, 4, 5]], dtype=intX)
1436        a = np.asarray(np.random.randint(0, 100, (size, size)),
1437                          dtype=intX)
1438        f(i, a)
1439
1440    def test_csr_dense_grad(self):
1441
1442        # shortcut: testing csc in float32, testing csr in float64
1443
1444        # allocate a random sparse matrix
1445        spmat = sp.csr_matrix(random_lil((4, 3), 'float64', 3))
1446
1447        mat = np.asarray(np.random.randn(2, 4), 'float64')
1448
1449        def buildgraph_T(mat):
1450            return Dot()(mat, spmat)
1451
1452        theano.tests.unittest_tools.verify_grad(buildgraph_T, [mat])
1453
1454
1455class UsmmTests(unittest.TestCase):
1456    """
1457    Test the Usmm and UsmmCscDense class and related optimization
1458    """
1459    def setUp(self):
1460        x_size = (10, 100)
1461        y_size = (100, 200)
1462        z_size = (x_size[0], y_size[1])
1463
1464        self.rng = np.random.RandomState(seed=utt.fetch_seed())
1465        self.x = np.asarray(self.rng.binomial(1, 0.5, x_size),
1466                               dtype=theano.config.floatX)
1467        self.y = np.asarray(self.rng.uniform(-1, 1, y_size),
1468                               dtype=theano.config.floatX)
1469        self.z = np.asarray(self.rng.uniform(-1, 1, z_size),
1470                               dtype=theano.config.floatX)
1471
1472    # this is slow, but it's the only test for the op.
1473    def test(self):
1474        def mat(format, name, dtype):
1475            if format == 'dense':
1476                return theano.tensor.matrix(name, dtype=dtype)
1477            else:
1478                return theano.sparse.matrix(format, name, dtype=dtype)
1479
1480        params = product(*([['float32', 'float64', 'int16', 'complex64']] * 4 +
1481                           [['dense', 'csc', 'csr']] * 2))
1482
1483        # All test are too slow, so we randomly take 100 of them.
1484        # The buildbot change the seed, so we will finish by running them all.
1485        # As of this writing they where all passing.
1486        #params = self.rng.permutation(list(params))[:500]
1487
1488        for dtype1, dtype2, dtype3, dtype4, format1, format2 in params:
1489            if format1 == 'dense' and format2 == 'dense':
1490                # Usmm won't be used!
1491                continue
1492            x = mat(format1, 'x', dtype1)
1493            y = mat(format2, 'y', dtype2)
1494            a = theano.tensor.scalar('a', dtype=dtype3)
1495            z = theano.shared(np.asarray(self.z, dtype=dtype4).copy())
1496
1497            f_b = lambda z, a, x, y: z - a * (x * y)
1498            x_data = np.asarray(self.x, dtype=dtype1)
1499            if format1 != 'dense':
1500                x_data = as_sparse_format(x_data, format1)
1501            y_data = np.asarray(self.y, dtype=dtype2)
1502            if format2 != 'dense':
1503                y_data = as_sparse_format(y_data, format2)
1504            a_data = np.asarray(1.5, dtype=dtype3)
1505            z_data = np.asarray(self.z, dtype=dtype4)
1506
1507            f_b_out = f_b(z_data, a_data, x_data, y_data)
1508
1509            # Can it work inplace?
1510            inplace = dtype4 == theano.scalar.upcast(dtype1, dtype2, dtype3)
1511
1512            # To make it easier to check the toposort
1513            mode = theano.compile.mode.get_default_mode().excluding('fusion')
1514
1515            if inplace:
1516                updates = [(z, z - a * theano.sparse.dot(x, y))]
1517                f_a = theano.function([a, x, y], [],
1518                                      updates=updates,
1519                                      mode=mode)
1520                f_a(a_data, x_data, y_data)
1521                f_a_out = z.get_value(borrow=True)
1522            else:
1523                f_a = theano.function([a, x, y],
1524                                      z - a * theano.sparse.dot(x, y),
1525                                      mode=mode)
1526                # In DebugMode there is a strange difference with complex
1527                # So we raise a little the threshold a little.
1528                try:
1529                    orig_atol = theano.tensor.basic.float64_atol
1530                    orig_rtol = theano.tensor.basic.float64_rtol
1531                    theano.tensor.basic.float64_atol = 1e-7
1532                    theano.tensor.basic.float64_rtol = 1e-6
1533                    f_a_out = f_a(a_data, x_data, y_data)
1534                finally:
1535                    theano.tensor.basic.float64_atol = orig_atol
1536                    theano.tensor.basic.float64_rtol = orig_rtol
1537
1538            # As we do a dot product of 2 vector of 100 element,
1539            # This mean we can have 2*100*eps abs error.
1540            if f_a_out.dtype in ['float64', 'complex128']:
1541                atol = 3e-8
1542                rtol = 1e-5
1543            else:
1544                atol = None
1545                rtol = None
1546            utt.assert_allclose(f_a_out, f_b_out, rtol=rtol, atol=atol)
1547            topo = f_a.maker.fgraph.toposort()
1548            up = theano.scalar.upcast(dtype1, dtype2, dtype3, dtype4)
1549
1550            fast_compile = theano.config.mode == "FAST_COMPILE"
1551
1552            if not theano.config.blas.ldflags:
1553                # Usmm should not be inserted, because it relies on BLAS
1554                assert len(topo) == 4, topo
1555                assert isinstance(topo[0].op, theano.sparse.Dot)
1556                assert isinstance(topo[1].op, theano.tensor.DimShuffle)
1557                assert (isinstance(topo[2].op, theano.tensor.Elemwise) and
1558                        isinstance(topo[2].op.scalar_op, theano.scalar.Mul))
1559                assert (isinstance(topo[3].op, theano.tensor.Elemwise) and
1560                        isinstance(topo[3].op.scalar_op, theano.scalar.Sub))
1561            elif (y.type.dtype == up and format1 == 'csc'
1562                    and format2 == 'dense' and not fast_compile
1563                    and theano.config.cxx and up in ('float32', 'float64')):
1564                # The op UsmmCscDense should be inserted
1565                assert (sum([isinstance(node.op, tensor.Elemwise) and
1566                             isinstance(node.op.scalar_op,
1567                                        theano.scalar.basic.Cast)
1568                             for node in topo]) == len(topo) - 5)
1569                new_topo = []
1570                for node in topo:
1571                    if not (isinstance(node.op, tensor.Elemwise) and
1572                       isinstance(node.op.scalar_op,
1573                                  theano.scalar.basic.Cast)):
1574                        new_topo.append(node)
1575                topo = new_topo
1576                assert len(topo) == 5, topo
1577                # Usmm is tested at the same time in debugmode
1578                # Check if the optimization local_usmm and local_usmm_csx is
1579                # applied
1580                def check_once(x):
1581                    assert sum([isinstance(n.op, x) for n in topo]) == 1
1582                check_once(theano.sparse.basic.CSMProperties)
1583                check_once(theano.tensor.DimShuffle)
1584                check_once(theano.tensor.Subtensor)
1585                check_once(UsmmCscDense)
1586                check_once(theano.tensor.Elemwise)
1587                if inplace:
1588                    assert topo[4].op.inplace
1589            elif not fast_compile:
1590                # The op Usmm should be inserted
1591                assert len(topo) == 3, topo
1592                assert isinstance(topo[0].op, theano.tensor.DimShuffle)
1593                assert topo[1].op == theano.tensor.neg
1594                assert isinstance(topo[2].op, theano.sparse.Usmm)
1595
1596    def test_infer_shape(self):
1597        def mat(format, name, dtype):
1598            if format == 'dense':
1599                return theano.tensor.matrix(name, dtype=dtype)
1600            else:
1601                return theano.sparse.matrix(format, name, dtype=dtype)
1602
1603        params = [('float32', 'float64', 'int16', 'complex64', 'csc', 'dense'),
1604                  ('float32', 'float64', 'int16', 'complex64', 'csr', 'dense')]
1605        for dtype1, dtype2, dtype3, dtype4, format1, format2 in params:
1606            if format1 == 'dense' and format2 == 'dense':
1607                # Usmm won't be used!
1608                continue
1609            x = mat(format1, 'x', dtype1)
1610            y = mat(format2, 'y', dtype2)
1611            a = theano.tensor.scalar('a', dtype=dtype3)
1612            z = theano.shared(np.asarray(self.z, dtype=dtype4).copy())
1613
1614            f_b = lambda z, a, x, y: z - a * (x * y)
1615            x_data = np.asarray(self.x, dtype=dtype1)
1616            if format1 != 'dense':
1617                x_data = as_sparse_format(x_data, format1)
1618            y_data = np.asarray(self.y, dtype=dtype2)
1619            if format2 != 'dense':
1620                y_data = as_sparse_format(y_data, format2)
1621            a_data = np.asarray(1.5, dtype=dtype3)
1622            z_data = np.asarray(self.z, dtype=dtype4)
1623
1624            f_b_out = f_b(z_data, a_data, x_data, y_data)
1625
1626            # Can it work inplace?
1627            inplace = dtype4 == theano.scalar.upcast(dtype1, dtype2, dtype3)
1628
1629            # To make it easier to check the toposort
1630            mode = theano.compile.mode.get_default_mode().excluding('fusion')
1631
1632            # test infer_shape of Dot got applied
1633            f_shape = theano.function([a, x, y],
1634                                      (z - a * theano.sparse.dot(x, y)).shape,
1635                                      mode=mode)
1636            assert all(f_shape(a_data, x_data, y_data) == f_b_out.shape)
1637            topo = f_shape.maker.fgraph.toposort()
1638            if theano.config.mode != 'FAST_COMPILE':
1639                nb = 0
1640            else:
1641                nb = 1
1642            assert sum([isinstance(node.op, (Dot, Usmm, UsmmCscDense))
1643                        for node in topo]) == nb
1644
1645
1646class test_zeros_like(unittest.TestCase):
1647    def test(self):
1648        x = theano.sparse.csr_matrix()
1649        f = theano.function([x], theano.sparse.sp_zeros_like(x))
1650        vx = scipy.sparse.csr_matrix(np.asarray(
1651            np.random.binomial(1, 0.5, (100, 100)),
1652            dtype=theano.config.floatX))
1653
1654        fx = f(vx)
1655
1656        assert fx.nnz == 0
1657        assert fx.shape == vx.shape
1658
1659
1660def test_shape_i():
1661    sparse_dtype = 'float32'
1662
1663    a = SparseType('csr', dtype=sparse_dtype)()
1664    f = theano.function([a], a.shape[1])
1665    assert f(sp.csr_matrix(random_lil((100, 10), sparse_dtype, 3))) == 10
1666
1667
1668def test_shape():
1669    # Test that getting the shape of a sparse variable
1670    # does not actually create a dense tensor in the process.
1671    sparse_dtype = 'float32'
1672
1673    a = SparseType('csr', dtype=sparse_dtype)()
1674    f = theano.function([a], a.shape)
1675    assert np.all(f(sp.csr_matrix(random_lil((100, 10), sparse_dtype, 3)))
1676                     == (100, 10))
1677    if theano.config.mode != 'FAST_COMPILE':
1678        topo = f.maker.fgraph.toposort()
1679        assert len(topo) == 3
1680        assert isinstance(topo[0].op, tensor.opt.Shape_i)
1681        assert isinstance(topo[1].op, tensor.opt.Shape_i)
1682        assert isinstance(topo[2].op, tensor.opt.MakeVector)
1683
1684
1685def test_may_share_memory():
1686    a = scipy.sparse.csc_matrix(scipy.sparse.eye(5, 3))
1687    b = scipy.sparse.csc_matrix(scipy.sparse.eye(4, 3))
1688    as_ar = lambda a: theano._asarray(a, dtype='int32')
1689    for a_, b_, rep in [(a, a, True),
1690                        (b, b, True),
1691                        (a, b, False),
1692                        (a, a.data, True),
1693                        (a, a.indptr, True),
1694                        (a, a.indices, True),
1695                        (a, as_ar(a.shape), False),
1696                        (a.data, a, True),
1697                        (a.indptr, a, True),
1698                        (a.indices, a, True),
1699                        (as_ar(a.shape), a, False),
1700                        (b, b.data, True),
1701                        (b, b.indptr, True),
1702                        (b, b.indices, True),
1703                        (b, as_ar(b.shape), False),
1704                        (b.data, b, True),
1705                        (b.indptr, b, True),
1706                        (b.indices, b, True),
1707                        (as_ar(b.shape), b, False),
1708                        (b.data, a, False),
1709                        (b.indptr, a, False),
1710                        (b.indices, a, False),
1711                        (as_ar(b.shape), a, False),
1712                        (a.transpose(), a, True),
1713                        (b.transpose(), b, True),
1714                        (a.transpose(), b, False),
1715                        (b.transpose(), a, False),
1716                        ]:
1717
1718        assert SparseType.may_share_memory(a_, b_) == rep
1719
1720
1721def test_sparse_shared_memory():
1722    # Note : There are no inplace ops on sparse matrix yet. If one is
1723    # someday implemented, we could test it here.
1724    a = random_lil((3, 4), 'float32', 3).tocsr()
1725    m1 = random_lil((4, 4), 'float32', 3).tocsr()
1726    m2 = random_lil((4, 4), 'float32', 3).tocsr()
1727    x = SparseType('csr', dtype='float32')()
1728    y = SparseType('csr', dtype='float32')()
1729
1730    sdot = theano.sparse.structured_dot
1731    z = sdot(x * 3, m1) + sdot(y * 2, m2)
1732
1733    f = theano.function([theano.In(x, mutable=True),
1734                         theano.In(y, mutable=True)], z, mode='FAST_RUN')
1735
1736    def f_(x, y, m1=m1, m2=m2):
1737        return ((x * 3) * m1) + ((y * 2) * m2)
1738
1739    assert SparseType.may_share_memory(a, a)  # This is trivial
1740    result = f(a, a)
1741    result_ = f_(a, a)
1742    assert (result_.todense() == result.todense()).all()
1743
1744
1745def test_size():
1746    # Ensure the `size` attribute of sparse matrices behaves as in numpy.
1747
1748    for sparse_type in ('csc_matrix', 'csr_matrix'):
1749        x = getattr(theano.sparse, sparse_type)()
1750        y = getattr(scipy.sparse, sparse_type)((5, 7)).astype(config.floatX)
1751        get_size = theano.function([x], x.size)
1752
1753        def check():
1754            assert y.size == get_size(y)
1755        # We verify that the size is correctly updated as we store more data
1756        # into the sparse matrix (including zeros).
1757        check()
1758        y[0, 0] = 1
1759        check()
1760        y[0, 1] = 0
1761        check()
1762
1763
1764class ColScaleCSCTester(utt.InferShapeTester):
1765    def setUp(self):
1766        super(ColScaleCSCTester, self).setUp()
1767        self.op = sparse.col_scale
1768
1769    def test_op(self):
1770        for format in sparse.sparse_formats:
1771            variable, data = sparse_random_inputs(format, shape=(8, 10))
1772            variable.append(tensor.vector())
1773            data.append(np.random.random(10).astype(config.floatX))
1774
1775            f = theano.function(variable, self.op(*variable))
1776
1777            tested = f(*data)
1778            x, s = data[0].toarray(), data[1][np.newaxis, :]
1779            expected = x * s
1780
1781            assert tested.format == format
1782            utt.assert_allclose(expected, tested.toarray())
1783
1784    def test_infer_shape(self):
1785        for format, cls in [('csc', sparse.ColScaleCSC),
1786                            ('csr', sparse.RowScaleCSC)]:
1787            variable, data = sparse_random_inputs(format, shape=(8, 10))
1788            variable.append(tensor.vector())
1789            data.append(np.random.random(10).astype(config.floatX))
1790
1791            self._compile_and_check(variable,
1792                                    [self.op(*variable)],
1793                                    data,
1794                                    cls)
1795
1796    def test_grad(self):
1797        for format in sparse.sparse_formats:
1798            variable, data = sparse_random_inputs(format, shape=(8, 10))
1799            variable.append(tensor.vector())
1800            data.append(np.random.random(10).astype(config.floatX))
1801
1802            verify_grad_sparse(self.op, data, structured=True)
1803
1804
1805class RowScaleCSCTester(utt.InferShapeTester):
1806    def setUp(self):
1807        super(RowScaleCSCTester, self).setUp()
1808        self.op = sparse.row_scale
1809
1810    def test_op(self):
1811        for format in sparse.sparse_formats:
1812            variable, data = sparse_random_inputs(format, shape=(8, 10))
1813            variable.append(tensor.vector())
1814            data.append(np.random.random(8).astype(config.floatX))
1815
1816            f = theano.function(variable, self.op(*variable))
1817
1818            tested = f(*data)
1819            x, s = data[0].toarray(), data[1][:, np.newaxis]
1820            expected = x * s
1821
1822            assert tested.format == format
1823            utt.assert_allclose(expected, tested.toarray())
1824
1825    def test_infer_shape(self):
1826        for format, cls in [('csc', sparse.RowScaleCSC),
1827                            ('csr', sparse.ColScaleCSC)]:
1828            variable, data = sparse_random_inputs(format, shape=(8, 10))
1829            variable.append(tensor.vector())
1830            data.append(np.random.random(8).astype(config.floatX))
1831
1832            self._compile_and_check(variable,
1833                                    [self.op(*variable)],
1834                                    data,
1835                                    cls)
1836
1837    def test_grad(self):
1838        for format in sparse.sparse_formats:
1839            variable, data = sparse_random_inputs(format, shape=(8, 10))
1840            variable.append(tensor.vector())
1841            data.append(np.random.random(8).astype(config.floatX))
1842
1843            verify_grad_sparse(self.op, data, structured=True)
1844
1845
1846class SpSumTester(utt.InferShapeTester):
1847    possible_axis = [None, 0, 1]
1848
1849    def setUp(self):
1850        super(SpSumTester, self).setUp()
1851        self.op_class = sparse.SpSum
1852        self.op = sparse.sp_sum
1853
1854    def test_op(self):
1855        for format in sparse.sparse_formats:
1856            for axis in self.possible_axis:
1857                variable, data = sparse_random_inputs(format,
1858                                                      shape=(10, 10))
1859
1860                z = theano.sparse.sp_sum(variable[0], axis=axis)
1861                if axis is None:
1862                    assert z.type.broadcastable == ()
1863                else:
1864                    assert z.type.broadcastable == (False, )
1865
1866                f = theano.function(variable, self.op(variable[0], axis=axis))
1867                tested = f(*data)
1868                expected = data[0].todense().sum(axis).ravel()
1869                utt.assert_allclose(expected, tested)
1870
1871    def test_infer_shape(self):
1872        for format in sparse.sparse_formats:
1873            for axis in self.possible_axis:
1874                variable, data = sparse_random_inputs(format,
1875                                                      shape=(9, 10))
1876                self._compile_and_check(variable,
1877                                        [self.op(variable[0], axis=axis)],
1878                                        data,
1879                                        self.op_class)
1880
1881    def test_grad(self):
1882        for format in sparse.sparse_formats:
1883            for axis in self.possible_axis:
1884                for struct in [True, False]:
1885                    variable, data = sparse_random_inputs(format,
1886                                                          shape=(9, 10))
1887                    verify_grad_sparse(
1888                        self.op_class(axis=axis, sparse_grad=struct),
1889                        data,
1890                        structured=struct)
1891
1892
1893class DiagTester(utt.InferShapeTester):
1894    def setUp(self):
1895        super(DiagTester, self).setUp()
1896        self.op_class = Diag
1897        self.op = diag
1898
1899    def test_op(self):
1900        for format in sparse.sparse_formats:
1901            variable, data = sparse_random_inputs(format,
1902                                                  shape=(10, 10))
1903
1904            z = self.op(*variable)
1905            assert z.type.broadcastable == (False, )
1906
1907            f = theano.function(variable, z)
1908            tested = f(*data)
1909            expected = data[0].toarray().diagonal()
1910
1911            utt.assert_allclose(expected, tested)
1912
1913    def test_infer_shape(self):
1914        for format in sparse.sparse_formats:
1915            variable, data = sparse_random_inputs(format,
1916                                                  shape=(10, 10))
1917            self._compile_and_check(variable,
1918                                    [self.op(*variable)],
1919                                    data,
1920                                    self.op_class,
1921                                    warn=False)
1922
1923    def test_grad(self):
1924        for format in sparse.sparse_formats:
1925            variable, data = sparse_random_inputs(format,
1926                                                  shape=(10, 10))
1927            verify_grad_sparse(
1928                self.op,
1929                data,
1930                structured=False)
1931
1932
1933class SquareDiagonalTester(utt.InferShapeTester):
1934    def setUp(self):
1935        super(SquareDiagonalTester, self).setUp()
1936        self.op_class = SquareDiagonal
1937        self.op = square_diagonal
1938
1939    def test_op(self):
1940        for format in sparse.sparse_formats:
1941            for size in range(5, 9):
1942                variable = [tensor.vector()]
1943                data = [np.random.random(size).astype(config.floatX)]
1944
1945                f = theano.function(variable, self.op(*variable))
1946                tested = f(*data).toarray()
1947
1948                expected = np.diag(*data)
1949                utt.assert_allclose(expected, tested)
1950                assert tested.dtype == expected.dtype
1951                assert tested.shape == expected.shape
1952
1953    def test_infer_shape(self):
1954        for format in sparse.sparse_formats:
1955            for size in range(5, 9):
1956                variable = [tensor.vector()]
1957                data = [np.random.random(size).astype(config.floatX)]
1958
1959                self._compile_and_check(variable,
1960                                        [self.op(*variable)],
1961                                        data,
1962                                        self.op_class)
1963
1964    def test_grad(self):
1965        for format in sparse.sparse_formats:
1966            for size in range(5, 9):
1967                variable = [tensor.vector()]
1968                data = [np.random.random(size).astype(config.floatX)]
1969
1970                verify_grad_sparse(
1971                    self.op,
1972                    data,
1973                    structured=False)
1974
1975
1976class EnsureSortedIndicesTester(utt.InferShapeTester):
1977    def setUp(self):
1978        super(EnsureSortedIndicesTester, self).setUp()
1979        self.op_class = EnsureSortedIndices
1980        self.op = ensure_sorted_indices
1981
1982    def test_op(self):
1983        for format in sparse.sparse_formats:
1984            for shape in zip(range(5, 9), range(3, 7)[::-1]):
1985                variable, data = sparse_random_inputs(format, shape=shape)
1986
1987                f = theano.function(variable, self.op(*variable))
1988                tested = f(*data).toarray()
1989                expected = data[0].sorted_indices().toarray()
1990
1991                utt.assert_allclose(expected, tested)
1992
1993    def test_infer_shape(self):
1994        for format in sparse.sparse_formats:
1995            for shape in zip(range(5, 9), range(3, 7)[::-1]):
1996                variable, data = sparse_random_inputs(format, shape=shape)
1997                self._compile_and_check(variable,
1998                                        [self.op(*variable)],
1999                                        data,
2000                                        self.op_class)
2001
2002    def test_grad(self):
2003        for format in sparse.sparse_formats:
2004            for shape in zip(range(5, 9), range(3, 7)[::-1]):
2005                variable, data = sparse_random_inputs(format, shape=shape)
2006                verify_grad_sparse(
2007                    self.op,
2008                    data,
2009                    structured=False)
2010
2011
2012class CleanTester(utt.InferShapeTester):
2013    def setUp(self):
2014        super(CleanTester, self).setUp()
2015        self.op = clean
2016
2017    def test_op(self):
2018        for format in sparse.sparse_formats:
2019            for shape in zip(range(5, 9), range(3, 7)[::-1]):
2020                variable, data = sparse_random_inputs(format, shape=shape)
2021
2022                data[0][0, 0] = data[0][1, 1] = 0
2023
2024                f = theano.function(variable, self.op(*variable))
2025                tested = f(*data)
2026                expected = data[0]
2027                expected.eliminate_zeros()
2028
2029                assert all(tested.data == expected.data)
2030                assert not all(tested.data == 0)
2031
2032                tested = tested.toarray()
2033                expected = expected.toarray()
2034                utt.assert_allclose(expected, tested)
2035
2036    def test_grad(self):
2037        for format in sparse.sparse_formats:
2038            for shape in zip(range(5, 9), range(3, 7)[::-1]):
2039                variable, data = sparse_random_inputs(format, shape=shape)
2040                verify_grad_sparse(
2041                    self.op,
2042                    data,
2043                    structured=False)
2044
2045
2046class Remove0Tester(utt.InferShapeTester):
2047    def setUp(self):
2048        super(Remove0Tester, self).setUp()
2049        self.op_class = Remove0
2050
2051    def test_remove0(self):
2052        configs = [
2053            # structure type, numpy matching class
2054            ('csc', scipy.sparse.csc_matrix),
2055            ('csr', scipy.sparse.csr_matrix), ]
2056
2057        for format, matrix_class in configs:
2058            for zero, unsor in [(True, True), (True, False),
2059                              (False, True), (False, False)]:
2060                (x,), (mat,) = sparse_random_inputs(format, (6, 8),
2061                                            out_dtype=config.floatX,
2062                                            explicit_zero=zero,
2063                                            unsorted_indices=unsor)
2064                assert 0 in mat.data or not zero
2065                assert not mat.has_sorted_indices or not unsor
2066
2067                # the In thingy has to be there because theano has as rule not
2068                # to optimize inputs
2069                f = theano.function([theano.In(x, borrow=True, mutable=True)],
2070                                    Remove0()(x))
2071
2072                # assert optimization local_inplace_remove0 is applied in
2073                # modes with optimization
2074                if theano.config.mode not in ['FAST_COMPILE']:
2075                    # list of apply nodes in the optimized graph.
2076                    nodes = f.maker.fgraph.toposort()
2077                    # Check there isn't any Remove0 instance not inplace.
2078                    assert not any([isinstance(node.op, Remove0) and
2079                                    not node.op.inplace for node in nodes]), (
2080                           'Inplace optimization should have been applied')
2081                    # Check there is at least one Remove0 inplace.
2082                    assert any([isinstance(node.op, Remove0) and node.op.inplace
2083                                for node in nodes])
2084                # checking
2085                # makes sense to change its name
2086                target = mat
2087                result = f(mat)
2088                mat.eliminate_zeros()
2089                msg = 'Matrices sizes differ. Have zeros been removed ?'
2090                assert result.size == target.size, msg
2091                if unsor:
2092                    assert not result.has_sorted_indices
2093                    assert not target.has_sorted_indices
2094                else:
2095                    assert result.has_sorted_indices
2096                    assert target.has_sorted_indices
2097
2098    def test_infer_shape(self):
2099        mat = (np.arange(12) + 1).reshape((4, 3))
2100        mat[0, 1] = mat[1, 0] = mat[2, 2] = 0
2101
2102        x_csc = theano.sparse.csc_matrix(dtype=theano.config.floatX)
2103        mat_csc = sp.csc_matrix(mat, dtype=theano.config.floatX)
2104        self._compile_and_check([x_csc],
2105                                [Remove0()(x_csc)],
2106                                [mat_csc],
2107                                self.op_class)
2108
2109        x_csr = theano.sparse.csr_matrix(dtype=theano.config.floatX)
2110        mat_csr = sp.csr_matrix(mat, dtype=theano.config.floatX)
2111        self._compile_and_check([x_csr],
2112                                [Remove0()(x_csr)],
2113                                [mat_csr],
2114                                self.op_class)
2115
2116    def test_grad(self):
2117        mat = (np.arange(9) + 1).reshape((3, 3))
2118        mat[0, 1] = mat[1, 0] = mat[2, 2] = 0
2119
2120        mat_csc = sp.csc_matrix(mat, dtype=theano.config.floatX)
2121        verify_grad_sparse(Remove0(), [mat_csc])
2122
2123        mat_csr = sp.csr_matrix(mat, dtype=theano.config.floatX)
2124        verify_grad_sparse(Remove0(), [mat_csr])
2125
2126
2127class Test_getitem(unittest.TestCase):
2128    def setUp(self):
2129        self.rng = np.random.RandomState(utt.fetch_seed())
2130
2131    def test_GetItemList(self):
2132
2133        a, A = sparse_random_inputs('csr', (4, 5))
2134        b, B = sparse_random_inputs('csc', (4, 5))
2135        y = a[0][[0, 1, 2, 3, 1]]
2136        z = b[0][[0, 1, 2, 3, 1]]
2137
2138        fa = theano.function([a[0]], y)
2139        fb = theano.function([b[0]], z)
2140
2141        t_geta = fa(A[0]).todense()
2142        t_getb = fb(B[0]).todense()
2143
2144        s_geta = scipy.sparse.csr_matrix(A[0])[[0, 1, 2, 3, 1]].todense()
2145        s_getb = scipy.sparse.csc_matrix(B[0])[[0, 1, 2, 3, 1]].todense()
2146
2147        utt.assert_allclose(t_geta, s_geta)
2148        utt.assert_allclose(t_getb, s_getb)
2149
2150    def test_GetItemList_wrong_index(self):
2151        a, A = sparse_random_inputs('csr', (4, 5))
2152        y = a[0][[0, 4]]
2153        f = theano.function([a[0]], y)
2154
2155        self.assertRaises(IndexError, f, A[0])
2156
2157    def test_get_item_list_grad(self):
2158        op = theano.sparse.basic.GetItemList()
2159        def op_with_fixed_index(x):
2160            return op(x, index=np.asarray([0, 1]))
2161
2162        x, x_val = sparse_random_inputs("csr", (4, 5))
2163
2164        try:
2165            verify_grad_sparse(op_with_fixed_index, x_val)
2166        except NotImplementedError as e:
2167            assert "Scipy version is to old" in str(e)
2168
2169    def test_GetItem2Lists(self):
2170
2171        a, A = sparse_random_inputs('csr', (4, 5))
2172        b, B = sparse_random_inputs('csc', (4, 5))
2173        y = a[0][[0, 0, 1, 3], [0, 1, 2, 4]]
2174        z = b[0][[0, 0, 1, 3], [0, 1, 2, 4]]
2175
2176        fa = theano.function([a[0]], y)
2177        fb = theano.function([b[0]], z)
2178
2179        t_geta = fa(A[0])
2180        t_getb = fb(B[0])
2181
2182        s_geta = np.asarray(scipy.sparse.csr_matrix(A[0])[[0, 0, 1, 3], [0, 1, 2, 4]])
2183        s_getb = np.asarray(scipy.sparse.csc_matrix(B[0])[[0, 0, 1, 3], [0, 1, 2, 4]])
2184
2185        utt.assert_allclose(t_geta, s_geta)
2186        utt.assert_allclose(t_getb, s_getb)
2187
2188    def test_GetItem2Lists_wrong_index(self):
2189        a, A = sparse_random_inputs('csr', (4, 5))
2190        y1 = a[0][[0, 5], [0, 3]]
2191        y2 = a[0][[0, 3], [0, 5]]
2192
2193        f1 = theano.function([a[0]], y1)
2194        f2 = theano.function([a[0]], y2)
2195
2196        self.assertRaises(IndexError, f1, A[0])
2197        self.assertRaises(IndexError, f2, A[0])
2198
2199    def test_get_item_2lists_grad(self):
2200        op = theano.sparse.basic.GetItem2Lists()
2201        def op_with_fixed_index(x):
2202            return op(x, ind1=np.asarray([0, 1]), ind2=np.asarray([2, 3]))
2203
2204        x, x_val = sparse_random_inputs("csr", (4, 5))
2205
2206        verify_grad_sparse(op_with_fixed_index, x_val)
2207
2208    def test_GetItem2D(self):
2209        scipy_ver = [int(n) for n in scipy.__version__.split('.')[:2]]
2210        is_supported_version = bool(scipy_ver >= [0, 14])
2211
2212        sparse_formats = ('csc', 'csr')
2213        for format in sparse_formats:
2214            x = theano.sparse.matrix(format, name='x')
2215            a = theano.tensor.iscalar('a')
2216            b = theano.tensor.iscalar('b')
2217            c = theano.tensor.iscalar('c')
2218            d = theano.tensor.iscalar('d')
2219            e = theano.tensor.iscalar('e')
2220            f = theano.tensor.iscalar('f')
2221
2222            # index
2223            m = 1
2224            n = 5
2225            p = 10
2226            q = 15
2227            if is_supported_version:
2228                j = 2
2229                k = 3
2230            else:
2231                j = None
2232                k = None
2233
2234            vx = as_sparse_format(self.rng.binomial(1, 0.5, (100, 97)),
2235                                      format).astype(theano.config.floatX)
2236
2237            #mode_no_debug = theano.compile.mode.get_default_mode()
2238            # if isinstance(mode_no_debug, theano.compile.DebugMode):
2239            #    mode_no_debug = 'FAST_RUN'
2240            if is_supported_version:
2241                f1 = theano.function([x, a, b, c, d, e, f], x[a:b:e, c:d:f])
2242                r1 = f1(vx, m, n, p, q, j, k)
2243                t1 = vx[m:n:j, p:q:k]
2244            else:
2245                f1 = theano.function([x, a, b, c, d], x[a:b, c:d])
2246                r1 = f1(vx, m, n, p, q)
2247                t1 = vx[m:n, p:q]
2248            assert r1.shape == t1.shape
2249            assert np.all(t1.toarray() == r1.toarray())
2250
2251            """
2252            Important: based on a discussion with both Fred and James
2253            The following indexing methods is not supported because the rval
2254            would be a sparse matrix rather than a sparse vector, which is a
2255            deviation from numpy indexing rule. This decision is made largely
2256            for keeping the consistency between numpy and theano.
2257
2258            f2 = theano.function([x, a, b, c], x[a:b, c])
2259            r2 = f2(vx, m, n, p)
2260            t2 = vx[m:n, p]
2261            assert r2.shape == t2.shape
2262            assert np.all(t2.toarray() == r2.toarray())
2263
2264            f3 = theano.function([x, a, b, c], x[a, b:c])
2265            r3 = f3(vx, m, n, p)
2266            t3 = vx[m, n:p]
2267            assert r3.shape == t3.shape
2268            assert np.all(t3.toarray() == r3.toarray())
2269
2270            f5 = theano.function([x], x[1:2,3])
2271            r5 = f5(vx)
2272            t5 = vx[1:2, 3]
2273            assert r5.shape == t5.shape
2274            assert np.all(r5.toarray() == t5.toarray())
2275
2276            f7 = theano.function([x], x[50])
2277            r7 = f7(vx)
2278            t7 = vx[50]
2279            assert r7.shape == t7.shape
2280            assert np.all(r7.toarray() == t7.toarray())
2281            """
2282            if is_supported_version:
2283                f4 = theano.function([x, a, b, e], x[a:b:e])
2284                r4 = f4(vx, m, n, j)
2285                t4 = vx[m:n:j]
2286            else:
2287                f4 = theano.function([x, a, b], x[a:b])
2288                r4 = f4(vx, m, n)
2289                t4 = vx[m:n]
2290            assert r4.shape == t4.shape
2291            assert np.all(t4.toarray() == r4.toarray())
2292
2293            #-----------------------------------------------------------
2294            # test cases using int indexing instead of theano variable
2295            f6 = theano.function([x], x[1:10:j, 10:20:k])
2296            r6 = f6(vx)
2297            t6 = vx[1:10:j, 10:20:k]
2298            assert r6.shape == t6.shape
2299            assert np.all(r6.toarray() == t6.toarray())
2300
2301            #----------------------------------------------------------
2302            # test cases with indexing both with theano variable and int
2303            if is_supported_version:
2304                f8 = theano.function([x, a, b, e], x[a:b:e, 10:20:1])
2305                r8 = f8(vx, m, n, j)
2306                t8 = vx[m:n:j, 10:20:1]
2307            else:
2308                f8 = theano.function([x, a, b], x[a:b, 10:20])
2309                r8 = f8(vx, m, n)
2310                t8 = vx[m:n, 10:20]
2311            assert r8.shape == t8.shape
2312            assert np.all(r8.toarray() == t8.toarray())
2313
2314            f9 = theano.function([x, a, b], x[1:a:j, 1:b:k])
2315            r9 = f9(vx, p, q)
2316            t9 = vx[1:p:j, 1:q:k]
2317            assert r9.shape == t9.shape
2318            assert np.all(r9.toarray() == t9.toarray())
2319
2320            #-----------------------------------------------------------
2321            # Test mixing None and variables
2322            f10 = theano.function([x, a, b], x[:a, :b])
2323            r10 = f10(vx, p, q)
2324            t10 = vx[:p, :q]
2325            assert r10.shape == t10.shape
2326            assert np.all(r10.toarray() == t10.toarray())
2327
2328            f11 = theano.function([x, a], x[:, a:])
2329            r11 = f11(vx, p)
2330            t11 = vx[:, p:]
2331            assert r11.shape == t11.shape
2332            assert np.all(r11.toarray() == t11.toarray())
2333
2334            # Test that is work with shared variable
2335            sx = theano.shared(vx)
2336            f12 = theano.function([a], sx[:, a:])
2337            r12 = f12(p)
2338            t12 = vx[:, p:]
2339            assert r12.shape == t12.shape
2340            assert np.all(r12.toarray() == t12.toarray())
2341
2342            #------------------------------------------------------------
2343            # Invalid things
2344            # The syntax is a bit awkward because assertRaises forbids
2345            # the [] shortcut for getitem.
2346            # x[a:b] is not accepted because we don't have sparse vectors
2347            self.assertRaises(NotImplementedError,
2348                              x.__getitem__, (slice(a, b), c))
2349
2350            # x[a:b:step, c:d] is not accepted because scipy silently drops
2351            # the step (!)
2352            if not is_supported_version:
2353                self.assertRaises(ValueError,
2354                                  x.__getitem__, (slice(a, b, -1), slice(c, d)))
2355                self.assertRaises(ValueError,
2356                                  x.__getitem__, (slice(a, b), slice(c, d, 2)))
2357            else:
2358                raise SkipTest("Slicing with step is supported.")
2359
2360            # Advanced indexing is not supported
2361            self.assertRaises(ValueError,
2362                              x.__getitem__,
2363                              (tensor.ivector('l'), slice(a, b)))
2364
2365            # Indexing with random things is not supported either
2366            self.assertRaises(ValueError,
2367                              x.__getitem__, slice(tensor.fscalar('f'), None))
2368            self.assertRaises(ValueError,
2369                              x.__getitem__,
2370                              (slice(None), slice([1, 3, 4], None)))
2371
2372    def test_GetItemScalar(self):
2373        sparse_formats = ('csc', 'csr')
2374        for format in sparse_formats:
2375            x = theano.sparse.csc_matrix('x')
2376            a = theano.tensor.iscalar()
2377            b = theano.tensor.iscalar()
2378
2379            m = 50
2380            n = 42
2381
2382            vx = as_sparse_format(self.rng.binomial(1, 0.5, (97, 100)),
2383                                  format).astype(theano.config.floatX)
2384
2385            f1 = theano.function([x, a, b], x[a, b])
2386            r1 = f1(vx, 10, 10)
2387            t1 = vx[10, 10]
2388            assert r1.shape == t1.shape
2389            assert np.all(t1 == r1)
2390
2391            f2 = theano.function([x, a], x[50, a])
2392            r2 = f2(vx, m)
2393            t2 = vx[50, m]
2394            assert r2.shape == t2.shape
2395            assert np.all(t2 == r2)
2396
2397            f3 = theano.function([x, a], x[a, 50])
2398            r3 = f3(vx, m)
2399            t3 = vx[m, 50]
2400            assert r3.shape == t3.shape
2401            assert np.all(t3 == r3)
2402
2403            f4 = theano.function([x], x[50, 42])
2404            r4 = f4(vx)
2405            t4 = vx[m, n]
2406            assert r3.shape == t3.shape
2407            assert np.all(t4 == r4)
2408
2409            # Test that is work with shared variable
2410            sx = theano.shared(vx)
2411            f1 = theano.function([a, b], sx[a, b])
2412            r1 = f1(10, 10)
2413            t1 = vx[10, 10]
2414            assert r1.shape == t1.shape
2415            assert np.all(t1 == r1)
2416
2417
2418class CastTester(utt.InferShapeTester):
2419    def setUp(self):
2420        super(CastTester, self).setUp()
2421
2422    # slow but only test
2423    def test_cast(self):
2424        for format in sparse.sparse_formats:
2425            for i_dtype in sparse.all_dtypes:
2426                for o_dtype in sparse.all_dtypes:
2427                    (variable, ),  (data, ) = sparse_random_inputs(
2428                        format,
2429                        shape=(4, 7),
2430                        out_dtype=i_dtype)
2431
2432                    func = theano.function([variable], cast(variable, o_dtype))
2433                    cls = theano.function([variable], Cast(o_dtype)(variable))
2434                    prop = theano.function([variable],
2435                                           variable.astype(o_dtype))
2436
2437                    t_func, t_cls, t_prop = func(data), cls(data), prop(data)
2438
2439                    expected = data.toarray().astype(o_dtype)
2440
2441                    assert t_func.format == format
2442                    assert t_cls.format == format
2443                    assert t_prop.format == format
2444
2445                    t_func = t_func.toarray()
2446                    t_cls = t_cls.toarray()
2447                    t_prop = t_prop.toarray()
2448
2449                    utt.assert_allclose(expected, t_func)
2450                    utt.assert_allclose(expected, t_cls)
2451                    utt.assert_allclose(expected, t_prop)
2452
2453    @attr('slow')
2454    def test_infer_shape(self):
2455        for format in sparse.sparse_formats:
2456            for i_dtype in sparse.all_dtypes:
2457                for o_dtype in sparse.all_dtypes:
2458                    variable, data = sparse_random_inputs(
2459                        format,
2460                        shape=(4, 7),
2461                        out_dtype=i_dtype)
2462                    self._compile_and_check(variable,
2463                                            [Cast(o_dtype)(*variable)],
2464                                            data,
2465                                            Cast)
2466
2467    def test_grad(self):
2468        for format in sparse.sparse_formats:
2469            for i_dtype in sparse.float_dtypes:
2470                for o_dtype in tensor.float_dtypes:
2471                    if o_dtype == 'float16':
2472                        # Don't test float16 output.
2473                        continue
2474                    _, data = sparse_random_inputs(
2475                        format,
2476                        shape=(4, 7),
2477                        out_dtype=i_dtype)
2478
2479                    eps = None
2480                    if o_dtype == 'float32':
2481                        eps = 1e-2
2482
2483                    verify_grad_sparse(Cast(o_dtype), data, eps=eps)
2484
2485
2486def _format_info(nb):
2487    x = {}
2488    mat = {}
2489
2490    for format in sparse.sparse_formats:
2491        variable = getattr(theano.sparse, format + '_matrix')
2492        spa = getattr(sp, format + '_matrix')
2493
2494        x[format] = [variable() for t in range(nb)]
2495        mat[format] = [spa(random_lil((3, 4), theano.config.floatX, 8))
2496                       for t in range(nb)]
2497    return x, mat
2498
2499
2500class _HVStackTester(utt.InferShapeTester):
2501    """
2502    Test for both HStack and VStack.
2503    """
2504    nb = 3  # Number of sparse matrix to stack
2505    x, mat = _format_info(nb)
2506
2507    def test_op(self):
2508        for format in sparse.sparse_formats:
2509            for out_f in sparse.sparse_formats:
2510                for dtype in sparse.all_dtypes:
2511                    blocks = self.mat[format]
2512
2513                    f = theano.function(
2514                        self.x[format],
2515                        self.op_class(
2516                            format=out_f, dtype=dtype)(*self.x[format]),
2517                        allow_input_downcast=True)
2518
2519                    tested = f(*blocks)
2520                    expected = self.expected_f(blocks,
2521                                               format=out_f,
2522                                               dtype=dtype)
2523
2524                    utt.assert_allclose(expected.toarray(), tested.toarray())
2525                    assert tested.format == expected.format
2526                    assert tested.dtype == expected.dtype
2527
2528    def test_infer_shape(self):
2529        for format in sparse.sparse_formats:
2530            self._compile_and_check(self.x[format],
2531                                    [self.op_class(dtype='float64')
2532                                     (*self.x[format])],
2533                                    self.mat[format],
2534                                    self.op_class)
2535
2536    def test_grad(self):
2537        for format in sparse.sparse_formats:
2538            for out_f in sparse.sparse_formats:
2539                for dtype in sparse.float_dtypes:
2540                    verify_grad_sparse(
2541                        self.op_class(format=out_f, dtype=dtype),
2542                        self.mat[format],
2543                        structured=False,
2544                        eps=1e-2,
2545                        )
2546
2547
2548def _hv_switch(op, expected_function):
2549    """
2550    Return the right test class for HStack or VStack.
2551
2552    :Parameters:
2553    - `op`: HStack or VStack class.
2554    - `expected_function`: function from scipy for comparaison.
2555    """
2556
2557    class XStackTester(_HVStackTester):
2558        op_class = op
2559
2560        def expected_f(self, a, format=None, dtype=None):
2561            return expected_function(a, format, dtype)
2562    XStackTester.__name__ = op.__name__ + "Tester"
2563    if hasattr(XStackTester, '__qualname__'):
2564        XStackTester.__qualname__ = XStackTester.__name__
2565    return XStackTester
2566
2567HStackTester = _hv_switch(HStack, sp.hstack)
2568VStackTester = _hv_switch(VStack, sp.vstack)
2569
2570
2571class AddSSDataTester(utt.InferShapeTester):
2572    x = {}
2573    a = {}
2574
2575    def setUp(self):
2576        super(AddSSDataTester, self).setUp()
2577        self.op_class = AddSSData
2578
2579        for format in sparse.sparse_formats:
2580            variable = getattr(theano.sparse, format + '_matrix')
2581
2582            rand = np.array(
2583                np.random.randint(1, 4, size=(3, 4)) - 1,
2584                dtype=theano.config.floatX)
2585            constant = as_sparse_format(rand, format)
2586
2587            self.x[format] = [variable() for t in range(2)]
2588            self.a[format] = [constant for t in range(2)]
2589
2590    def test_op(self):
2591        for format in sparse.sparse_formats:
2592            f = theano.function(
2593                self.x[format],
2594                add_s_s_data(*self.x[format]))
2595
2596            tested = f(*self.a[format])
2597            expected = 2 * self.a[format][0]
2598
2599            utt.assert_allclose(expected.toarray(), tested.toarray())
2600            assert tested.format == expected.format
2601            assert tested.dtype == expected.dtype
2602
2603    def test_infer_shape(self):
2604        for format in sparse.sparse_formats:
2605            self._compile_and_check(self.x[format],
2606                                    [add_s_s_data(*self.x[format])],
2607                                    self.a[format],
2608                                    self.op_class)
2609
2610    def test_grad(self):
2611        for format in sparse.sparse_formats:
2612            verify_grad_sparse(self.op_class(),
2613                               self.a[format],
2614                               structured=True)
2615
2616
2617def elemwise_checker(op, expected_f, gap=None, test_dtypes=None,
2618                     grad_test=True, name=None, gap_grad=None):
2619    """
2620    Return the appropriate test class for the elemwise on sparse.
2621
2622    :param op: Op to test.
2623    :expected_f: Function use to compare. This function must act
2624                 on dense matrix. If the op is structured
2625                 see the `structure_function` decorator to make
2626                 this function structured.
2627    :param gap: Tuple for the range of the random sample. When
2628                length is 1, it is assumed to be the exclusive
2629                max, when `gap` = (`a`, `b`) it provide a sample
2630                from [a, b[. If `None` is used, it provide [0, 1]
2631                for float dtypes and [0, 50[ for integer dtypes.
2632    :param test_dtypes: Particular dtypes for testing the op.
2633                        If `None`, this is set to the most common
2634                        dtypes.
2635    :param grad_test: True for testing the grad. False will
2636                      skip this test.
2637    :param gap_grad: If None, we reuse gap. Otherwise it is the same as gap
2638                     but for testing the gradiant of the op.
2639
2640    :return: The class that perform the tests, not an instance
2641             of the class.
2642    """
2643
2644    if test_dtypes is None:
2645        test_dtypes = sparse.all_dtypes
2646
2647    class Tester(unittest.TestCase):
2648
2649        def setUp(self):
2650            super(Tester, self).setUp()
2651            self.op = op
2652            self.expected_f = expected_f
2653            self.gap = gap
2654            if gap_grad is not None:
2655                self.gap_grad = gap_grad
2656            else:
2657                self.gap_grad = gap
2658            # Ensure the test's name is correct.
2659            utt.seed_rng()
2660            assert eval(self.__class__.__name__) is self.__class__
2661
2662        def test_op(self):
2663            for format in sparse.sparse_formats:
2664                for dtype in test_dtypes:
2665                    if dtype == 'int8' or dtype == 'uint8':
2666                        continue
2667
2668                    # When testing with unsigned integers,
2669                    # we must check if the gap contains
2670                    # negative numbers.
2671                    if dtype.startswith('uint'):
2672                        if self.gap and len(self.gap) == 2 and self.gap[0] < 0:
2673                            if self.gap[1] >= 1:
2674                                self.gap = (0, self.gap[1])
2675                            else:
2676                                raise TypeError('Gap not suitable for',
2677                                                dtype, self.__name__)
2678
2679                    variable, data = sparse_random_inputs(
2680                        format,
2681                        shape=(4, 7),
2682                        out_dtype=dtype,
2683                        gap=self.gap)
2684
2685                    f = theano.function(variable, self.op(*variable))
2686
2687                    tested = f(*data)
2688                    data = [m.toarray() for m in data]
2689                    expected = self.expected_f(*data)
2690
2691                    assert tested.format == format
2692                    tested = tested.toarray()
2693
2694                    try:
2695                        utt.assert_allclose(expected, tested)
2696                    except AssertionError:
2697                        raise AssertionError(self.__name__)
2698
2699                # Test with int8 as dtype
2700                # These tests are not in the loop for two reasons.
2701                # First, in recent version of numpy, when a numpy
2702                # function have int8 as input dtype, it returns a
2703                # float16 as output dtype. Since this does not provide
2704                # enough precision, we upcast the data before we apply the
2705                # function.
2706                # Second, the tolerance for the checkup in DebugMode
2707                # is too high.
2708                for dtype in ['int8', 'uint8']:
2709                    if dtype in test_dtypes:
2710                        if self.gap:
2711                            domain = self.gap
2712                            # When testing with unsigned integers,
2713                            # we must check if the gap contains
2714                            # negative numbers.
2715                            if dtype == 'uint8':
2716                                if len(domain) == 2 and domain[0] < 0:
2717                                    if domain[1] >= 1:
2718                                        domain = (0, domain[1])
2719                                    else:
2720                                        raise TypeError('Gap not suitable for',
2721                                                        dtype, self.__name__)
2722
2723                        else:
2724                            domain = (0, 5)
2725
2726                        variable, data = sparse_random_inputs(
2727                            format,
2728                            shape=(4, 7),
2729                            out_dtype=dtype,
2730                            gap=domain)
2731
2732                        f = theano.function(variable, self.op(*variable))
2733
2734                        old_value = (tensor.basic.float32_atol,
2735                                     tensor.basic.float32_rtol,
2736                                     tensor.basic.float64_atol,
2737                                     tensor.basic.float64_rtol)
2738                        tensor.basic.float32_atol = 1e-4
2739                        tensor.basic.float32_rtol = 1e-3
2740                        tensor.basic.float64_atol = 1e-3
2741                        tensor.basic.float64_rtol = 1e-4
2742                        try:
2743                            tested = f(*data)
2744                        finally:
2745                            (tensor.basic.float32_atol,
2746                             tensor.basic.float32_rtol,
2747                             tensor.basic.float64_atol,
2748                             tensor.basic.float64_rtol) = old_value
2749
2750                        data = [m.toarray().astype('float32') for m in data]
2751                        expected = self.expected_f(*data)
2752
2753                        assert tested.format == format
2754                        tested = tested.toarray()
2755
2756                        try:
2757                            utt.assert_allclose(tested, expected, rtol=1e-2)
2758                        except AssertionError:
2759                            raise AssertionError(self.__name__)
2760
2761        if grad_test:
2762            def test_grad(self):
2763                for format in sparse.sparse_formats:
2764                    for dtype in sparse.float_dtypes:
2765                        variable, data = sparse_random_inputs(
2766                            format,
2767                            shape=(4, 7),
2768                            out_dtype=dtype,
2769                            gap=self.gap_grad)
2770
2771                        verify_grad_sparse(self.op,
2772                                           data,
2773                                           structured=True)
2774
2775    # Set proper class name to uniquely identify tests.
2776    # Note that it is important to run this code *outside* of the `Tester`
2777    # class itself, otherwise it will not work properly for some reason.
2778    if name is None:
2779        name = op.__name__.capitalize() + 'Tester'
2780    Tester.__name__ = name
2781    if hasattr(Tester, '__qualname__'):
2782        Tester.__qualname__ = name
2783    assert 'Roundhalftoeven' not in Tester.__name__
2784
2785    return Tester
2786
2787
2788def test_hstack_vstack():
2789    # Tests sparse.hstack and sparse.vstack (as opposed to the HStack and VStack
2790    # classes that they wrap).
2791
2792    def make_block(dtype):
2793        return theano.sparse.csr_matrix(name="%s block" % dtype,
2794                                        dtype=dtype)
2795
2796    def get_expected_dtype(blocks, to_dtype):
2797        if to_dtype is None:
2798            block_dtypes = tuple(b.dtype for b in blocks)
2799            return theano.scalar.upcast(*block_dtypes)
2800        else:
2801            return to_dtype
2802
2803    # a deliberately weird mix of dtypes to stack
2804    dtypes = ('complex128', theano.config.floatX)
2805
2806    blocks = [make_block(dtype) for dtype in dtypes]
2807
2808    for stack_dimension, stack_function in enumerate((theano.sparse.vstack,
2809                                                      theano.sparse.hstack)):
2810
2811        for to_dtype in (None, ) + dtypes:
2812            stacked_blocks = stack_function(blocks, dtype=to_dtype)
2813            expected_dtype = get_expected_dtype(blocks, to_dtype)
2814            assert stacked_blocks.dtype == expected_dtype
2815
2816
2817def structure_function(f, index=0):
2818    """
2819    Decorator to structure a function which
2820    apply on dense matrix.
2821
2822    Here, the inputs of the function must be
2823    dense matrix. The sparse pattern is
2824    determined by finding the zeros.
2825
2826    :param index: The index of the parameter
2827                  from which the function must
2828                  be structured.
2829
2830    :return: The structured function for its
2831             `index` parameter.
2832    """
2833
2834    def structured_function(*args):
2835        pattern = args[index]
2836        evaluated = f(*args)
2837        evaluated[pattern == 0] = 0
2838        return evaluated
2839    return structured_function
2840
2841StructuredSigmoidTester = elemwise_checker(
2842    sparse.structured_sigmoid,
2843    structure_function(lambda x: 1.0 / (1.0 + np.exp(-x))),
2844    test_dtypes=[m for m in sparse.all_dtypes
2845                 if (not m in sparse.complex_dtypes and
2846                     not m.startswith('uint'))],
2847    gap=(-5, 5),
2848    name='StructuredSigmoidTester')
2849
2850StructuredExpTester = elemwise_checker(
2851    sparse.structured_exp,
2852    structure_function(np.exp),
2853    name='StructuredExpTester')
2854
2855StructuredLogTester = elemwise_checker(
2856    sparse.structured_log,
2857    structure_function(np.log),
2858    gap=(0.5, 10),
2859    name='StructuredLogTester')
2860
2861StructuredPowTester = elemwise_checker(
2862    lambda x: sparse.structured_pow(x, 2),
2863    structure_function(lambda x: np.power(x, 2)),
2864    name='StructuredPowTester')
2865
2866StructuredMinimumTester = elemwise_checker(
2867    lambda x: structured_minimum(x, 2),
2868    structure_function(lambda x: np.minimum(x, 2)),
2869    name='StructuredMinimumTester')
2870
2871StructuredMaximumTester = elemwise_checker(
2872    lambda x: structured_maximum(x, 2),
2873    structure_function(lambda x: np.maximum(x, 2)),
2874    name='StructuredMaximumTester')
2875
2876StructuredAddTester = elemwise_checker(
2877    lambda x: structured_add(x, 2),
2878    structure_function(lambda x: np.add(x, 2)),
2879    name='StructuredAddTester')
2880
2881SinTester = elemwise_checker(
2882    sparse.sin,
2883    np.sin)
2884
2885TanTester = elemwise_checker(
2886    sparse.tan,
2887    np.tan,
2888    gap=(-1, 1))
2889
2890ArcsinTester = elemwise_checker(
2891    sparse.arcsin,
2892    np.arcsin,
2893    gap=(-1, 1),
2894    gap_grad=(-0.99, 0.99))
2895
2896ArctanTester = elemwise_checker(
2897    sparse.arctan,
2898    np.arctan)
2899
2900SinhTester = elemwise_checker(
2901    sparse.sinh,
2902    np.sinh)
2903
2904ArcsinhTester = elemwise_checker(
2905    sparse.arcsinh,
2906    np.arcsinh,
2907    gap=(-1, 1))
2908
2909TanhTester = elemwise_checker(
2910    sparse.tanh,
2911    np.tanh,
2912    gap=(-1, 1))
2913
2914ArctanhTester = elemwise_checker(
2915    sparse.arctanh,
2916    np.arctanh,
2917    gap=(-0.9, 1),
2918    gap_grad=(-0.9, 0.95))
2919
2920RintTester = elemwise_checker(
2921    sparse.rint,
2922    np.rint,
2923    grad_test=False,
2924    test_dtypes=sparse.float_dtypes)
2925
2926SgnTester = elemwise_checker(
2927    sparse.sgn,
2928    np.sign,
2929    grad_test=False,
2930    test_dtypes=[m for m in sparse.all_dtypes
2931                 if (not m in sparse.complex_dtypes and
2932                     not m.startswith('uint'))])
2933
2934CeilTester = elemwise_checker(
2935    sparse.ceil,
2936    np.ceil,
2937    grad_test=False,
2938    test_dtypes=[m for m in sparse.all_dtypes
2939                 if not m in sparse.complex_dtypes])
2940
2941FloorTester = elemwise_checker(
2942    sparse.floor,
2943    np.floor,
2944    grad_test=False,
2945    test_dtypes=[m for m in sparse.all_dtypes
2946                 if not m in sparse.complex_dtypes])
2947
2948Log1pTester = elemwise_checker(
2949    sparse.log1p,
2950    np.log1p,
2951    gap=(0.5, 10))
2952
2953Expm1Tester = elemwise_checker(
2954    sparse.expm1,
2955    np.expm1)
2956
2957Deg2radTester = elemwise_checker(
2958    sparse.deg2rad,
2959    np.deg2rad,
2960    test_dtypes=[m for m in sparse.all_dtypes
2961                 if not m in sparse.complex_dtypes])
2962
2963Rad2degTester = elemwise_checker(
2964    sparse.rad2deg,
2965    np.rad2deg,
2966    test_dtypes=[m for m in sparse.all_dtypes
2967                 if not m in sparse.complex_dtypes])
2968
2969
2970TruncTester = elemwise_checker(
2971    sparse.trunc,
2972    np.trunc,
2973    test_dtypes=[m for m in sparse.all_dtypes
2974                 if not m in sparse.complex_dtypes],
2975    grad_test=False)
2976
2977
2978SqrTester = elemwise_checker(
2979    sparse.sqr,
2980    lambda x: x * x)
2981
2982SqrtTester = elemwise_checker(
2983    sparse.sqrt,
2984    np.sqrt,
2985    gap=(0, 10))
2986
2987ConjTester = elemwise_checker(
2988    sparse.conj,
2989    np.conj,
2990    grad_test=False)
2991
2992
2993class MulSVTester(unittest.TestCase):
2994    def setUp(self):
2995        utt.seed_rng()
2996
2997    def test_mul_s_v_grad(self):
2998        sp_types = {'csc': sp.csc_matrix,
2999                    'csr': sp.csr_matrix}
3000
3001        for format in ['csr', 'csc']:
3002            for dtype in ['float32', 'float64']:
3003                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
3004                mat = np.asarray(np.random.rand(3), dtype=dtype)
3005
3006                verify_grad_sparse(mul_s_v,
3007                                   [spmat, mat],
3008                                   structured=True)
3009
3010    def test_mul_s_v(self):
3011        sp_types = {'csc': sp.csc_matrix,
3012                    'csr': sp.csr_matrix}
3013
3014        for format in ['csr', 'csc']:
3015            for dtype in ['float32', 'float64']:
3016                x = theano.sparse.SparseType(format, dtype=dtype)()
3017                y = tensor.vector(dtype=dtype)
3018                f = theano.function([x, y], mul_s_v(x, y))
3019
3020                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
3021                mat = np.asarray(np.random.rand(3), dtype=dtype)
3022
3023                out = f(spmat, mat)
3024
3025                utt.assert_allclose(spmat.toarray() * mat, out.toarray())
3026
3027
3028class StructuredAddSVTester(unittest.TestCase):
3029    def setUp(self):
3030        utt.seed_rng()
3031
3032    def test_structured_add_s_v_grad(self):
3033        sp_types = {'csc': sp.csc_matrix,
3034                    'csr': sp.csr_matrix}
3035
3036        for format in ['csr', 'csc']:
3037            for dtype in ['float32', 'float64']:
3038                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
3039                mat = np.asarray(np.random.rand(3), dtype=dtype)
3040
3041                verify_grad_sparse(structured_add_s_v,
3042                                   [spmat, mat],
3043                                   structured=True)
3044
3045    def test_structured_add_s_v(self):
3046        sp_types = {'csc': sp.csc_matrix,
3047                    'csr': sp.csr_matrix}
3048
3049        for format in ['csr', 'csc']:
3050            for dtype in ['float32', 'float64']:
3051                x = theano.sparse.SparseType(format, dtype=dtype)()
3052                y = tensor.vector(dtype=dtype)
3053                f = theano.function([x, y], structured_add_s_v(x, y))
3054
3055                spmat = sp_types[format](random_lil((4, 3), dtype, 3))
3056                spones = spmat.copy()
3057                spones.data = np.ones_like(spones.data)
3058                mat = np.asarray(np.random.rand(3), dtype=dtype)
3059
3060                out = f(spmat, mat)
3061
3062                utt.assert_allclose(as_ndarray(spones.multiply(spmat + mat)),
3063                                    out.toarray())
3064
3065
3066class TrueDotTester(utt.InferShapeTester):
3067    def setUp(self):
3068        super(TrueDotTester, self).setUp()
3069        self.op = true_dot
3070        self.op_class = TrueDot
3071
3072    def test_op_ss(self):
3073        for format in sparse.sparse_formats:
3074            for dtype in sparse.all_dtypes:
3075                variable, data = sparse_random_inputs(format,
3076                                                      shape=(10, 10),
3077                                                      out_dtype=dtype,
3078                                                      n=2,
3079                                                      p=0.1)
3080
3081                f = theano.function(variable, self.op(*variable))
3082
3083                tested = f(*data)
3084
3085                x, y = [m.toarray() for m in data]
3086                expected = np.dot(x, y)
3087
3088                assert tested.format == format
3089                assert tested.dtype == expected.dtype
3090                tested = tested.toarray()
3091                utt.assert_allclose(tested, expected)
3092
3093    def test_op_sd(self):
3094        for format in sparse.sparse_formats:
3095            for dtype in sparse.all_dtypes:
3096                variable, data = sparse_random_inputs(format,
3097                                                      shape=(10, 10),
3098                                                      out_dtype=dtype,
3099                                                      n=2,
3100                                                      p=0.1)
3101                variable[1] = tensor.TensorType(dtype=dtype,
3102                                                broadcastable=(False, False))()
3103                data[1] = data[1].toarray()
3104
3105                f = theano.function(variable, self.op(*variable))
3106
3107                tested = f(*data)
3108                expected = np.dot(data[0].toarray(), data[1])
3109
3110                assert tested.format == format
3111                assert tested.dtype == expected.dtype
3112                tested = tested.toarray()
3113                utt.assert_allclose(tested, expected)
3114
3115    def test_infer_shape(self):
3116        for format in sparse.sparse_formats:
3117            for dtype in sparse.all_dtypes:
3118                (x, ), (x_value, ) = sparse_random_inputs(format,
3119                                                          shape=(9, 10),
3120                                                          out_dtype=dtype,
3121                                                          p=0.1)
3122                (y, ), (y_value, ) = sparse_random_inputs(format,
3123                                                          shape=(10, 24),
3124                                                          out_dtype=dtype,
3125                                                          p=0.1)
3126                variable = [x, y]
3127                data = [x_value, y_value]
3128                self._compile_and_check(variable,
3129                                        [self.op(*variable)],
3130                                        data,
3131                                        self.op_class)
3132
3133    def test_grad(self):
3134        for format in sparse.sparse_formats:
3135            for dtype in sparse.float_dtypes:
3136                (x, ), (x_value, ) = sparse_random_inputs(format,
3137                                                          shape=(9, 10),
3138                                                          out_dtype=dtype,
3139                                                          p=0.1)
3140                (y, ), (y_value, ) = sparse_random_inputs(format,
3141                                                          shape=(10, 24),
3142                                                          out_dtype=dtype,
3143                                                          p=0.1)
3144                variable = [x, y]
3145                data = [x_value, y_value]
3146                verify_grad_sparse(
3147                    self.op,
3148                    data,
3149                    structured=False)
3150
3151
3152class SamplingDotTester(utt.InferShapeTester):
3153    x = [tensor.matrix() for t in range(2)]
3154    x.append(sparse.csr_matrix())
3155    # unsquare shape
3156    a = [np.array(np.random.randint(1, 6, size=(4, 3)) - 1,
3157                     dtype=theano.config.floatX),
3158         np.array(np.random.randint(1, 6, size=(5, 3)) - 1,
3159                     dtype=theano.config.floatX),
3160         np.array(np.random.randint(1, 3, size=(4, 5)) - 1,
3161                     dtype=theano.config.floatX)
3162         ]
3163    a[2] = sp.csr_matrix(a[2])
3164
3165    def setUp(self):
3166        super(SamplingDotTester, self).setUp()
3167        self.op_class = SamplingDot
3168
3169    def test_op(self):
3170        f = theano.function(
3171            self.x,
3172            sampling_dot(*self.x))
3173
3174        tested = f(*self.a)
3175        x, y, p = self.a
3176        expected = p.multiply(np.dot(x, y.T))
3177
3178        utt.assert_allclose(as_ndarray(expected), tested.toarray())
3179        assert tested.format == 'csr'
3180        assert tested.dtype == expected.dtype
3181
3182    def test_negative_stride(self):
3183        f = theano.function(
3184            self.x,
3185            sampling_dot(*self.x))
3186
3187        a2 = [self.a[0][::-1,:], self.a[1][:,::-1], self.a[2]]
3188        tested = f(*a2)
3189        x, y, p = a2
3190        expected = p.multiply(np.dot(x, y.T))
3191
3192        utt.assert_allclose(as_ndarray(expected), tested.toarray())
3193        assert tested.format == 'csr'
3194        assert tested.dtype == expected.dtype
3195
3196    def test_infer_shape(self):
3197        self._compile_and_check(self.x,
3198                                [sampling_dot(*self.x)],
3199                                self.a,
3200                                self.op_class,
3201                                excluding=['local_sampling_dot_csr'])
3202
3203    def test_grad(self):
3204        def _helper(x, y):
3205            return sampling_dot(x, y, self.a[2])
3206        verify_grad_sparse(_helper, self.a[:2])
3207
3208
3209import theano.tensor.tests.test_sharedvar
3210@theano.tensor.tests.test_sharedvar.makeSharedTester(
3211    shared_constructor_=theano.sparse.shared,
3212    dtype_='float64',
3213    get_value_borrow_true_alias_=True,
3214    shared_borrow_true_alias_=True,
3215    set_value_borrow_true_alias_=True,
3216    set_value_inplace_=False,
3217    set_cast_value_inplace_=False,
3218    shared_constructor_accept_ndarray_=False,
3219    internal_type_=scipy.sparse.csc_matrix,
3220    test_internal_type_=scipy.sparse.issparse,
3221    theano_fct_=lambda a: dense_from_sparse(a * 2.),
3222    ref_fct_=lambda a: np.asarray((a * 2).todense()),
3223    cast_value_=scipy.sparse.csr_matrix,
3224    expect_fail_fast_shape_inplace=False,
3225)
3226class test_shared_options(object):
3227    pass
3228
3229
3230if __name__ == '__main__':
3231    unittest.main()
3232