def test_implementation(self, test, parameters_dict): """Test computed values for implementations generated for all *backends* and *stencil suites*. The generated implementations are reused from previous tests by means of a :class:`utils.ImplementationsDB` instance shared at module scope. """ # backend = "debug" cls = type(self) implementation_list = test["implementations"] if not implementation_list: pytest.skip( "Cannot perform validation tests, since there are no valid implementations." ) for implementation in implementation_list: if not isinstance(implementation, StencilObject): raise RuntimeError( "Wrong function got from implementations_db cache!") fields, exec_info = parameters_dict # Domain from gt4py.definitions import Shape from gt4py.ir.nodes import Index origin = cls.origin shapes = {} for name, field in [(k, v) for k, v in fields.items() if isinstance(v, np.ndarray)]: shapes[name] = Shape(field.shape) max_domain = Shape([sys.maxsize] * implementation.domain_info.ndims) for name, shape in shapes.items(): upper_boundary = Index( [b[1] for b in cls.symbols[name].boundary]) max_domain &= shape - (Index(origin) + upper_boundary) domain = max_domain max_boundary = ((0, 0), (0, 0), (0, 0)) for name, info in implementation.field_info.items(): if isinstance(info, gt_definitions.FieldInfo): max_boundary = tuple( (max(m[0], abs(b[0])), max(m[1], b[1])) for m, b in zip(max_boundary, info.boundary)) new_boundary = tuple( (max(abs(b[0]), abs(mb[0])), max(abs(b[1]), abs(mb[1]))) for b, mb in zip(cls.max_boundary, max_boundary)) shape = None for name, field in fields.items(): if isinstance(field, np.ndarray): assert field.shape == (shape if shape is not None else field.shape) shape = field.shape patched_origin = tuple(nb[0] for nb in new_boundary) patching_origin = tuple(po - o for po, o in zip(patched_origin, origin)) patched_shape = tuple(nb[0] + nb[1] + d for nb, d in zip(new_boundary, domain)) patching_slices = [ slice(po, po + s) for po, s in zip(patching_origin, shape) ] for k, v in implementation.constants.items(): sys.modules[self.__module__].__dict__[k] = v inputs = {} for k, f in fields.items(): if isinstance(f, np.ndarray): patched_f = np.empty(shape=patched_shape) patched_f[patching_slices] = f inputs[k] = gt_storage.from_array( patched_f, dtype=test["definition"].__annotations__[k], shape=patched_f.shape, default_origin=patched_origin, backend=test["backend"], ) else: inputs[k] = f validation_fields = { name: np.array(field, copy=True) for name, field in inputs.items() } implementation(**inputs, origin=patched_origin, exec_info=exec_info) domain = exec_info["domain"] validation_origins = { name: tuple( nb[0] - g[0] for nb, g in zip(new_boundary, cls.symbols[name].boundary)) for name in implementation.field_info.keys() } validation_shapes = { name: tuple(d + g[0] + g[1] for d, g in zip(domain, cls.symbols[name].boundary)) for name in implementation.field_info.keys() } validation_field_views = { name: field[tuple( slice(o, o + s) for o, s in zip(validation_origins[name], validation_shapes[name]))] if name in implementation.field_info else field # parameters for name, field in validation_fields.items() } cls.validation( **validation_field_views, domain=domain, origin={ name: tuple(b[0] for b in cls.symbols[name].boundary) for name in validation_fields if name in implementation.field_info }, ) # Test values for (name, value), (expected_name, expected_value) in zip(inputs.items(), validation_fields.items()): if isinstance(fields[name], np.ndarray): domain_slice = [ slice(new_boundary[d][0], new_boundary[d][0] + domain[d]) for d in range(len(domain)) ] np.testing.assert_allclose( value.data[domain_slice], expected_value[domain_slice], rtol=RTOL, atol=ATOL, equal_nan=EQUAL_NAN, err_msg="Wrong data in output field '{name}'".format( name=name), )
def _run_test_implementation(self, parameters_dict, implementation): cls = type(self) fields, exec_info = parameters_dict # Domain from gt4py.definitions import Shape from gt4py.ir.nodes import Index origin = cls.origin shapes = {} for name, field in [(k, v) for k, v in fields.items() if isinstance(v, np.ndarray)]: shapes[name] = Shape(field.shape) max_domain = Shape([sys.maxsize] * implementation.domain_info.ndims) for name, shape in shapes.items(): upper_boundary = Index([b[1] for b in cls.symbols[name].boundary]) max_domain &= shape - (Index(origin) + upper_boundary) domain = max_domain max_boundary = ((0, 0), (0, 0), (0, 0)) for info in implementation.field_info.values(): if isinstance(info, gt_definitions.FieldInfo): max_boundary = tuple( (max(m[0], abs(b[0])), max(m[1], b[1])) for m, b in zip(max_boundary, info.boundary) ) new_boundary = tuple( (max(abs(b[0]), abs(mb[0])), max(abs(b[1]), abs(mb[1]))) for b, mb in zip(cls.max_boundary, max_boundary) ) shape = None for field in fields.values(): if isinstance(field, np.ndarray): assert field.shape == (shape if shape is not None else field.shape) shape = field.shape patched_origin = tuple(nb[0] for nb in new_boundary) patching_origin = tuple(po - o for po, o in zip(patched_origin, origin)) patched_shape = tuple(nb[0] + nb[1] + d for nb, d in zip(new_boundary, domain)) patching_slices = [slice(po, po + s) for po, s in zip(patching_origin, shape)] for k, v in implementation.constants.items(): sys.modules[self.__module__].__dict__[k] = v inputs = {} for k, f in fields.items(): if isinstance(f, np.ndarray): patched_f = np.empty(shape=patched_shape) patched_f[patching_slices] = f inputs[k] = gt_storage.from_array( patched_f, dtype=f.dtype, shape=patched_f.shape, default_origin=patched_origin, backend=implementation.backend, ) else: inputs[k] = f # remove unused input parameters inputs = {key: value for key, value in inputs.items() if value is not None} validation_fields = {name: np.array(field, copy=True) for name, field in inputs.items()} implementation(**inputs, origin=patched_origin, exec_info=exec_info) domain = exec_info["domain"] validation_origins = { name: tuple(nb[0] - g[0] for nb, g in zip(new_boundary, cls.symbols[name].boundary)) for name in inputs if name in implementation.field_info } validation_shapes = { name: tuple(d + g[0] + g[1] for d, g in zip(domain, cls.symbols[name].boundary)) for name in inputs if name in implementation.field_info } validation_field_views = { name: field[ tuple( slice(o, o + s) for o, s in zip(validation_origins[name], validation_shapes[name]) ) ] if name in implementation.field_info else field # parameters for name, field in validation_fields.items() } cls.validation( **validation_field_views, domain=domain, origin={ name: tuple(b[0] for b in cls.symbols[name].boundary) for name in validation_fields if name in implementation.field_info }, ) # Test values for (name, value), expected_value in zip(inputs.items(), validation_fields.values()): if isinstance(fields[name], np.ndarray): domain_slice = [ slice(new_boundary[d][0], new_boundary[d][0] + domain[d]) for d in range(len(domain)) ] if gt_backend.from_name(value.backend).storage_info["device"] == "gpu": value.synchronize() value = value.data.get() else: value = value.data np.testing.assert_allclose( value[domain_slice], expected_value[domain_slice], rtol=RTOL, atol=ATOL, equal_nan=EQUAL_NAN, err_msg="Wrong data in output field '{name}'".format(name=name), )
def _run_test_implementation(cls, parameters_dict, implementation): # noqa: C901 # too complex input_data, exec_info = parameters_dict origin = cls.origin max_boundary = Boundary(cls.max_boundary) field_params = cls.field_params field_masks = {} for name, value in input_data.items(): if isinstance(value, np.ndarray): field_masks[name] = tuple( ax in field_params[name][0] for ax in CartesianSpace.names ) data_shape = Shape((sys.maxsize,) * 3) for name, data in input_data.items(): if isinstance(data, np.ndarray): data_shape &= Shape( interpolate_mask(data.shape, field_masks[name], default=sys.maxsize) ) domain = data_shape - ( Index(max_boundary.lower_indices) + Index(max_boundary.upper_indices) ) referenced_inputs = { name: info for name, info in implementation.field_info.items() if info is not None } referenced_inputs.update( {name: info for name, info in implementation.parameter_info.items() if info is not None} ) # set externals for validation method for k, v in implementation.constants.items(): sys.modules[cls.__module__].__dict__[k] = v # copy input data test_values = {} validation_values = {} for name, data in input_data.items(): data = input_data[name] if name in referenced_inputs: info = referenced_inputs[name] if isinstance(info, FieldInfo): data_dims = field_params[name][1] if data_dims: dtype = (data.dtype, data_dims) shape = data.shape[: -len(data_dims)] else: dtype = data.dtype shape = data.shape test_values[name] = gt_storage.from_array( data, dtype=dtype, shape=shape, mask=field_masks[name], default_origin=origin, backend=implementation.backend, ) validation_values[name] = np.array(data) else: test_values[name] = data validation_values[name] = data else: test_values[name] = None validation_values[name] = None # call implementation implementation(**test_values, origin=origin, exec_info=exec_info) assert domain == exec_info["domain"] # for validation data, data is cropped to actually touched domain, so that origin offseting # does not have to be implemented for every test suite. This is done based on info # specified in test suite cropped_validation_values = {} for name, data in validation_values.items(): sym = cls.symbols[name] if data is not None and sym.kind == SymbolKind.FIELD: field_extent_low = tuple(b[0] for b in sym.boundary) offset_low = tuple(b[0] - e for b, e in zip(max_boundary, field_extent_low)) field_extent_high = tuple(b[1] for b in sym.boundary) offset_high = tuple(b[1] - e for b, e in zip(max_boundary, field_extent_high)) validation_slice = filter_mask( tuple(slice(o, s - h) for o, s, h in zip(offset_low, data_shape, offset_high)), field_masks[name], ) data_dims = field_params[name][1] if data_dims: validation_slice = tuple([*validation_slice] + [slice(None)] * len(data_dims)) cropped_validation_values[name] = data[validation_slice] else: cropped_validation_values[name] = data cls.validation( **cropped_validation_values, domain=domain, origin={ name: info.boundary.lower_indices for name, info in implementation.field_info.items() if info is not None }, ) # Test values for name, value in test_values.items(): if isinstance(value, np.ndarray): expected_value = validation_values[name] if gt_backend.from_name(value.backend).storage_info["device"] == "gpu": value.synchronize() value = value.data.get() else: value = value.data np.testing.assert_allclose( value, expected_value, rtol=RTOL, atol=ATOL, equal_nan=EQUAL_NAN, err_msg="Wrong data in output field '{name}'".format(name=name), )
def _run_test_implementation(self, parameters_dict, implementation): cls = type(self) input_data, exec_info = parameters_dict origin = cls.origin max_boundary = Boundary(cls.max_boundary) shape_iter = (Shape(v.shape) for v in input_data.values() if isinstance(v, np.ndarray)) shape = next(shape_iter) assert all(shape == sh for sh in shape_iter) domain = shape - (Index(max_boundary.lower_indices) + Index(max_boundary.upper_indices)) referenced_inputs = { name: info for name, info in implementation.field_info.items() if info is not None } referenced_inputs.update({ name: info for name, info in implementation.parameter_info.items() if info is not None }) # set externals for validation method for k, v in implementation.constants.items(): sys.modules[self.__module__].__dict__[k] = v # copy input data inputs = {} validation_inputs = {} for name, data in input_data.items(): data = input_data[name] if name in referenced_inputs: info = referenced_inputs[name] if isinstance(info, FieldInfo): inputs[name] = gt_storage.from_array( data, dtype=data.dtype, shape=shape, default_origin=origin, backend=implementation.backend, ) validation_inputs[name] = np.array(data) else: inputs[name] = data validation_inputs[name] = data else: inputs[name] = None validation_inputs[name] = None # call implementation implementation(**inputs, origin=origin, exec_info=exec_info) assert domain == exec_info["domain"] # for validation data, data is cropped to actually touched domain, so that origin offseting # does not have to be implemented for every test suite. This is done based on info # specified in test suite cropped_validation_inputs = {} for name, data in validation_inputs.items(): sym = cls.symbols[name] if data is not None and sym.kind == SymbolKind.FIELD: field_extent_low = tuple(b[0] for b in sym.boundary) offset_low = tuple( b[0] - e for b, e in zip(max_boundary, field_extent_low)) field_extent_high = tuple(b[1] for b in sym.boundary) offset_high = tuple( b[1] - e for b, e in zip(max_boundary, field_extent_high)) validation_slice = tuple( slice(o, s - h) for o, s, h in zip(offset_low, shape, offset_high)) cropped_validation_inputs[name] = data[validation_slice] else: cropped_validation_inputs[name] = data cls.validation( **cropped_validation_inputs, domain=domain, origin={ name: info.boundary.lower_indices for name, info in implementation.field_info.items() if info is not None }, ) # Test values for name, value in inputs.items(): if isinstance(value, np.ndarray): expected_value = validation_inputs[name] if gt_backend.from_name( value.backend).storage_info["device"] == "gpu": value.synchronize() value = value.data.get() else: value = value.data np.testing.assert_allclose( value, expected_value, rtol=RTOL, atol=ATOL, equal_nan=EQUAL_NAN, err_msg="Wrong data in output field '{name}'".format( name=name), )