Revision eb4c6f11d0e74b43b6256fc18569be1f80816977 authored by Alex Nitz on 13 April 2021, 14:19:23 UTC, committed by GitHub on 13 April 2021, 14:19:23 UTC
1 parent 87ce2d5
test_schemes.py
# Copyright (C) 2012 Alex Nitz, Andrew Miller, Josh Willis
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# =============================================================================
#
# Preamble
#
# =============================================================================
#
'''
The tests in this file are designed to ensure that the schemes behave as designed,
and that data are moved to and from the GPU as they should, or apprpriate exceptions
are raised. We only attempt this for two representative functions: one basic
arithemtic operation, and one that should *not* move its data (regardless of scheme).
We do not specifically test that the lalwrapped functions raise exceptions from the
GPU, because that test is done in the test_lalwrap unit tests.
'''
import pycbc
import unittest
from pycbc.types import *
from pycbc.scheme import *
import numpy
from numpy import dtype, float32, float64, complex64, complex128
import lal
from utils import parse_args_all_schemes, simple_exit
_scheme, _context = parse_args_all_schemes("Scheme")
# By importing the current schemes array type, it will make it
# easier to check the array types later
if isinstance(_context,CUDAScheme):
import pycuda
import pycuda.gpuarray
from pycuda.gpuarray import GPUArray as SchemeArray
elif isinstance(_context,CPUScheme):
from numpy import ndarray as SchemeArray
from numpy import ndarray as CPUArray
class SchemeTestBase(unittest.TestCase):
__test__ = False
def setUp(self):
self.context = _context
self.scheme = _scheme
# Determine kind (real or complex) from dtype:
if self.dtype == float32 or self.dtype == float64:
self.kind = 'real'
else:
self.kind = 'complex'
if self.odtype == float32 or self.odtype == float64:
self.okind = 'real'
else:
self.okind = 'complex'
# Now set up the arrays we'll need. We run this from a factory
# constructor that creates many different instances for the
# various kind/precision combinations.
if self.kind == 'real':
self.a = Array([5,3,1],dtype=self.dtype)
if self.okind == 'real':
self.b = Array([10,8,6],dtype=self.odtype)
self.answer = Array([50,24,6],dtype=self.dtype)
else:
self.b = Array([10+6j,8+4j,6+2j],dtype=self.odtype)
self.answer = Array([50+30j,24+12j,6+2j],dtype=self.odtype)
else:
self.a = Array([5+1j,3+3j,1+5j],dtype=self.dtype)
if self.okind == 'real':
self.b = Array([10,8,6],dtype=self.odtype)
self.answer = Array([50+10j,24+24j,6+30j],dtype=self.dtype)
else:
self.b = Array([10+6j,8+4j,6+2j],dtype=self.odtype)
self.answer = Array([44+40j,12+36j,-4+32j],dtype=self.dtype)
def test_move(self):
'''
This test uses the __mul__ special method to verify that arrays are moved
on and off of the GPU automatically when they should be, and that the _scheme
property and array types are correct for the executing architecture.
'''
# Make some copies
a1 = type(self.a)(self.a)
a2 = type(self.a)(self.a)
b1 = type(self.b)(self.b)
with self.context:
# The following should move both of a1 and b1 onto the GPU (if self.context
# isn't CPU)
c = a1 * b1
# Check that the data types are correct
self.assertTrue(isinstance(a1._data, SchemeArray))
self.assertTrue(isinstance(b1._data, SchemeArray))
self.assertTrue(isinstance(c._data, SchemeArray))
# Check that schemes are correct
self.assertTrue(isinstance(a1._scheme, type(self.context)))
self.assertTrue(isinstance(b1._scheme, type(self.context)))
self.assertTrue(isinstance(c._scheme, type(self.context)))
# And finally check that the values are correct
self.assertEqual(a1,self.a)
self.assertEqual(b1,self.b)
self.assertEqual(c,self.answer)
# Now check that nothing about a2 has changed, since it wasn't involved
# in the computation
self.assertTrue(isinstance(a2._data, CPUArray))
self.assertTrue(isinstance(a2._scheme, DefaultScheme))
self.assertEqual(a2,self.a)
# Now move back to the CPU, and check that everything is correctly
# transferred:
c = a1 * b1
# Check that schemes are correct
self.assertTrue(isinstance(a1._scheme, DefaultScheme))
self.assertTrue(isinstance(b1._scheme, DefaultScheme))
self.assertTrue(isinstance(c._scheme, DefaultScheme))
# Check that the data types are correct
self.assertTrue(isinstance(a1._data, CPUArray))
self.assertTrue(isinstance(b1._data, CPUArray))
self.assertTrue(isinstance(c.data, CPUArray))
# And finally check that the values are correct
self.assertEqual(a1,self.a)
self.assertEqual(b1,self.b)
self.assertEqual(c,self.answer)
def test_do_not_move(self):
'''
This test checks that the __eq__ special method (invoked via the
'==' operator) does *not* change the scheme or type, since it
does its comparisons by copying from the CPU to GPU, but should
leave the original arrays in place, with their data properties and
schemes unchanged.
'''
acopy = type(self.a)(self.a)
with self.context:
# Force a move to the GPU by trivially multiplying by one:
a1 = acopy*1
a2 = acopy*1
truth = (a1 == a2)
# Now verify that nothing moved
self.assertTrue(isinstance(a1._scheme, type(self.context)))
self.assertTrue(isinstance(a2._scheme, type(self.context)))
self.assertTrue(isinstance(a1.data, SchemeArray))
self.assertTrue(isinstance(a2.data, SchemeArray))
# Now the function that creates our various classes
def scheme_test_maker(dtype,odtype):
class tests(SchemeTestBase):
__test__ = True
def __init__(self,*args):
self.dtype = dtype
self.odtype = odtype
unittest.TestCase.__init__(self,*args)
tests.__name__ = _scheme + " " + dtype.__name__ + " with " + odtype.__name__
return tests
types = [ (float32,[float32,complex64]), (float64,[float64,complex128]),
(complex64,[complex64,float32]), (complex128,[float64,complex128]) ]
suite = unittest.TestSuite()
ind = 0
for ty,oktype in types:
for ot in oktype:
na = 'test' + str(ind)
vars()[na] = scheme_test_maker(ty,ot)
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(vars()[na]))
ind += 1
if __name__ == '__main__':
results = unittest.TextTestRunner(verbosity=2).run(suite)
simple_exit(results)
Computing file changes ...