1from petsc4py import PETSc 2import unittest, numpy 3from sys import getrefcount 4# -------------------------------------------------------------------- 5 6class Matrix(object): 7 8 def __init__(self): 9 pass 10 11 def create(self, mat): 12 pass 13 14 def destroy(self, mat): 15 pass 16 17class ScaledIdentity(Matrix): 18 19 s = 2.0 20 21 def scale(self, mat, s): 22 self.s *= s 23 24 def shift(self, mat, s): 25 self.s += s 26 27 def mult(self, mat, x, y): 28 x.copy(y) 29 y.scale(self.s) 30 31 def getDiagonal(self, mat, vd): 32 vd.set(self.s) 33 34 def productSetFromOptions(self, mat, producttype, A, B, C): 35 return True 36 37 def productSymbolic(self, mat, product, producttype, A, B, C): 38 if producttype == 'AB': 39 if mat is A: # product = identity * B 40 product.setType(B.getType()) 41 product.setSizes(B.getSizes()) 42 product.setUp() 43 product.assemble() 44 B.copy(product) 45 elif mat is B: # product = A * identity 46 product.setType(A.getType()) 47 product.setSizes(A.getSizes()) 48 product.setUp() 49 product.assemble() 50 A.copy(product) 51 else: 52 raise RuntimeError('wrong configuration') 53 elif producttype == 'AtB': 54 if mat is A: # product = identity^T * B 55 product.setType(B.getType()) 56 product.setSizes(B.getSizes()) 57 product.setUp() 58 product.assemble() 59 B.copy(product) 60 elif mat is B: # product = A^T * identity 61 tmp = PETSc.Mat() 62 A.transpose(tmp) 63 product.setType(tmp.getType()) 64 product.setSizes(tmp.getSizes()) 65 product.setUp() 66 product.assemble() 67 tmp.copy(product) 68 else: 69 raise RuntimeError('wrong configuration') 70 elif producttype == 'ABt': 71 if mat is A: # product = identity * B^T 72 tmp = PETSc.Mat() 73 B.transpose(tmp) 74 product.setType(tmp.getType()) 75 product.setSizes(tmp.getSizes()) 76 product.setUp() 77 product.assemble() 78 tmp.copy(product) 79 elif mat is B: # product = A * identity^T 80 product.setType(A.getType()) 81 product.setSizes(A.getSizes()) 82 product.setUp() 83 product.assemble() 84 A.copy(product) 85 else: 86 raise RuntimeError('wrong configuration') 87 elif producttype == 'PtAP': 88 if mat is A: # product = P^T * identity * P 89 self.tmp = PETSc.Mat() 90 B.transposeMatMult(B, self.tmp) 91 product.setType(self.tmp.getType()) 92 product.setSizes(self.tmp.getSizes()) 93 product.setUp() 94 product.assemble() 95 self.tmp.copy(product) 96 elif mat is B: # product = identity^T * A * identity 97 product.setType(A.getType()) 98 product.setSizes(A.getSizes()) 99 product.setUp() 100 product.assemble() 101 A.copy(product) 102 else: 103 raise RuntimeError('wrong configuration') 104 elif producttype == 'RARt': 105 if mat is A: # product = R * identity * R^t 106 self.tmp = PETSc.Mat() 107 B.matTransposeMult(B, self.tmp) 108 product.setType(self.tmp.getType()) 109 product.setSizes(self.tmp.getSizes()) 110 product.setUp() 111 product.assemble() 112 self.tmp.copy(product) 113 elif mat is B: # product = identity * A * identity^T 114 product.setType(A.getType()) 115 product.setSizes(A.getSizes()) 116 product.setUp() 117 product.assemble() 118 A.copy(product) 119 else: 120 raise RuntimeError('wrong configuration') 121 elif producttype == 'ABC': 122 if mat is A: # product = identity * B * C 123 self.tmp = PETSc.Mat() 124 B.matMult(C, self.tmp) 125 product.setType(self.tmp.getType()) 126 product.setSizes(self.tmp.getSizes()) 127 product.setUp() 128 product.assemble() 129 self.tmp.copy(product) 130 elif mat is B: # product = A * identity * C 131 self.tmp = PETSc.Mat() 132 A.matMult(C, self.tmp) 133 product.setType(self.tmp.getType()) 134 product.setSizes(self.tmp.getSizes()) 135 product.setUp() 136 product.assemble() 137 self.tmp.copy(product) 138 elif mat is C: # product = A * B * identity 139 self.tmp = PETSc.Mat() 140 A.matMult(B, self.tmp) 141 product.setType(self.tmp.getType()) 142 product.setSizes(self.tmp.getSizes()) 143 product.setUp() 144 product.assemble() 145 self.tmp.copy(product) 146 else: 147 raise RuntimeError('wrong configuration') 148 else: 149 raise RuntimeError('Product {} not implemented'.format(producttype)) 150 product.zeroEntries() 151 152 def productNumeric(self, mat, product, producttype, A, B, C): 153 if producttype == 'AB': 154 if mat is A: # product = identity * B 155 B.copy(product, structure=True) 156 elif mat is B: # product = A * identity 157 A.copy(product, structure=True) 158 else: 159 raise RuntimeError('wrong configuration') 160 product.scale(self.s) 161 elif producttype == 'AtB': 162 if mat is A: # product = identity^T * B 163 B.copy(product, structure=True) 164 elif mat is B: # product = A^T * identity 165 A.transpose(product) 166 else: 167 raise RuntimeError('wrong configuration') 168 product.scale(self.s) 169 elif producttype == 'ABt': 170 if mat is A: # product = identity * B^T 171 B.transpose(product) 172 elif mat is B: # product = A * identity^T 173 A.copy(product, structure=True) 174 else: 175 raise RuntimeError('wrong configuration') 176 product.scale(self.s) 177 elif producttype == 'PtAP': 178 if mat is A: # product = P^T * identity * P 179 B.transposeMatMult(B, self.tmp) 180 self.tmp.copy(product, structure=True) 181 product.scale(self.s) 182 elif mat is B: # product = identity^T * A * identity 183 A.copy(product, structure=True) 184 product.scale(self.s**2) 185 else: 186 raise RuntimeError('wrong configuration') 187 elif producttype == 'RARt': 188 if mat is A: # product = R * identity * R^t 189 B.matTransposeMult(B, self.tmp) 190 self.tmp.copy(product, structure=True) 191 product.scale(self.s) 192 elif mat is B: # product = identity * A * identity^T 193 A.copy(product, structure=True) 194 product.scale(self.s**2) 195 else: 196 raise RuntimeError('wrong configuration') 197 elif producttype == 'ABC': 198 if mat is A: # product = identity * B * C 199 B.matMult(C, self.tmp) 200 self.tmp.copy(product, structure=True) 201 elif mat is B: # product = A * identity * C 202 A.matMult(C, self.tmp) 203 self.tmp.copy(product, structure=True) 204 elif mat is C: # product = A * B * identity 205 A.matMult(B, self.tmp) 206 self.tmp.copy(product, structure=True) 207 else: 208 raise RuntimeError('wrong configuration') 209 product.scale(self.s) 210 else: 211 raise RuntimeError('Product {} not implemented'.format(producttype)) 212 213class Diagonal(Matrix): 214 215 def create(self, mat): 216 super(Diagonal,self).create(mat) 217 mat.setUp() 218 self.D = mat.createVecLeft() 219 220 def destroy(self, mat): 221 self.D.destroy() 222 super(Diagonal,self).destroy(mat) 223 224 def scale(self, mat, a): 225 self.D.scale(a) 226 227 def shift(self, mat, a): 228 self.D.shift(a) 229 230 def zeroEntries(self, mat): 231 self.D.zeroEntries() 232 233 def mult(self, mat, x, y): 234 y.pointwiseMult(x, self.D) 235 236 def getDiagonal(self, mat, vd): 237 self.D.copy(vd) 238 239 def setDiagonal(self, mat, vd, im): 240 if isinstance (im, bool): 241 addv = im 242 if addv: 243 self.D.axpy(1, vd) 244 else: 245 vd.copy(self.D) 246 elif im == PETSc.InsertMode.INSERT_VALUES: 247 vd.copy(self.D) 248 elif im == PETSc.InsertMode.ADD_VALUES: 249 self.D.axpy(1, vd) 250 else: 251 raise ValueError('wrong InsertMode %d'% im) 252 253 def diagonalScale(self, mat, vl, vr): 254 if vl: self.D.pointwiseMult(self.D, vl) 255 if vr: self.D.pointwiseMult(self.D, vr) 256 257# -------------------------------------------------------------------- 258 259class TestMatrix(unittest.TestCase): 260 261 COMM = PETSc.COMM_WORLD 262 PYMOD = __name__ 263 PYCLS = 'Matrix' 264 265 def _getCtx(self): 266 return self.A.getPythonContext() 267 268 def setUp(self): 269 N = self.N = 10 270 self.A = PETSc.Mat() 271 if 0: # command line way 272 self.A.create(self.COMM) 273 self.A.setSizes([N,N]) 274 self.A.setType('python') 275 OptDB = PETSc.Options(self.A) 276 OptDB['mat_python_type'] = '%s.%s' % (self.PYMOD,self.PYCLS) 277 self.A.setFromOptions() 278 self.A.setUp() 279 del OptDB['mat_python_type'] 280 self.assertTrue(self._getCtx() is not None) 281 else: # python way 282 context = globals()[self.PYCLS]() 283 self.A.createPython([N,N], context, comm=self.COMM) 284 self.A.setUp() 285 self.assertTrue(self._getCtx() is context) 286 self.assertEqual(getrefcount(context), 3) 287 del context 288 self.assertEqual(getrefcount(self._getCtx()), 2) 289 290 def tearDown(self): 291 ctx = self.A.getPythonContext() 292 self.assertEqual(getrefcount(ctx), 3) 293 self.A.destroy() # XXX 294 self.A = None 295 self.assertEqual(getrefcount(ctx), 2) 296 #import gc,pprint; pprint.pprint(gc.get_referrers(ctx)) 297 298 def testBasic(self): 299 ctx = self.A.getPythonContext() 300 self.assertTrue(self._getCtx() is ctx) 301 self.assertEqual(getrefcount(ctx), 3) 302 303 def testZeroEntries(self): 304 f = lambda : self.A.zeroEntries() 305 self.assertRaises(Exception, f) 306 307 def testMult(self): 308 x, y = self.A.createVecs() 309 f = lambda : self.A.mult(x, y) 310 self.assertRaises(Exception, f) 311 312 def testMultTranspose(self): 313 x, y = self.A.createVecs() 314 f = lambda : self.A.multTranspose(x, y) 315 self.assertRaises(Exception, f) 316 317 def testGetDiagonal(self): 318 d = self.A.createVecLeft() 319 f = lambda : self.A.getDiagonal(d) 320 self.assertRaises(Exception, f) 321 322 def testSetDiagonal(self): 323 d = self.A.createVecLeft() 324 f = lambda : self.A.setDiagonal(d) 325 self.assertRaises(Exception, f) 326 327 def testDiagonalScale(self): 328 x, y = self.A.createVecs() 329 f = lambda : self.A.diagonalScale(x, y) 330 self.assertRaises(Exception, f) 331 332class TestScaledIdentity(TestMatrix): 333 334 PYCLS = 'ScaledIdentity' 335 336 def testMult(self): 337 s = self._getCtx().s 338 x, y = self.A.createVecs() 339 x.setRandom() 340 self.A.mult(x,y) 341 self.assertTrue(y.equal(s*x)) 342 343 def testMultTransposeSymmKnown(self): 344 s = self._getCtx().s 345 x, y = self.A.createVecs() 346 x.setRandom() 347 self.A.setOption(PETSc.Mat.Option.SYMMETRIC, True) 348 self.A.multTranspose(x,y) 349 self.assertTrue(y.equal(s*x)) 350 self.A.setOption(PETSc.Mat.Option.SYMMETRIC, False) 351 f = lambda : self.A.multTranspose(x, y) 352 self.assertRaises(Exception, f) 353 354 def testMultTransposeNewMeth(self): 355 s = self._getCtx().s 356 x, y = self.A.createVecs() 357 x.setRandom() 358 AA = self.A.getPythonContext() 359 AA.multTranspose = AA.mult 360 self.A.multTranspose(x,y) 361 del AA.multTranspose 362 self.assertTrue(y.equal(s*x)) 363 364 def testGetDiagonal(self): 365 s = self._getCtx().s 366 d = self.A.createVecLeft() 367 o = d.duplicate() 368 o.set(s) 369 self.A.getDiagonal(d) 370 self.assertTrue(o.equal(d)) 371 372 def testMatMat(self): 373 s = self._getCtx().s 374 R = PETSc.Random().create(self.COMM) 375 R.setFromOptions() 376 A = PETSc.Mat().create(self.COMM) 377 A.setSizes(self.A.getSizes()) 378 A.setType(PETSc.Mat.Type.AIJ) 379 A.setUp() 380 A.setRandom(R) 381 B = PETSc.Mat().create(self.COMM) 382 B.setSizes(self.A.getSizes()) 383 B.setType(PETSc.Mat.Type.AIJ) 384 B.setUp() 385 B.setRandom(R) 386 I = PETSc.Mat().create(self.COMM) 387 I.setSizes(self.A.getSizes()) 388 I.setType(PETSc.Mat.Type.AIJ) 389 I.setUp() 390 I.assemble() 391 I.shift(s) 392 393 self.assertTrue(self.A.matMult(A).equal(I.matMult(A))) 394 self.assertTrue(A.matMult(self.A).equal(A.matMult(I))) 395 if self.A.getComm().Get_size() == 1: 396 self.assertTrue(self.A.matTransposeMult(A).equal(I.matTransposeMult(A))) 397 self.assertTrue(A.matTransposeMult(self.A).equal(A.matTransposeMult(I))) 398 self.assertTrue(self.A.transposeMatMult(A).equal(I.transposeMatMult(A))) 399 self.assertTrue(A.transposeMatMult(self.A).equal(A.transposeMatMult(I))) 400 self.assertAlmostEqual((self.A.ptap(A) - I.ptap(A)).norm(), 0.0, places=5) 401 self.assertAlmostEqual((A.ptap(self.A) - A.ptap(I)).norm(), 0.0, places=5) 402 if self.A.getComm().Get_size() == 1: 403 self.assertAlmostEqual((self.A.rart(A) - I.rart(A)).norm(), 0.0, places=5) 404 self.assertAlmostEqual((A.rart(self.A) - A.rart(I)).norm(), 0.0, places=5) 405 self.assertAlmostEqual((self.A.matMatMult(A,B)-I.matMatMult(A,B)).norm(), 0.0, places=5) 406 self.assertAlmostEqual((A.matMatMult(self.A,B)-A.matMatMult(I,B)).norm(), 0.0, places=5) 407 self.assertAlmostEqual((A.matMatMult(B,self.A)-A.matMatMult(B,I)).norm(), 0.0, places=5) 408 409 def testH2Opus(self): 410 if not PETSc.Sys.hasExternalPackage("h2opus"): 411 return 412 if self.A.getComm().Get_size() > 1: 413 return 414 h = PETSc.Mat() 415 416 # need transpose operation for norm estimation 417 AA = self.A.getPythonContext() 418 AA.multTranspose = AA.mult 419 420 # without coordinates 421 h.createH2OpusFromMat(self.A,leafsize=2) 422 h.assemble() 423 h.destroy() 424 425 # with coordinates 426 coords = numpy.linspace((1,2,3),(10,20,30),self.A.getSize()[0],dtype=PETSc.RealType) 427 h.createH2OpusFromMat(self.A,coords,leafsize=2) 428 h.assemble() 429 h.destroy() 430 431 del AA.multTranspose 432 433 def testShift(self): 434 sold = self._getCtx().s 435 self.A.shift(-0.5) 436 s = self._getCtx().s 437 self.assertTrue(s == sold - 0.5) 438 439 def testScale(self): 440 sold = self._getCtx().s 441 self.A.scale(-0.5) 442 s = self._getCtx().s 443 self.assertTrue(s == sold * -0.5) 444 445class TestDiagonal(TestMatrix): 446 447 PYCLS = 'Diagonal' 448 449 def setUp(self): 450 super(TestDiagonal, self).setUp() 451 D = self.A.createVecLeft() 452 s, e = D.getOwnershipRange() 453 for i in range(s, e): 454 D[i] = i+1 455 D.assemble() 456 self.A.setDiagonal(D) 457 458 def testZeroEntries(self): 459 self.A.zeroEntries() 460 D = self._getCtx().D 461 self.assertEqual(D.norm(), 0) 462 463 def testMult(self): 464 x, y = self.A.createVecs() 465 x.set(1) 466 self.A.mult(x,y) 467 self.assertTrue(y.equal(self._getCtx().D)) 468 469 def testMultTransposeSymmKnown(self): 470 x, y = self.A.createVecs() 471 x.set(1) 472 self.A.setOption(PETSc.Mat.Option.SYMMETRIC, True) 473 self.A.multTranspose(x,y) 474 self.assertTrue(y.equal(self._getCtx().D)) 475 self.A.setOption(PETSc.Mat.Option.SYMMETRIC, False) 476 f = lambda : self.A.multTranspose(x, y) 477 self.assertRaises(Exception, f) 478 479 def testMultTransposeNewMeth(self): 480 x, y = self.A.createVecs() 481 x.set(1) 482 AA = self.A.getPythonContext() 483 AA.multTranspose = AA.mult 484 self.A.multTranspose(x,y) 485 del AA.multTranspose 486 self.assertTrue(y.equal(self._getCtx().D)) 487 488 def testGetDiagonal(self): 489 d = self.A.createVecLeft() 490 self.A.getDiagonal(d) 491 self.assertTrue(d.equal(self._getCtx().D)) 492 493 def testSetDiagonal(self): 494 d = self.A.createVecLeft() 495 d.setRandom() 496 self.A.setDiagonal(d) 497 self.assertTrue(d.equal(self._getCtx().D)) 498 499 def testDiagonalScale(self): 500 x, y = self.A.createVecs() 501 x.set(2) 502 y.set(3) 503 old = self._getCtx().D.copy() 504 self.A.diagonalScale(x, y) 505 D = self._getCtx().D 506 self.assertTrue(D.equal(old*6)) 507 508 def testCreateTranspose(self): 509 A = self.A 510 A.setOption(PETSc.Mat.Option.SYMMETRIC, True) 511 AT = PETSc.Mat().createTranspose(A) 512 x, y = A.createVecs() 513 xt, yt = AT.createVecs() 514 # 515 y.setRandom() 516 A.multTranspose(y, x) 517 y.copy(xt) 518 AT.mult(xt, yt) 519 self.assertTrue(yt.equal(x)) 520 # 521 x.setRandom() 522 A.mult(x, y) 523 x.copy(yt) 524 AT.multTranspose(yt, xt) 525 self.assertTrue(xt.equal(y)) 526 del A 527 528 def testConvert(self): 529 self.assertTrue(self.A.convert(PETSc.Mat.Type.AIJ,PETSc.Mat()).equal(self.A)) 530 self.assertTrue(self.A.convert(PETSc.Mat.Type.BAIJ,PETSc.Mat()).equal(self.A)) 531 self.assertTrue(self.A.convert(PETSc.Mat.Type.SBAIJ,PETSc.Mat()).equal(self.A)) 532 self.assertTrue(self.A.convert(PETSc.Mat.Type.DENSE,PETSc.Mat()).equal(self.A)) 533 534 def testShift(self): 535 old = self._getCtx().D.copy() 536 self.A.shift(-0.5) 537 D = self._getCtx().D 538 self.assertTrue(D.equal(old-0.5)) 539 540 def testScale(self): 541 old = self._getCtx().D.copy() 542 self.A.scale(-0.5) 543 D = self._getCtx().D 544 self.assertTrue(D.equal(-0.5*old)) 545 546 547# -------------------------------------------------------------------- 548 549if __name__ == '__main__': 550 unittest.main() 551