3
"""Unit tests for the Matrix interface"""
5
# Copyright (C) 2011-2014 Garth N. Wells
7
# This file is part of DOLFIN.
9
# DOLFIN is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU Lesser General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
14
# DOLFIN is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU Lesser General Public License for more details.
19
# You should have received a copy of the GNU Lesser General Public License
20
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
22
# Modified by Anders Logg 2011
23
# Modified by Mikael Mortensen 2011
24
# Modified by Jan Blechta 2013
26
from __future__ import print_function
29
from six.moves import xrange as range
31
from dolfin_utils.test import *
34
# TODO: Reuse this fixture setup code between matrix and vector tests:
36
# Lists of backends supporting or not supporting data access
38
no_data_backends = [("PETSc", "")]
40
# Add serial only backends
41
if MPI.size(mpi_comm_world()) == 1:
42
# TODO: What about "Dense" and "Sparse"? The sub_backend wasn't
43
# used in the old test.
44
data_backends += [("uBLAS", "Dense"), ("uBLAS", "Sparse")]
45
no_data_backends += [("PETScCusp", "")]
47
# TODO: STL tests were disabled in old test framework, and do not work now:
48
# If we have PETSc, STL Vector gets typedefed to one of these and data
49
# test will not work. If none of these backends are available
50
# STLVector defaults to uBLASVEctor, which data will work
51
#if has_linear_algebra_backend("PETSc"):
52
# no_data_backends += [("STL", "")]
54
# data_backends += [("STL", "")]
56
# Remove backends we haven't built with
57
data_backends = [b for b in data_backends if has_linear_algebra_backend(b[0])]
58
no_data_backends = [b for b in no_data_backends if has_linear_algebra_backend(b[0])]
59
any_backends = data_backends + no_data_backends
62
# Fixtures setting up and resetting the global linear algebra backend for a list of backends
63
any_backend = set_parameters_fixture("linear_algebra_backend", any_backends, lambda x: x[0])
64
data_backend = set_parameters_fixture("linear_algebra_backend", data_backends, lambda x: x[0])
65
no_data_backend = set_parameters_fixture("linear_algebra_backend", no_data_backends, lambda x: x[0])
67
# With and without explicit backend choice
68
use_backend = true_false_fixture
71
class TestMatrixForAnyBackend:
73
def assemble_matrices(self, use_backend=False, keep_diagonal=False):
74
" Assemble a pair of matrices, one (square) MxM and one MxN"
75
mesh = UnitSquareMesh(21, 23)
77
V = FunctionSpace(mesh, "Lagrange", 2)
78
W = FunctionSpace(mesh, "Lagrange", 1)
85
a = dot(grad(u), grad(v))*ds
89
backend = globals()[self.backend + self.sub_backend + 'Factory'].instance()
93
A = assemble(a, backend=backend, keep_diagonal=keep_diagonal)
94
B = assemble(b, backend=backend, keep_diagonal=keep_diagonal)
97
def test_basic_la_operations(self, use_backend, any_backend):
98
# Hack to make old tests work in new framework. The original
99
# setup was a bit exoteric...
100
# TODO: Removing use of self in this class will make it
101
# clearer what happens in this test.
102
self.backend, self.sub_backend = any_backend
104
from numpy import ndarray, array, ones, sum
106
# Tests bailout for this choice
107
if self.backend == "uBLAS" and not use_backend:
110
A, B = self.assemble_matrices(use_backend)
111
unit_norm = A.norm('frobenius')
113
def wrong_getitem(type):
122
with pytest.raises(TypeError):
124
with pytest.raises(TypeError):
126
with pytest.raises(TypeError):
129
# Test __imul__ operator
131
assert round(A.norm('frobenius') - 2*unit_norm, 7) == 0
133
# Test __idiv__ operator
135
assert round(A.norm('frobenius') - unit_norm, 7) == 0
137
# Test __mul__ operator
139
assert round(C.norm('frobenius') - 4*unit_norm, 7) == 0
141
# Test __iadd__ operator
143
assert round(A.norm('frobenius') - 5*unit_norm, 7) == 0
145
# Test __isub__ operator
147
assert round(A.norm('frobenius') - unit_norm, 7) == 0
149
# Test __mul__ and __add__ operator
151
assert round(D.norm('frobenius') - unit_norm, 7) == 0
153
# Test __div__ and __sub__ operator
155
assert round(F.norm('frobenius') - unit_norm, 7) == 0
159
assert round(A.norm('frobenius') - 41*unit_norm, 7) == 0
161
# Test expected size of rectangular array
162
assert A.size(0) == B.size(0)
163
assert B.size(1) == 528
165
# Test setitem/getitem
170
def test_numpy_array(self, use_backend, any_backend):
171
self.backend, self.sub_backend = any_backend
173
from numpy import ndarray, array, ones, sum
175
# Tests bailout for this choice
176
if self.backend == "uBLAS" and not use_backend:
180
A, B = self.assemble_matrices(use_backend)
182
# Test to NumPy array
184
assert isinstance(A2,ndarray)
185
assert A2.shape == (2021, 2021)
186
assert round(sqrt(sum(A2**2)) - A.norm('frobenius'), 7) == 0
188
if self.backend == 'uBLAS' and self.sub_backend == 'Sparse':
193
assert isinstance(A3, scipy.sparse.csr_matrix)
194
assert round(numpy.linalg.norm(A3.todense() - A2) - 0.0, 7) == 0
198
#def create_sparsity_pattern(self):
199
# "Create a sparsity pattern"
200
# mesh = UnitSquareMesh(34, 33)
202
# V = FunctionSpace(mesh, "Lagrange", 2)
203
# W = FunctionSpace(mesh, "Lagrange", 1)
205
# v = TestFunction(V)
206
# u = TrialFunction(V)
207
# s = TrialFunction(W)
210
# a = dot(grad(u), grad(v))*dx
213
def test_create_empty_matrix(self, any_backend):
215
assert A.size(0) == 0
216
assert A.size(1) == 0
220
def test_copy_empty_matrix(self, any_backend):
223
assert B.size(0) == 0
224
assert B.size(1) == 0
226
def test_copy_matrix(self, any_backend):
227
A0, B0 = self.assemble_matrices()
230
assert A0.size(0) == A1.size(0)
231
assert A0.size(1) == A1.size(1)
232
assert A0.norm("frobenius") == A1.norm("frobenius")
235
assert B0.size(0) == B1.size(0)
236
assert B0.size(1) == B1.size(1)
237
assert round(B0.norm("frobenius") - B1.norm("frobenius"), 7) == 0
239
def test_compress_matrix(self, any_backend):
240
self.backend, self.sub_backend = any_backend
242
A, B = self.assemble_matrices()
243
A_norm = A.norm('frobenius')
246
C_norm = C.norm('frobenius')
247
assert round(A_norm - C_norm, 7) == 0
249
@pytest.mark.skipif(MPI.size(mpi_comm_world()) > 1, reason="Disabled because it tends to crash the tests in parallel.")
251
def test_ident_zeros(self, use_backend, any_backend):
252
self.backend, self.sub_backend = any_backend
254
# Check that PETScMatrix::ident_zeros() rethrows PETSc error
255
if self.backend[0:5] == "PETSc":
256
A, B = self.assemble_matrices(use_backend=use_backend)
257
with pytest.raises(Exception):
260
# Assemble matrix A with diagonal entries
261
A, B = self.assemble_matrices(use_backend=use_backend, keep_diagonal=True)
265
for i in range(A.local_range(0)[0], A.local_range(0)[1]):
267
if sum(abs(row)) < DOLFIN_EPS:
270
# Set zero rows to (0,...,0, 1, 0,...,0)
275
cols = A.getrow(i)[0]
277
for j in range(cols.size + 1):
279
assert round(row[j] - 1.0, 7) == 0
282
assert round(sum(abs(row)) - 1.0, 7) == 0
284
def test_setting_diagonal(self, use_backend, any_backend):
285
self.backend, self.sub_backend = any_backend
287
mesh = UnitSquareMesh(21, 23)
289
V = FunctionSpace(mesh, "Lagrange", 2)
290
W = FunctionSpace(mesh, "Lagrange", 1)
296
backend = globals()[self.backend + self.sub_backend + 'Factory'].instance()
300
B = assemble(u*v*dx(), backend=backend, keep_diagonal=True)
302
b = assemble(action(u*v*dx(), Constant(1)))
309
A.init_vector(resultsA, 1)
310
B.init_vector(resultsB, 1)
315
A.mult(ones, resultsA)
316
B.mult(ones, resultsB)
317
assert round(resultsA.norm("l2") - resultsB.norm("l2"), 7) == 0
319
#def test_create_from_sparsity_pattern(self):
321
#def test_size(self):
323
#def test_local_range(self):
325
#def test_zero(self):
327
#def test_apply(self):
331
#def test_resize(self):
334
# Test the access of the raw data through pointers
335
# This is only available for uBLAS and MTL4 backends
336
def test_matrix_data(self, use_backend, data_backend):
337
""" Test for ordinary Matrix"""
338
self.backend, self.sub_backend = data_backend
340
# Tests bailout for this choice
341
if self.backend == "uBLAS" and \
342
(not use_backend or self.sub_backend =="Dense"):
345
A, B = self.assemble_matrices(use_backend)
347
rows, cols, values = A.data()
349
for row in range(A.size(0)):
350
for col in range(rows[row], rows[row+1]):
351
assert array[row, cols[col]] == values[i]
354
# Test none writeable of a shallow copy of the data
355
rows, cols, values = A.data(False)
356
def write_data(data):
358
with pytest.raises(Exception):
360
with pytest.raises(Exception):
362
with pytest.raises(Exception):
365
# Test for as_backend_typeed Matrix
366
A = as_backend_type(A)
367
rows, cols, values = A.data()
368
for row in range(A.size(0)):
369
for k in range(rows[row], rows[row+1]):
370
assert array[row,cols[k]] == values[k]
373
def test_matrix_no_data(self, no_data_backend):
374
self.backend, self.sub_backend = no_data_backend
376
A, B = self.assemble_matrices()
377
with pytest.raises(RuntimeError):
380
A = as_backend_type(A)
381
with pytest.raises(RuntimeError):
385
def test_matrix_nnz(self, any_backend):
386
A, B = self.assemble_matrices()
387
assert A.nnz() == 2992
388
assert B.nnz() == 9398