diff --git a/docs/development/ADRs/0015-Test_Exclusion_Matrices.md b/docs/development/ADRs/0015-Test_Exclusion_Matrices.md index b338169d61..c868757905 100644 --- a/docs/development/ADRs/0015-Test_Exclusion_Matrices.md +++ b/docs/development/ADRs/0015-Test_Exclusion_Matrices.md @@ -1,5 +1,5 @@ --- -tags: [] +tags: [testing] --- # Test-Exclusion Matrices @@ -7,7 +7,7 @@ tags: [] - **Status**: valid - **Authors**: Edoardo Paone (@edopao), Enrique G. Paredes (@egparedes) - **Created**: 2023-09-21 -- **Updated**: 2023-09-21 +- **Updated**: 2024-01-25 In the context of Field View testing, lacking support for specific ITIR features while a certain backend is being developed, we decided to use `pytest` fixtures to exclude unsupported tests. @@ -22,7 +22,7 @@ the supported backends, while keeping the test code clean. ## Decision It was decided to apply fixtures and markers from `pytest` module. The fixture is the same used to execute the test -on different backends (`fieldview_backend` and `program_processor`), but it is extended with a check on the available feature markers. +on different backends (`exec_alloc_descriptor` and `program_processor`), but it is extended with a check on the available feature markers. If a test is annotated with a feature marker, the fixture will check if this feature is supported on the selected backend. If no marker is specified, the test is supposed to run on all backends. @@ -33,7 +33,7 @@ In the example below, `test_offset_field` requires the backend to support dynami def test_offset_field(cartesian_case): ``` -In order to selectively enable the backends, the dictionary `next_tests.exclusion_matrices.BACKEND_SKIP_TEST_MATRIX` +In order to selectively enable the backends, the dictionary `next_tests.definitions.BACKEND_SKIP_TEST_MATRIX` lists for each backend the features that are not supported. The fixture will check if the annotated feature is present in the exclusion-matrix for the selected backend. If so, the exclusion matrix will also specify the action `pytest` should take (e.g. `SKIP` or `XFAIL`). diff --git a/pyproject.toml b/pyproject.toml index 675bdae9d0..51cfc267d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -340,7 +340,11 @@ markers = [ 'uses_origin: tests that require backend support for domain origin', 'uses_reduction_over_lift_expressions: tests that require backend support for reduction over lift expressions', 'uses_reduction_with_only_sparse_fields: tests that require backend support for with sparse fields', + 'uses_scan: tests that uses scan', 'uses_scan_in_field_operator: tests that require backend support for scan in field operator', + 'uses_scan_without_field_args: tests that require calls to scan that do not have any fields as arguments', + 'uses_scan_nested: tests that use nested scans', + 'uses_scan_requiring_projector: tests need a projector implementation in gtfn', 'uses_sparse_fields: tests that require backend support for sparse fields', 'uses_sparse_fields_as_output: tests that require backend support for writing sparse fields', 'uses_strided_neighbor_offset: tests that require backend support for strided neighbor offset', @@ -349,7 +353,7 @@ markers = [ 'uses_zero_dimensional_fields: tests that require backend support for zero-dimensional fields', 'uses_cartesian_shift: tests that use a Cartesian connectivity', 'uses_unstructured_shift: tests that use a unstructured connectivity', - 'uses_scan: tests that uses scan', + 'uses_max_over: tests that use the max_over builtin', 'checks_specific_error: tests that rely on the backend to produce a specific error message' ] norecursedirs = ['dist', 'build', 'cpp_backend_tests/build*', '_local/*', '.*'] diff --git a/src/gt4py/next/allocators.py b/src/gt4py/next/allocators.py index 97e83276fe..44203bf6d8 100644 --- a/src/gt4py/next/allocators.py +++ b/src/gt4py/next/allocators.py @@ -231,6 +231,7 @@ def __init__(self) -> None: device_allocators[core_defs.DeviceType.CPU] = StandardCPUFieldBufferAllocator() + assert is_field_allocator(device_allocators[core_defs.DeviceType.CPU]) diff --git a/src/gt4py/next/common.py b/src/gt4py/next/common.py index 949f4b461a..33a0591813 100644 --- a/src/gt4py/next/common.py +++ b/src/gt4py/next/common.py @@ -843,8 +843,10 @@ def is_connectivity_field( return isinstance(v, ConnectivityField) # type: ignore[misc] # we use extended_runtime_checkable +# Utility function to construct a `Field` from different buffer representations. +# Consider removing this function and using `Field` constructor directly. See also `_connectivity`. @functools.singledispatch -def field( +def _field( definition: Any, /, *, @@ -854,8 +856,9 @@ def field( raise NotImplementedError +# See comment for `_field`. @functools.singledispatch -def connectivity( +def _connectivity( definition: Any, /, codomain: Dimension, @@ -980,7 +983,7 @@ def restrict(self, index: AnyIndexSpec) -> core_defs.IntegralScalar: __getitem__ = restrict -connectivity.register(numbers.Integral, CartesianConnectivity.from_offset) +_connectivity.register(numbers.Integral, CartesianConnectivity.from_offset) @enum.unique diff --git a/src/gt4py/next/constructors.py b/src/gt4py/next/constructors.py index 9bb4cf17e5..8b41bf7cba 100644 --- a/src/gt4py/next/constructors.py +++ b/src/gt4py/next/constructors.py @@ -87,7 +87,7 @@ def empty( buffer = next_allocators.allocate( domain, dtype, aligned_index=aligned_index, allocator=allocator, device=device ) - res = common.field(buffer.ndarray, domain=domain) + res = common._field(buffer.ndarray, domain=domain) assert common.is_mutable_field(res) assert isinstance(res, nd_array_field.NdArrayField) return res @@ -356,9 +356,9 @@ def as_connectivity( if (allocator is None) and (device is None) and xtyping.supports_dlpack(data): device = core_defs.Device(*data.__dlpack_device__()) buffer = next_allocators.allocate(actual_domain, dtype, allocator=allocator, device=device) - # TODO(havogt): consider addin MutableNDArrayObject + # TODO(havogt): consider adding MutableNDArrayObject buffer.ndarray[...] = storage_utils.asarray(data) # type: ignore[index] - connectivity_field = common.connectivity( + connectivity_field = common._connectivity( buffer.ndarray, codomain=codomain, domain=actual_domain ) assert isinstance(connectivity_field, nd_array_field.NdArrayConnectivityField) diff --git a/src/gt4py/next/embedded/nd_array_field.py b/src/gt4py/next/embedded/nd_array_field.py index 9fc1b42038..52a61b40bb 100644 --- a/src/gt4py/next/embedded/nd_array_field.py +++ b/src/gt4py/next/embedded/nd_array_field.py @@ -95,9 +95,7 @@ class NdArrayField( _domain: common.Domain _ndarray: core_defs.NDArrayObject - array_ns: ClassVar[ - ModuleType - ] # TODO(havogt) after storage PR is merged, update to the NDArrayNamespace protocol + array_ns: ClassVar[ModuleType] # TODO(havogt) introduce a NDArrayNamespace protocol @property def domain(self) -> common.Domain: @@ -197,7 +195,11 @@ def remap( # finally, take the new array new_buffer = xp.take(self._ndarray, new_idx_array, axis=dim_idx) - return self.__class__.from_array(new_buffer, domain=new_domain, dtype=self.dtype) + return self.__class__.from_array( + new_buffer, + domain=new_domain, + dtype=self.dtype, + ) __call__ = remap # type: ignore[assignment] @@ -510,7 +512,7 @@ class NumPyArrayField(NdArrayField): array_ns: ClassVar[ModuleType] = np -common.field.register(np.ndarray, NumPyArrayField.from_array) +common._field.register(np.ndarray, NumPyArrayField.from_array) @dataclasses.dataclass(frozen=True, eq=False) @@ -518,7 +520,7 @@ class NumPyArrayConnectivityField(NdArrayConnectivityField): array_ns: ClassVar[ModuleType] = np -common.connectivity.register(np.ndarray, NumPyArrayConnectivityField.from_array) +common._connectivity.register(np.ndarray, NumPyArrayConnectivityField.from_array) # CuPy if cp: @@ -528,13 +530,13 @@ class NumPyArrayConnectivityField(NdArrayConnectivityField): class CuPyArrayField(NdArrayField): array_ns: ClassVar[ModuleType] = cp - common.field.register(cp.ndarray, CuPyArrayField.from_array) + common._field.register(cp.ndarray, CuPyArrayField.from_array) @dataclasses.dataclass(frozen=True, eq=False) class CuPyArrayConnectivityField(NdArrayConnectivityField): array_ns: ClassVar[ModuleType] = cp - common.connectivity.register(cp.ndarray, CuPyArrayConnectivityField.from_array) + common._connectivity.register(cp.ndarray, CuPyArrayConnectivityField.from_array) # JAX if jnp: @@ -552,7 +554,7 @@ def __setitem__( # TODO(havogt): use something like `self.ndarray = self.ndarray.at(index).set(value)` raise NotImplementedError("'__setitem__' for JaxArrayField not yet implemented.") - common.field.register(jnp.ndarray, JaxArrayField.from_array) + common._field.register(jnp.ndarray, JaxArrayField.from_array) def _broadcast(field: common.Field, new_dimensions: tuple[common.Dimension, ...]) -> common.Field: @@ -565,7 +567,7 @@ def _broadcast(field: common.Field, new_dimensions: tuple[common.Dimension, ...] else: domain_slice.append(np.newaxis) named_ranges.append((dim, common.UnitRange.infinite())) - return common.field(field.ndarray[tuple(domain_slice)], domain=common.Domain(*named_ranges)) + return common._field(field.ndarray[tuple(domain_slice)], domain=common.Domain(*named_ranges)) def _builtins_broadcast( diff --git a/src/gt4py/next/embedded/operators.py b/src/gt4py/next/embedded/operators.py index 0992401ebb..cb03373b41 100644 --- a/src/gt4py/next/embedded/operators.py +++ b/src/gt4py/next/embedded/operators.py @@ -13,11 +13,14 @@ # SPDX-License-Identifier: GPL-3.0-or-later import dataclasses +from types import ModuleType from typing import Any, Callable, Generic, ParamSpec, Sequence, TypeVar +import numpy as np + from gt4py import eve from gt4py._core import definitions as core_defs -from gt4py.next import common, constructors, errors, utils +from gt4py.next import common, errors, utils from gt4py.next.embedded import common as embedded_common, context as embedded_context @@ -43,7 +46,8 @@ def __call__(self, *args: common.Field | core_defs.Scalar, **kwargs: common.Fiel scan_range = embedded_context.closure_column_range.get() assert self.axis == scan_range[0] scan_axis = scan_range[0] - domain_intersection = _intersect_scan_args(*args, *kwargs.values()) + all_args = [*args, *kwargs.values()] + domain_intersection = _intersect_scan_args(*all_args) non_scan_domain = common.Domain(*[nr for nr in domain_intersection if nr[0] != scan_axis]) out_domain = common.Domain( @@ -53,7 +57,8 @@ def __call__(self, *args: common.Field | core_defs.Scalar, **kwargs: common.Fiel # even if the scan dimension is not in the input, we can scan over it out_domain = common.Domain(*out_domain, (scan_range)) - res = _construct_scan_array(out_domain)(self.init) + xp = _get_array_ns(*all_args) + res = _construct_scan_array(out_domain, xp)(self.init) def scan_loop(hpos): acc = self.init @@ -128,7 +133,11 @@ def _tuple_assign_field( ): @utils.tree_map def impl(target: common.MutableField, source: common.Field): - target[domain] = source[domain] + if common.is_field(source): + target[domain] = source[domain] + else: + assert core_defs.is_scalar_type(source) + target[domain] = source impl(target, source) @@ -141,10 +150,21 @@ def _intersect_scan_args( ) -def _construct_scan_array(domain: common.Domain): +def _get_array_ns( + *args: core_defs.Scalar | common.Field | tuple[core_defs.Scalar | common.Field | tuple, ...] +) -> ModuleType: + for arg in utils.flatten_nested_tuple(args): + if hasattr(arg, "array_ns"): + return arg.array_ns + return np + + +def _construct_scan_array( + domain: common.Domain, xp: ModuleType +): # TODO(havogt) introduce a NDArrayNamespace protocol @utils.tree_map def impl(init: core_defs.Scalar) -> common.Field: - return constructors.empty(domain, dtype=type(init)) + return common._field(xp.empty(domain.shape, dtype=type(init)), domain=domain) return impl @@ -168,6 +188,7 @@ def _tuple_at( @utils.tree_map def impl(field: common.Field | core_defs.Scalar) -> core_defs.Scalar: res = field[pos] if common.is_field(field) else field + res = res.item() if hasattr(res, "item") else res # extract scalar value from array assert core_defs.is_scalar_type(res) return res diff --git a/src/gt4py/next/ffront/fbuiltins.py b/src/gt4py/next/ffront/fbuiltins.py index cd75538da7..493493f697 100644 --- a/src/gt4py/next/ffront/fbuiltins.py +++ b/src/gt4py/next/ffront/fbuiltins.py @@ -188,12 +188,8 @@ def broadcast( assert core_defs.is_scalar_type( field ) # default implementation for scalars, Fields are handled via dispatch - return common.field( - np.asarray(field)[ - tuple([np.newaxis] * len(dims)) - ], # TODO(havogt) use FunctionField once available - domain=common.Domain(dims=dims, ranges=tuple([common.UnitRange.infinite()] * len(dims))), - ) + # TODO(havogt) implement with FunctionField, the workaround is to ignore broadcasting on scalars as they broadcast automatically, but we lose the check for compatible dimensions + return field # type: ignore[return-value] # see comment above @WhereBuiltinFunction diff --git a/src/gt4py/next/iterator/embedded.py b/src/gt4py/next/iterator/embedded.py index 390bec4312..6d610fd136 100644 --- a/src/gt4py/next/iterator/embedded.py +++ b/src/gt4py/next/iterator/embedded.py @@ -1035,7 +1035,7 @@ def _maker(a) -> common.Field: offset = origin.get(d, 0) ranges.append(common.UnitRange(-offset, s - offset)) - res = common.field(a, domain=common.Domain(dims=tuple(axes), ranges=tuple(ranges))) + res = common._field(a, domain=common.Domain(dims=tuple(axes), ranges=tuple(ranges))) return res return _maker diff --git a/tests/next_tests/__init__.py b/tests/next_tests/__init__.py index e2905ab49a..1745dac6ef 100644 --- a/tests/next_tests/__init__.py +++ b/tests/next_tests/__init__.py @@ -12,10 +12,10 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -from . import exclusion_matrices +from . import definitions -__all__ = ["exclusion_matrices", "get_processor_id"] +__all__ = ["definitions", "get_processor_id"] def get_processor_id(processor): diff --git a/tests/next_tests/exclusion_matrices.py b/tests/next_tests/definitions.py similarity index 77% rename from tests/next_tests/exclusion_matrices.py rename to tests/next_tests/definitions.py index f6d2b10a14..dbb2366f47 100644 --- a/tests/next_tests/exclusion_matrices.py +++ b/tests/next_tests/definitions.py @@ -14,11 +14,16 @@ """Contains definition of test-exclusion matrices, see ADR 15.""" +import dataclasses import enum import importlib +from typing import Final, Optional, Protocol import pytest +from gt4py.next import allocators as next_allocators +from gt4py.next.program_processors import processor_interface as ppi + # Skip definitions XFAIL = pytest.xfail @@ -38,8 +43,6 @@ def load(self) -> object: obj = eval(f"_m.{obj}", globs) return obj - __invert__ = load - def short_id(self, num_components: int = 2) -> str: return ".".join(self.value.split(".")[-num_components:]) @@ -55,6 +58,32 @@ class ProgramBackendId(_PythonObjectIdMixin, str, enum.Enum): DOUBLE_ROUNDTRIP = "gt4py.next.program_processors.runners.double_roundtrip.backend" +class ExecutionAndAllocatorDescriptor(Protocol): + # Used for test infrastructure, consider implementing this in gt4py when refactoring otf + @property + def executor(self) -> Optional[ppi.ProgramExecutor]: + ... + + @property + def allocator(self) -> next_allocators.FieldBufferAllocatorProtocol: + ... + + +@dataclasses.dataclass(frozen=True) +class EmbeddedExecutionDescriptor: + allocator: next_allocators.FieldBufferAllocatorProtocol + executor: Final = None + + +numpy_execution = EmbeddedExecutionDescriptor(next_allocators.StandardCPUFieldBufferAllocator()) +cupy_execution = EmbeddedExecutionDescriptor(next_allocators.StandardGPUFieldBufferAllocator()) + + +class EmbeddedIds(_PythonObjectIdMixin, str, enum.Enum): + NUMPY_EXECUTION = "next_tests.definitions.numpy_execution" + CUPY_EXECUTION = "next_tests.definitions.cupy_execution" + + class OptionalProgramBackendId(_PythonObjectIdMixin, str, enum.Enum): DACE_CPU = "gt4py.next.program_processors.runners.dace_iterator.run_dace_cpu" DACE_GPU = "gt4py.next.program_processors.runners.dace_iterator.run_dace_gpu" @@ -93,7 +122,11 @@ class ProgramFormatterId(_PythonObjectIdMixin, str, enum.Enum): USES_NEGATIVE_MODULO = "uses_negative_modulo" USES_ORIGIN = "uses_origin" USES_REDUCTION_OVER_LIFT_EXPRESSIONS = "uses_reduction_over_lift_expressions" +USES_SCAN = "uses_scan" USES_SCAN_IN_FIELD_OPERATOR = "uses_scan_in_field_operator" +USES_SCAN_WITHOUT_FIELD_ARGS = "uses_scan_without_field_args" +USES_SCAN_NESTED = "uses_scan_nested" +USES_SCAN_REQUIRING_PROJECTOR = "uses_scan_requiring_projector" USES_SPARSE_FIELDS = "uses_sparse_fields" USES_SPARSE_FIELDS_AS_OUTPUT = "uses_sparse_fields_as_output" USES_REDUCTION_WITH_ONLY_SPARSE_FIELDS = "uses_reduction_with_only_sparse_fields" @@ -103,7 +136,7 @@ class ProgramFormatterId(_PythonObjectIdMixin, str, enum.Enum): USES_ZERO_DIMENSIONAL_FIELDS = "uses_zero_dimensional_fields" USES_CARTESIAN_SHIFT = "uses_cartesian_shift" USES_UNSTRUCTURED_SHIFT = "uses_unstructured_shift" -USES_SCAN = "uses_scan" +USES_MAX_OVER = "uses_max_over" CHECKS_SPECIFIC_ERROR = "checks_specific_error" # Skip messages (available format keys: 'marker', 'backend') @@ -134,26 +167,38 @@ class ProgramFormatterId(_PythonObjectIdMixin, str, enum.Enum): EMBEDDED_SKIP_LIST = [ (USES_DYNAMIC_OFFSETS, XFAIL, UNSUPPORTED_MESSAGE), (CHECKS_SPECIFIC_ERROR, XFAIL, UNSUPPORTED_MESSAGE), + ( + USES_SCAN_WITHOUT_FIELD_ARGS, + XFAIL, + UNSUPPORTED_MESSAGE, + ), # we can't extract the field type from scan args ] GTFN_SKIP_TEST_LIST = COMMON_SKIP_TEST_LIST + [ # floordiv not yet supported, see https://github.com/GridTools/gt4py/issues/1136 (USES_FLOORDIV, XFAIL, BINDINGS_UNSUPPORTED_MESSAGE), (USES_STRIDED_NEIGHBOR_OFFSET, XFAIL, BINDINGS_UNSUPPORTED_MESSAGE), + # max_over broken, see https://github.com/GridTools/gt4py/issues/1289 + (USES_MAX_OVER, XFAIL, UNSUPPORTED_MESSAGE), + (USES_SCAN_REQUIRING_PROJECTOR, XFAIL, UNSUPPORTED_MESSAGE), ] #: Skip matrix, contains for each backend processor a list of tuples with following fields: #: (, ) BACKEND_SKIP_TEST_MATRIX = { - None: EMBEDDED_SKIP_LIST, + EmbeddedIds.NUMPY_EXECUTION: EMBEDDED_SKIP_LIST, + EmbeddedIds.CUPY_EXECUTION: EMBEDDED_SKIP_LIST, OptionalProgramBackendId.DACE_CPU: DACE_SKIP_TEST_LIST, OptionalProgramBackendId.DACE_GPU: DACE_SKIP_TEST_LIST + [ # awaiting dace fix, see https://github.com/spcl/dace/pull/1442 (USES_FLOORDIV, XFAIL, BINDINGS_UNSUPPORTED_MESSAGE), ], - ProgramBackendId.GTFN_CPU: GTFN_SKIP_TEST_LIST, - ProgramBackendId.GTFN_CPU_IMPERATIVE: GTFN_SKIP_TEST_LIST, - ProgramBackendId.GTFN_GPU: GTFN_SKIP_TEST_LIST, + ProgramBackendId.GTFN_CPU: GTFN_SKIP_TEST_LIST + + [(USES_SCAN_NESTED, XFAIL, UNSUPPORTED_MESSAGE)], + ProgramBackendId.GTFN_CPU_IMPERATIVE: GTFN_SKIP_TEST_LIST + + [(USES_SCAN_NESTED, XFAIL, UNSUPPORTED_MESSAGE)], + ProgramBackendId.GTFN_GPU: GTFN_SKIP_TEST_LIST + + [(USES_SCAN_NESTED, XFAIL, UNSUPPORTED_MESSAGE)], ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES: GTFN_SKIP_TEST_LIST + [ (USES_DYNAMIC_OFFSETS, XFAIL, UNSUPPORTED_MESSAGE), diff --git a/tests/next_tests/integration_tests/cases.py b/tests/next_tests/integration_tests/cases.py index 6217d3c782..03a0a9f5a7 100644 --- a/tests/next_tests/integration_tests/cases.py +++ b/tests/next_tests/integration_tests/cases.py @@ -28,11 +28,12 @@ from gt4py._core import definitions as core_defs from gt4py.eve import extended_typing as xtyping from gt4py.eve.extended_typing import Self -from gt4py.next import common, constructors, field_utils +from gt4py.next import allocators as next_allocators, common, constructors, field_utils from gt4py.next.ffront import decorator from gt4py.next.program_processors import processor_interface as ppi from gt4py.next.type_system import type_specifications as ts, type_translation +from next_tests import definitions as test_definitions from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( # noqa: F401 # fixture and aliases Cell, Edge, @@ -43,7 +44,7 @@ KDim, Koff, Vertex, - fieldview_backend, + exec_alloc_descriptor, reduction_setup, ) @@ -103,7 +104,7 @@ def scalar(self, dtype: np.typing.DTypeLike) -> ScalarValue: def field( self, - backend: ppi.ProgramProcessor, + allocator: next_allocators.FieldBufferAllocatorProtocol, sizes: dict[gtx.Dimension, int], dtype: np.typing.DTypeLike, ) -> FieldValue: @@ -137,7 +138,7 @@ def scalar_value(self) -> ScalarValue: def field( self, - backend: ppi.ProgramExecutor, + allocator: next_allocators.FieldBufferAllocatorProtocol, sizes: dict[gtx.Dimension, int], dtype: np.typing.DTypeLike, ) -> FieldValue: @@ -145,7 +146,7 @@ def field( domain=common.domain(sizes), fill_value=self.value, dtype=dtype, - allocator=backend, + allocator=allocator, ) @@ -166,7 +167,7 @@ def scalar_value(self) -> ScalarValue: def field( self, - backend: ppi.ProgramExecutor, + allocator: next_allocators.FieldBufferAllocatorProtocol, sizes: dict[gtx.Dimension, int], dtype: np.typing.DTypeLike, ) -> FieldValue: @@ -176,7 +177,7 @@ def field( ) n_data = list(sizes.values())[0] return constructors.as_field( - domain=common.domain(sizes), data=np.arange(0, n_data, dtype=dtype), allocator=backend + domain=common.domain(sizes), data=np.arange(0, n_data, dtype=dtype), allocator=allocator ) def from_case( @@ -207,7 +208,7 @@ def scalar_value(self) -> ScalarValue: def field( self, - backend: ppi.ProgramProcessor, + allocator: next_allocators.FieldBufferAllocatorProtocol, sizes: dict[gtx.Dimension, int], dtype: np.typing.DTypeLike, ) -> FieldValue: @@ -218,7 +219,7 @@ def field( return constructors.as_field( common.domain(sizes), np.arange(start, start + n_data, dtype=dtype).reshape(svals), - allocator=backend, + allocator=allocator, ) def from_case( @@ -382,7 +383,7 @@ def run( """Run fieldview code in the context of a given test case.""" if kwargs.get("offset_provider", None) is None: kwargs["offset_provider"] = case.offset_provider - fieldview_prog.with_grid_type(case.grid_type).with_backend(case.backend)(*args, **kwargs) + fieldview_prog.with_grid_type(case.grid_type).with_backend(case.executor)(*args, **kwargs) def verify( @@ -480,19 +481,25 @@ def verify_with_default_data( @pytest.fixture -def cartesian_case(fieldview_backend): # noqa: F811 # fixtures +def cartesian_case( + exec_alloc_descriptor: test_definitions.ExecutionAndAllocatorDescriptor, # noqa: F811 # fixtures +): yield Case( - fieldview_backend, + exec_alloc_descriptor.executor, offset_provider={"Ioff": IDim, "Joff": JDim, "Koff": KDim}, default_sizes={IDim: 10, JDim: 10, KDim: 10}, grid_type=common.GridType.CARTESIAN, + allocator=exec_alloc_descriptor.allocator, ) @pytest.fixture -def unstructured_case(reduction_setup, fieldview_backend): # noqa: F811 # fixtures +def unstructured_case( + reduction_setup, # noqa: F811 # fixtures + exec_alloc_descriptor: test_definitions.ExecutionAndAllocatorDescriptor, # noqa: F811 # fixtures +): yield Case( - fieldview_backend, + exec_alloc_descriptor.executor, offset_provider=reduction_setup.offset_provider, default_sizes={ Vertex: reduction_setup.num_vertices, @@ -501,6 +508,7 @@ def unstructured_case(reduction_setup, fieldview_backend): # noqa: F811 # fixtu KDim: reduction_setup.k_levels, }, grid_type=common.GridType.UNSTRUCTURED, + allocator=exec_alloc_descriptor.allocator, ) @@ -516,7 +524,7 @@ def _allocate_from_type( match arg_type: case ts.FieldType(dims=dims, dtype=arg_dtype): return strategy.field( - backend=case.backend, + allocator=case.allocator, sizes={dim: sizes[dim] for dim in dims}, dtype=dtype or arg_dtype.kind.name.lower(), ) @@ -601,11 +609,12 @@ def get_default_data( class Case: """Parametrizable components for single feature integration tests.""" - backend: ppi.ProgramProcessor + executor: Optional[ppi.ProgramProcessor] offset_provider: dict[str, common.Connectivity | gtx.Dimension] default_sizes: dict[gtx.Dimension, int] grid_type: common.GridType + allocator: next_allocators.FieldBufferAllocatorFactoryProtocol @property def as_field(self): - return constructors.as_field.partial(allocator=self.backend) + return constructors.as_field.partial(allocator=self.allocator) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/ffront_test_utils.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/ffront_test_utils.py index 1f5a1f0c48..e421763699 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/ffront_test_utils.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/ffront_test_utils.py @@ -14,7 +14,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from collections import namedtuple -from typing import Any, TypeVar +from typing import Any, Optional, TypeVar import numpy as np import pytest @@ -35,7 +35,6 @@ raise e import next_tests -import next_tests.exclusion_matrices as definitions @ppi.program_executor @@ -46,25 +45,33 @@ def no_backend(program: itir.FencilDefinition, *args: Any, **kwargs: Any) -> Non OPTIONAL_PROCESSORS = [] if dace_iterator: - OPTIONAL_PROCESSORS.append(definitions.OptionalProgramBackendId.DACE_CPU) + OPTIONAL_PROCESSORS.append(next_tests.definitions.OptionalProgramBackendId.DACE_CPU) OPTIONAL_PROCESSORS.append( - pytest.param(definitions.OptionalProgramBackendId.DACE_GPU, marks=pytest.mark.requires_gpu) + pytest.param( + next_tests.definitions.OptionalProgramBackendId.DACE_GPU, marks=pytest.mark.requires_gpu + ) ), @pytest.fixture( params=[ - definitions.ProgramBackendId.ROUNDTRIP, - definitions.ProgramBackendId.GTFN_CPU, - definitions.ProgramBackendId.GTFN_CPU_IMPERATIVE, - definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES, - pytest.param(definitions.ProgramBackendId.GTFN_GPU, marks=pytest.mark.requires_gpu), - None, + next_tests.definitions.ProgramBackendId.ROUNDTRIP, + next_tests.definitions.ProgramBackendId.GTFN_CPU, + next_tests.definitions.ProgramBackendId.GTFN_CPU_IMPERATIVE, + next_tests.definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES, + pytest.param( + next_tests.definitions.ProgramBackendId.GTFN_GPU, marks=pytest.mark.requires_gpu + ), + # will use the default (embedded) execution, but input/output allocated with the provided allocator + next_tests.definitions.EmbeddedIds.NUMPY_EXECUTION, + pytest.param( + next_tests.definitions.EmbeddedIds.CUPY_EXECUTION, marks=pytest.mark.requires_gpu + ), ] + OPTIONAL_PROCESSORS, - ids=lambda p: p.short_id() if p is not None else "None", + ids=lambda p: p.short_id(), ) -def fieldview_backend(request): +def exec_alloc_descriptor(request): """ Fixture creating field-view operator backend on-demand for tests. @@ -72,9 +79,9 @@ def fieldview_backend(request): Check ADR 15 for details on the test-exclusion matrices. """ backend_id = request.param - backend = None if backend_id is None else backend_id.load() + backend = backend_id.load() - for marker, skip_mark, msg in next_tests.exclusion_matrices.BACKEND_SKIP_TEST_MATRIX.get( + for marker, skip_mark, msg in next_tests.definitions.BACKEND_SKIP_TEST_MATRIX.get( backend_id, [] ): if request.node.get_closest_marker(marker): @@ -225,7 +232,7 @@ def reduction_setup(): __all__ = [ - "fieldview_backend", + "exec_alloc_descriptor", "reduction_setup", "debug_itir", "DimsType", diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_arg_call_interface.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_arg_call_interface.py index b41696a36b..354323afeb 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_arg_call_interface.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_arg_call_interface.py @@ -26,7 +26,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import IDim, IField, IJKFloatField, KDim, cartesian_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, ) @@ -59,7 +59,7 @@ def testee(a: IField, b: IField, c: IField) -> IField: pos_args = [args[name] for name in arg_names] kw_args = {name: args[name] for name in kwarg_names} - testee.with_backend(cartesian_case.backend)( + testee.with_backend(cartesian_case.executor)( *pos_args, **kw_args, out=out, offset_provider=cartesian_case.offset_provider ) @@ -85,7 +85,7 @@ def testee(a: IField, b: IField, out: IField): pos_args = [args[name] for name in arg_names] kw_args = {name: args[name] for name in kwarg_names} - testee.with_backend(cartesian_case.backend)( + testee.with_backend(cartesian_case.executor)( *pos_args, **kw_args, offset_provider=cartesian_case.offset_provider ) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_bound_args.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_bound_args.py index 0de953d85f..e4baedc6ee 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_bound_args.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_bound_args.py @@ -21,7 +21,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import cartesian_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, reduction_setup, ) @@ -52,7 +52,7 @@ def fieldop_args(a: cases.IField, condition: bool, scalar: int32) -> cases.IFiel scalar = 0 if not condition else scalar return a + scalar - @gtx.program(backend=cartesian_case.backend) + @gtx.program(backend=cartesian_case.executor) def program_args(a: cases.IField, condition: bool, scalar: int32, out: cases.IField): fieldop_args(a, condition, scalar, out=out) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_execution.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_execution.py index 70c79d7b6c..9482860d13 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_execution.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_execution.py @@ -51,7 +51,7 @@ unstructured_case, ) from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, reduction_setup, ) @@ -589,6 +589,7 @@ def testee(a: tuple[tuple[cases.IField, cases.IField], cases.IField]) -> cases.I @pytest.mark.uses_scan +@pytest.mark.uses_scan_without_field_args @pytest.mark.parametrize("forward", [True, False]) def test_fieldop_from_scan(cartesian_case, forward): init = 1.0 @@ -611,15 +612,9 @@ def simple_scan_operator(carry: float) -> float: @pytest.mark.uses_scan @pytest.mark.uses_lift_expressions +@pytest.mark.uses_scan_nested def test_solve_triag(cartesian_case): - if cartesian_case.backend in [ - gtfn.run_gtfn, - gtfn.run_gtfn_gpu, - gtfn.run_gtfn_imperative, - gtfn.run_gtfn_with_temporaries, - ]: - pytest.xfail("Nested `scan`s requires creating temporaries.") - if cartesian_case.backend == gtfn.run_gtfn_with_temporaries: + if cartesian_case.executor == gtfn.run_gtfn_with_temporaries: pytest.xfail("Temporary extraction does not work correctly in combination with scans.") @gtx.scan_operator(axis=KDim, forward=True, init=(0.0, 0.0)) @@ -721,7 +716,7 @@ def testee(a: cases.EField, b: cases.EField) -> cases.VField: @pytest.mark.uses_scan def test_ternary_scan(cartesian_case): - if cartesian_case.backend in [gtfn.run_gtfn_with_temporaries]: + if cartesian_case.executor in [gtfn.run_gtfn_with_temporaries]: pytest.xfail("Temporary extraction does not work correctly in combination with scans.") @gtx.scan_operator(axis=KDim, forward=True, init=0.0) @@ -743,9 +738,10 @@ def simple_scan_operator(carry: float, a: float) -> float: @pytest.mark.parametrize("forward", [True, False]) @pytest.mark.uses_scan +@pytest.mark.uses_scan_without_field_args @pytest.mark.uses_tuple_returns def test_scan_nested_tuple_output(forward, cartesian_case): - if cartesian_case.backend in [gtfn.run_gtfn_with_temporaries]: + if cartesian_case.executor in [gtfn.run_gtfn_with_temporaries]: pytest.xfail("Temporary extraction does not work correctly in combination with scans.") init = (1, (2, 3)) @@ -916,15 +912,8 @@ def program_domain(a: cases.IField, out: cases.IField): cases.verify(cartesian_case, program_domain, a, out, inout=out, ref=ref) +@pytest.mark.uses_floordiv def test_domain_input_bounds(cartesian_case): - if cartesian_case.backend in [ - gtfn.run_gtfn, - gtfn.run_gtfn_gpu, - gtfn.run_gtfn_imperative, - gtfn.run_gtfn_with_temporaries, - ]: - pytest.xfail("FloorDiv not fully supported in gtfn.") - lower_i = 1 upper_i = 10 @@ -970,7 +959,7 @@ def test_domain_input_bounds_1(cartesian_case): def fieldop_domain(a: cases.IJField) -> cases.IJField: return a + a - @gtx.program(backend=cartesian_case.backend) + @gtx.program(backend=cartesian_case.executor) def program_domain( a: cases.IJField, out: cases.IJField, @@ -1071,7 +1060,7 @@ def prog(inp: cases.IKField, k_index: gtx.Field[[KDim], gtx.IndexType], out: cas def test_undefined_symbols(cartesian_case): with pytest.raises(errors.DSLError, match="Undeclared symbol"): - @gtx.field_operator(backend=cartesian_case.backend) + @gtx.field_operator(backend=cartesian_case.executor) def return_undefined(): return undefined_symbol @@ -1171,7 +1160,7 @@ def test_tuple_unpacking_too_many_values(cartesian_case): match=(r"Too many values to unpack \(expected 3\)."), ): - @gtx.field_operator(backend=cartesian_case.backend) + @gtx.field_operator(backend=cartesian_case.executor) def _star_unpack() -> tuple[int32, float64, int32]: a, b, c = (1, 2.0, 3, 4, 5, 6, 7.0) return a, b, c @@ -1182,7 +1171,7 @@ def test_tuple_unpacking_too_few_values(cartesian_case): errors.DSLError, match=(r"Assignment value must be of type tuple, got 'int32'.") ): - @gtx.field_operator(backend=cartesian_case.backend) + @gtx.field_operator(backend=cartesian_case.executor) def _invalid_unpack() -> tuple[int32, float64, int32]: a, b, c = 1 return a diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_external_local_field.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_external_local_field.py index d100cd380c..bb1d878a6a 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_external_local_field.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_external_local_field.py @@ -21,7 +21,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import V2E, Edge, V2EDim, Vertex, unstructured_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, reduction_setup, ) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_gt4py_builtins.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_gt4py_builtins.py index e2434d860a..90d07f360d 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_gt4py_builtins.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_gt4py_builtins.py @@ -34,26 +34,19 @@ unstructured_case, ) from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, reduction_setup, ) @pytest.mark.uses_unstructured_shift +@pytest.mark.uses_max_over @pytest.mark.parametrize( "strategy", [cases.UniqueInitializer(1), cases.UniqueInitializer(-100)], ids=["positive_values", "negative_values"], ) def test_maxover_execution_(unstructured_case, strategy): - if unstructured_case.backend in [ - gtfn.run_gtfn, - gtfn.run_gtfn_gpu, - gtfn.run_gtfn_imperative, - gtfn.run_gtfn_with_temporaries, - ]: - pytest.xfail("`maxover` broken in gtfn, see #1289.") - @gtx.field_operator def testee(edge_f: cases.EField) -> cases.VField: out = max_over(edge_f(V2E), axis=V2EDim) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_builtin_execution.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_builtin_execution.py index 4444742c66..e076ec4227 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_builtin_execution.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_builtin_execution.py @@ -28,7 +28,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import IDim, cartesian_case, unstructured_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, ) from next_tests.integration_tests.feature_tests.math_builtin_test_data import math_builtin_test_data @@ -118,7 +118,7 @@ def make_builtin_field_operator(builtin_name: str, backend: Optional[ppi.Program @pytest.mark.parametrize("builtin_name, inputs", math_builtin_test_data()) def test_math_function_builtins_execution(cartesian_case, builtin_name: str, inputs): - if cartesian_case.backend is None: + if cartesian_case.executor is None: # TODO(havogt) find a way that works for embedded pytest.xfail("Test does not have a field view program.") if builtin_name == "gamma": @@ -131,7 +131,7 @@ def test_math_function_builtins_execution(cartesian_case, builtin_name: str, inp expected = ref_impl(*inputs) out = cartesian_case.as_field([IDim], np.zeros_like(expected)) - builtin_field_op = make_builtin_field_operator(builtin_name, cartesian_case.backend) + builtin_field_op = make_builtin_field_operator(builtin_name, cartesian_case.executor) builtin_field_op(*inps, out=out, offset_provider={}) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_unary_builtins.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_unary_builtins.py index f5bf453a09..b21f29c9bc 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_unary_builtins.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_math_unary_builtins.py @@ -41,7 +41,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import IDim, cartesian_case, unstructured_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, ) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_program.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_program.py index 938c69fb52..df0009d0d4 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_program.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_program.py @@ -23,7 +23,13 @@ from gt4py.next import errors from next_tests.integration_tests import cases -from next_tests.integration_tests.cases import IDim, Ioff, JDim, cartesian_case, fieldview_backend +from next_tests.integration_tests.cases import ( + IDim, + Ioff, + JDim, + cartesian_case, + exec_alloc_descriptor, +) from next_tests.past_common_fixtures import ( copy_program_def, copy_restrict_program_def, @@ -34,7 +40,7 @@ def test_identity_fo_execution(cartesian_case, identity_def): - identity = gtx.field_operator(identity_def, backend=cartesian_case.backend) + identity = gtx.field_operator(identity_def, backend=cartesian_case.executor) in_field = cases.allocate(cartesian_case, identity, "in_field").strategy( cases.ConstInitializer(1) @@ -82,13 +88,13 @@ def shift_by_one_program(in_field: cases.IFloatField, out_field: cases.IFloatFie def test_copy_execution(cartesian_case, copy_program_def): - copy_program = gtx.program(copy_program_def, backend=cartesian_case.backend) + copy_program = gtx.program(copy_program_def, backend=cartesian_case.executor) cases.verify_with_default_data(cartesian_case, copy_program, ref=lambda in_field: in_field) def test_double_copy_execution(cartesian_case, double_copy_program_def): - double_copy_program = gtx.program(double_copy_program_def, backend=cartesian_case.backend) + double_copy_program = gtx.program(double_copy_program_def, backend=cartesian_case.executor) cases.verify_with_default_data( cartesian_case, double_copy_program, ref=lambda in_field, intermediate_field: in_field @@ -96,7 +102,7 @@ def test_double_copy_execution(cartesian_case, double_copy_program_def): def test_copy_restricted_execution(cartesian_case, copy_restrict_program_def): - copy_restrict_program = gtx.program(copy_restrict_program_def, backend=cartesian_case.backend) + copy_restrict_program = gtx.program(copy_restrict_program_def, backend=cartesian_case.executor) cases.verify_with_default_data( cartesian_case, @@ -218,7 +224,7 @@ def prog( def test_wrong_argument_type(cartesian_case, copy_program_def): - copy_program = gtx.program(copy_program_def, backend=cartesian_case.backend) + copy_program = gtx.program(copy_program_def, backend=cartesian_case.executor) inp = cartesian_case.as_field([JDim], np.ones((cartesian_case.default_sizes[JDim],))) out = cases.allocate(cartesian_case, copy_program, "out").strategy(cases.ConstInitializer(1))() diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_scalar_if.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_scalar_if.py index af06da3e29..834966c125 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_scalar_if.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_scalar_if.py @@ -40,7 +40,7 @@ ) from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( Cell, - fieldview_backend, + exec_alloc_descriptor, size, ) diff --git a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_temporaries_with_sizes.py b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_temporaries_with_sizes.py index 788081b81e..c4cdd8a4be 100644 --- a/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_temporaries_with_sizes.py +++ b/tests/next_tests/integration_tests/feature_tests/ffront_tests/test_temporaries_with_sizes.py @@ -77,7 +77,7 @@ def prog( def test_verification(testee, run_gtfn_with_temporaries_and_symbolic_sizes, reduction_setup): unstructured_case = Case( - run_gtfn_with_temporaries_and_symbolic_sizes, + run_gtfn_with_temporaries_and_symbolic_sizes.executor, offset_provider=reduction_setup.offset_provider, default_sizes={ Vertex: reduction_setup.num_vertices, @@ -86,6 +86,7 @@ def test_verification(testee, run_gtfn_with_temporaries_and_symbolic_sizes, redu KDim: reduction_setup.k_levels, }, grid_type=common.GridType.UNSTRUCTURED, + allocator=run_gtfn_with_temporaries_and_symbolic_sizes.allocator, ) a = cases.allocate(unstructured_case, testee, "a")() diff --git a/tests/next_tests/integration_tests/feature_tests/test_util_cases.py b/tests/next_tests/integration_tests/feature_tests/test_util_cases.py index 579dec11f8..59c72bbf3f 100644 --- a/tests/next_tests/integration_tests/feature_tests/test_util_cases.py +++ b/tests/next_tests/integration_tests/feature_tests/test_util_cases.py @@ -18,11 +18,11 @@ import gt4py.next as gtx from gt4py.next import errors -import next_tests.exclusion_matrices as definitions +from next_tests import definitions from next_tests.integration_tests import cases from next_tests.integration_tests.cases import ( # noqa: F401 # fixtures cartesian_case, - fieldview_backend, + exec_alloc_descriptor, ) @@ -70,7 +70,7 @@ def test_allocate_const(cartesian_case): # noqa: F811 # fixtures assert b == 42.0 -@pytest.mark.parametrize("fieldview_backend", [~definitions.ProgramBackendId.ROUNDTRIP]) +@pytest.mark.parametrize("exec_alloc_descriptor", [definitions.ProgramBackendId.ROUNDTRIP.load()]) def test_verify_fails_with_wrong_reference(cartesian_case): # noqa: F811 # fixtures a = cases.allocate(cartesian_case, addition, "a")() b = cases.allocate(cartesian_case, addition, "b")() @@ -81,7 +81,7 @@ def test_verify_fails_with_wrong_reference(cartesian_case): # noqa: F811 # fixt cases.verify(cartesian_case, addition, a, b, out=out, ref=wrong_ref) -@pytest.mark.parametrize("fieldview_backend", [~definitions.ProgramBackendId.ROUNDTRIP]) +@pytest.mark.parametrize("exec_alloc_descriptor", [definitions.ProgramBackendId.ROUNDTRIP.load()]) def test_verify_fails_with_wrong_type(cartesian_case): # noqa: F811 # fixtures a = cases.allocate(cartesian_case, addition, "a").dtype(np.float32)() b = cases.allocate(cartesian_case, addition, "b")() @@ -91,7 +91,7 @@ def test_verify_fails_with_wrong_type(cartesian_case): # noqa: F811 # fixtures cases.verify(cartesian_case, addition, a, b, out=out, ref=a + b) -@pytest.mark.parametrize("fieldview_backend", [~definitions.ProgramBackendId.ROUNDTRIP]) +@pytest.mark.parametrize("exec_alloc_descriptor", [definitions.ProgramBackendId.ROUNDTRIP.load()]) def test_verify_with_default_data_fails_with_wrong_reference( cartesian_case, # noqa: F811 # fixtures ): diff --git a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_embedded_regression.py b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_embedded_regression.py index ba4b1b0cdb..65f017a518 100644 --- a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_embedded_regression.py +++ b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_embedded_regression.py @@ -22,7 +22,7 @@ from next_tests.integration_tests.cases import IField, cartesian_case # noqa: F401 # fixtures from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( # noqa: F401 # fixtures KDim, - fieldview_backend, + exec_alloc_descriptor, ) @@ -38,7 +38,7 @@ def copy(a: IField) -> IField: with pytest.raises(ValueError, match="No backend selected!"): # Calling this should fail if the default backend is respected - # due to `fieldview_backend` fixture (dependency of `cartesian_case`) + # due to `exec_alloc_descriptor` fixture (dependency of `cartesian_case`) # setting the default backend to something invalid. _ = copy(a, out=a, offset_provider={}) @@ -51,7 +51,7 @@ def test_default_backend_is_respected_scan_operator(cartesian_case): # noqa: F8 def sum(state: float, a: float) -> float: return state + a - a = gtx.ones({KDim: 10}, allocator=cartesian_case.backend) + a = gtx.ones({KDim: 10}, allocator=cartesian_case.allocator) with pytest.raises(ValueError, match="No backend selected!"): # see comment in field_operator test @@ -81,7 +81,7 @@ def copy_program(a: IField, b: IField) -> IField: def test_missing_arg_field_operator(cartesian_case): # noqa: F811 # fixtures """Test that calling a field_operator without required args raises an error.""" - @gtx.field_operator(backend=cartesian_case.backend) + @gtx.field_operator(backend=cartesian_case.executor) def copy(a: IField) -> IField: return a @@ -97,7 +97,7 @@ def copy(a: IField) -> IField: def test_missing_arg_scan_operator(cartesian_case): # noqa: F811 # fixtures """Test that calling a scan_operator without required args raises an error.""" - @gtx.scan_operator(backend=cartesian_case.backend, axis=KDim, init=0.0, forward=True) + @gtx.scan_operator(backend=cartesian_case.executor, axis=KDim, init=0.0, forward=True) def sum(state: float, a: float) -> float: return state + a @@ -122,7 +122,7 @@ def copy(a: IField) -> IField: with pytest.raises(errors.DSLError, match="Invalid call"): - @gtx.program(backend=cartesian_case.backend) + @gtx.program(backend=cartesian_case.executor) def copy_program(a: IField, b: IField) -> IField: copy(a) @@ -130,7 +130,7 @@ def copy_program(a: IField, b: IField) -> IField: with pytest.raises(TypeError, match="'offset_provider'"): - @gtx.program(backend=cartesian_case.backend) + @gtx.program(backend=cartesian_case.executor) def copy_program(a: IField, b: IField) -> IField: copy(a, out=b) diff --git a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_icon_like_scan.py b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_icon_like_scan.py index 5bd255f80f..f1a5b41f81 100644 --- a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_icon_like_scan.py +++ b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_icon_like_scan.py @@ -19,12 +19,12 @@ import gt4py.next as gtx from gt4py.next import common -from gt4py.next.program_processors.runners import gtfn, roundtrip +from next_tests import definitions as test_definitions from next_tests.integration_tests import cases from next_tests.integration_tests.cases import Cell, KDim, Koff from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, ) @@ -193,12 +193,13 @@ def reference( @pytest.fixture -def test_setup(fieldview_backend): +def test_setup(exec_alloc_descriptor): test_case = cases.Case( - fieldview_backend, + exec_alloc_descriptor.executor, offset_provider={"Koff": KDim}, default_sizes={Cell: 14, KDim: 10}, grid_type=common.GridType.UNSTRUCTURED, + allocator=exec_alloc_descriptor.allocator, ) @dataclasses.dataclass(frozen=True) @@ -226,15 +227,8 @@ class setup: @pytest.mark.uses_tuple_returns +@pytest.mark.uses_scan_requiring_projector def test_solve_nonhydro_stencil_52_like_z_q(test_setup): - if test_setup.case.backend in [ - gtfn.run_gtfn, - gtfn.run_gtfn_gpu, - gtfn.run_gtfn_imperative, - gtfn.run_gtfn_with_temporaries, - ]: - pytest.xfail("Needs implementation of scan projector.") - cases.verify( test_setup.case, solve_nonhydro_stencil_52_like_z_q, @@ -253,12 +247,15 @@ def test_solve_nonhydro_stencil_52_like_z_q(test_setup): @pytest.mark.uses_tuple_returns def test_solve_nonhydro_stencil_52_like_z_q_tup(test_setup): - if test_setup.case.backend in [gtfn.run_gtfn_with_temporaries]: + if ( + test_setup.case.executor + == test_definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES.load().executor + ): pytest.xfail( "Needs implementation of scan projector. Breaks in type inference as executed" "again after CollapseTuple." ) - if test_setup.case.backend == roundtrip.backend: + if test_setup.case.executor == test_definitions.ProgramBackendId.ROUNDTRIP.load().executor: pytest.xfail("Needs proper handling of tuple[Column] <-> Column[tuple].") cases.verify( @@ -277,7 +274,10 @@ def test_solve_nonhydro_stencil_52_like_z_q_tup(test_setup): @pytest.mark.uses_tuple_returns def test_solve_nonhydro_stencil_52_like(test_setup): - if test_setup.case.backend in [gtfn.run_gtfn_with_temporaries]: + if ( + test_setup.case.executor + == test_definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES.load().executor + ): pytest.xfail("Temporary extraction does not work correctly in combination with scans.") cases.run( @@ -296,9 +296,12 @@ def test_solve_nonhydro_stencil_52_like(test_setup): @pytest.mark.uses_tuple_returns def test_solve_nonhydro_stencil_52_like_with_gtfn_tuple_merge(test_setup): - if test_setup.case.backend in [gtfn.run_gtfn_with_temporaries]: + if ( + test_setup.case.executor + == test_definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES.load().executor + ): pytest.xfail("Temporary extraction does not work correctly in combination with scans.") - if test_setup.case.backend == roundtrip.backend: + if test_setup.case.executor == test_definitions.ProgramBackendId.ROUNDTRIP.load().executor: pytest.xfail("Needs proper handling of tuple[Column] <-> Column[tuple].") cases.run( diff --git a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_laplacian.py b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_laplacian.py index 4f4d4969a9..6784857211 100644 --- a/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_laplacian.py +++ b/tests/next_tests/integration_tests/multi_feature_tests/ffront_tests/test_laplacian.py @@ -20,7 +20,7 @@ from next_tests.integration_tests import cases from next_tests.integration_tests.cases import IDim, Ioff, JDim, Joff, cartesian_case from next_tests.integration_tests.feature_tests.ffront_tests.ffront_test_utils import ( - fieldview_backend, + exec_alloc_descriptor, ) diff --git a/tests/next_tests/unit_tests/conftest.py b/tests/next_tests/unit_tests/conftest.py index 4177a5aeee..d3f9bdb761 100644 --- a/tests/next_tests/unit_tests/conftest.py +++ b/tests/next_tests/unit_tests/conftest.py @@ -31,8 +31,8 @@ else: raise e + import next_tests -import next_tests.exclusion_matrices as definitions @pytest.fixture( @@ -49,7 +49,7 @@ def lift_mode(request): OPTIONAL_PROCESSORS = [] if dace_iterator: - OPTIONAL_PROCESSORS.append((definitions.OptionalProgramBackendId.DACE_CPU, True)) + OPTIONAL_PROCESSORS.append((next_tests.definitions.OptionalProgramBackendId.DACE_CPU, True)) # TODO(havogt): update tests to use proper allocation # OPTIONAL_PROCESSORS.append( # pytest.param( @@ -61,16 +61,16 @@ def lift_mode(request): @pytest.fixture( params=[ (None, True), - (definitions.ProgramBackendId.ROUNDTRIP, True), - (definitions.ProgramBackendId.DOUBLE_ROUNDTRIP, True), - (definitions.ProgramBackendId.GTFN_CPU, True), - (definitions.ProgramBackendId.GTFN_CPU_IMPERATIVE, True), - (definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES, True), + (next_tests.definitions.ProgramBackendId.ROUNDTRIP, True), + (next_tests.definitions.ProgramBackendId.DOUBLE_ROUNDTRIP, True), + (next_tests.definitions.ProgramBackendId.GTFN_CPU, True), + (next_tests.definitions.ProgramBackendId.GTFN_CPU_IMPERATIVE, True), + (next_tests.definitions.ProgramBackendId.GTFN_CPU_WITH_TEMPORARIES, True), # pytest.param((definitions.ProgramBackendId.GTFN_GPU, True), marks=pytest.mark.requires_gpu), # TODO(havogt): update tests to use proper allocation - (definitions.ProgramFormatterId.LISP_FORMATTER, False), - (definitions.ProgramFormatterId.ITIR_PRETTY_PRINTER, False), - (definitions.ProgramFormatterId.ITIR_TYPE_CHECKER, False), - (definitions.ProgramFormatterId.GTFN_CPP_FORMATTER, False), + (next_tests.definitions.ProgramFormatterId.LISP_FORMATTER, False), + (next_tests.definitions.ProgramFormatterId.ITIR_PRETTY_PRINTER, False), + (next_tests.definitions.ProgramFormatterId.ITIR_TYPE_CHECKER, False), + (next_tests.definitions.ProgramFormatterId.GTFN_CPP_FORMATTER, False), ] + OPTIONAL_PROCESSORS, ids=lambda p: p[0].short_id() if p[0] is not None else "None", @@ -89,7 +89,7 @@ def program_processor(request) -> tuple[ppi.ProgramProcessor, bool]: processor = processor_id.load() assert is_backend == ppi.is_program_backend(processor) - for marker, skip_mark, msg in next_tests.exclusion_matrices.BACKEND_SKIP_TEST_MATRIX.get( + for marker, skip_mark, msg in next_tests.definitions.BACKEND_SKIP_TEST_MATRIX.get( processor_id, [] ): if request.node.get_closest_marker(marker): diff --git a/tests/next_tests/unit_tests/embedded_tests/test_nd_array_field.py b/tests/next_tests/unit_tests/embedded_tests/test_nd_array_field.py index 6863b09c12..1972b55852 100644 --- a/tests/next_tests/unit_tests/embedded_tests/test_nd_array_field.py +++ b/tests/next_tests/unit_tests/embedded_tests/test_nd_array_field.py @@ -77,7 +77,7 @@ def _make_field(lst: Iterable, nd_array_implementation, *, domain=None, dtype=No domain = tuple( (common.Dimension(f"D{i}"), common.UnitRange(0, s)) for i, s in enumerate(buffer.shape) ) - return common.field( + return common._field( buffer, domain=domain, ) @@ -119,14 +119,14 @@ def test_where_builtin_different_domain(nd_array_implementation): true_ = np.asarray([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], dtype=np.float32) false_ = np.asarray([7.0, 8.0, 9.0, 10.0], dtype=np.float32) - cond_field = common.field( + cond_field = common._field( nd_array_implementation.asarray(cond), domain=common.domain({JDim: 2}) ) - true_field = common.field( + true_field = common._field( nd_array_implementation.asarray(true_), domain=common.domain({IDim: common.UnitRange(0, 2), JDim: common.UnitRange(-1, 2)}), ) - false_field = common.field( + false_field = common._field( nd_array_implementation.asarray(false_), domain=common.domain({JDim: common.UnitRange(-1, 3)}), ) @@ -225,8 +225,8 @@ def test_binary_operations_with_intersection(binary_arithmetic_op, dims, expecte arr2 = np.ones((5, 5)) arr2_domain = common.Domain(dims=(IDim, JDim), ranges=(UnitRange(5, 10), UnitRange(5, 10))) - field1 = common.field(arr1, domain=arr1_domain) - field2 = common.field(arr2, domain=arr2_domain) + field1 = common._field(arr1, domain=arr1_domain) + field2 = common._field(arr2, domain=arr2_domain) op_result = binary_arithmetic_op(field1, field2) expected_result = binary_arithmetic_op(arr1[expected_indices[0], expected_indices[1]], arr2) @@ -287,11 +287,11 @@ def test_remap_implementation(): V_START, V_STOP = 2, 7 E_START, E_STOP = 0, 10 - v_field = common.field( + v_field = common._field( -0.1 * np.arange(V_START, V_STOP), domain=common.Domain(dims=(V,), ranges=(UnitRange(V_START, V_STOP),)), ) - e2v_conn = common.connectivity( + e2v_conn = common._connectivity( np.arange(E_START, E_STOP), domain=common.Domain( dims=(E,), @@ -303,7 +303,7 @@ def test_remap_implementation(): ) result = v_field.remap(e2v_conn) - expected = common.field( + expected = common._field( -0.1 * np.arange(V_START, V_STOP), domain=common.Domain(dims=(E,), ranges=(UnitRange(V_START, V_STOP),)), ) @@ -318,14 +318,14 @@ def test_cartesian_remap_implementation(): V_START, V_STOP = 2, 7 OFFSET = 2 - v_field = common.field( + v_field = common._field( -0.1 * np.arange(V_START, V_STOP), domain=common.Domain(dims=(V,), ranges=(UnitRange(V_START, V_STOP),)), ) - v2_conn = common.connectivity(OFFSET, V) + v2_conn = common._connectivity(OFFSET, V) result = v_field.remap(v2_conn) - expected = common.field( + expected = common._field( v_field.ndarray, domain=common.Domain(dims=(V,), ranges=(UnitRange(V_START - OFFSET, V_STOP - OFFSET),)), ) @@ -340,7 +340,7 @@ def test_cartesian_remap_implementation(): ( ( (IDim,), - common.field( + common._field( np.arange(10), domain=common.Domain(dims=(IDim,), ranges=(UnitRange(0, 10),)) ), Domain(dims=(IDim,), ranges=(UnitRange(0, 10),)), @@ -349,7 +349,7 @@ def test_cartesian_remap_implementation(): ( ( (IDim, JDim), - common.field( + common._field( np.arange(10), domain=common.Domain(dims=(IDim,), ranges=(UnitRange(0, 10),)) ), Domain(dims=(IDim, JDim), ranges=(UnitRange(0, 10), UnitRange.infinite())), @@ -358,7 +358,7 @@ def test_cartesian_remap_implementation(): ( ( (IDim, JDim), - common.field( + common._field( np.arange(10), domain=common.Domain(dims=(JDim,), ranges=(UnitRange(0, 10),)) ), Domain(dims=(IDim, JDim), ranges=(UnitRange.infinite(), UnitRange(0, 10))), @@ -367,7 +367,7 @@ def test_cartesian_remap_implementation(): ( ( (IDim, JDim, KDim), - common.field( + common._field( np.arange(10), domain=common.Domain(dims=(JDim,), ranges=(UnitRange(0, 10),)) ), Domain( @@ -455,7 +455,7 @@ def test_absolute_indexing(domain_slice, expected_dimensions, expected_shape): domain = common.Domain( dims=(IDim, JDim, KDim), ranges=(UnitRange(5, 10), UnitRange(5, 15), UnitRange(10, 25)) ) - field = common.field(np.ones((5, 10, 15)), domain=domain) + field = common._field(np.ones((5, 10, 15)), domain=domain) indexed_field = field[domain_slice] assert common.is_field(indexed_field) @@ -465,7 +465,7 @@ def test_absolute_indexing(domain_slice, expected_dimensions, expected_shape): def test_absolute_indexing_value_return(): domain = common.Domain(dims=(IDim, JDim), ranges=(UnitRange(10, 20), UnitRange(5, 15))) - field = common.field(np.reshape(np.arange(100, dtype=np.int32), (10, 10)), domain=domain) + field = common._field(np.reshape(np.arange(100, dtype=np.int32), (10, 10)), domain=domain) named_index = ((IDim, 12), (JDim, 6)) value = field[named_index] @@ -502,7 +502,7 @@ def test_absolute_indexing_value_return(): ) def test_relative_indexing_slice_2D(index, expected_shape, expected_domain): domain = common.Domain(dims=(IDim, JDim), ranges=(UnitRange(5, 15), UnitRange(2, 12))) - field = common.field(np.ones((10, 10)), domain=domain) + field = common._field(np.ones((10, 10)), domain=domain) indexed_field = field[index] assert common.is_field(indexed_field) @@ -558,7 +558,7 @@ def test_relative_indexing_slice_3D(index, expected_shape, expected_domain): domain = common.Domain( dims=(IDim, JDim, KDim), ranges=(UnitRange(5, 15), UnitRange(10, 25), UnitRange(10, 20)) ) - field = common.field(np.ones((10, 15, 10)), domain=domain) + field = common._field(np.ones((10, 15, 10)), domain=domain) indexed_field = field[index] assert common.is_field(indexed_field) @@ -572,7 +572,7 @@ def test_relative_indexing_slice_3D(index, expected_shape, expected_domain): ) def test_relative_indexing_value_return(index, expected_value): domain = common.Domain(dims=(IDim, JDim), ranges=(UnitRange(5, 15), UnitRange(2, 12))) - field = common.field(np.reshape(np.arange(100, dtype=int), (10, 10)), domain=domain) + field = common._field(np.reshape(np.arange(100, dtype=int), (10, 10)), domain=domain) indexed_field = field[index] assert indexed_field == expected_value @@ -581,7 +581,7 @@ def test_relative_indexing_value_return(index, expected_value): @pytest.mark.parametrize("lazy_slice", [lambda f: f[13], lambda f: f[:5, :3, :2]]) def test_relative_indexing_out_of_bounds(lazy_slice): domain = common.Domain(dims=(IDim, JDim), ranges=(UnitRange(3, 13), UnitRange(-5, 5))) - field = common.field(np.ones((10, 10)), domain=domain) + field = common._field(np.ones((10, 10)), domain=domain) with pytest.raises((embedded_exceptions.IndexOutOfBounds, IndexError)): lazy_slice(field) @@ -590,7 +590,7 @@ def test_relative_indexing_out_of_bounds(lazy_slice): @pytest.mark.parametrize("index", [IDim, "1", (IDim, JDim)]) def test_field_unsupported_index(index): domain = common.Domain(dims=(IDim,), ranges=(UnitRange(0, 10),)) - field = common.field(np.ones((10,)), domain=domain) + field = common._field(np.ones((10,)), domain=domain) with pytest.raises(IndexError, match="Unsupported index type"): field[index] @@ -602,12 +602,12 @@ def test_field_unsupported_index(index): ((1, slice(None)), np.ones((10,)) * 42.0), ( (1, slice(None)), - common.field(np.ones((10,)) * 42.0, domain=common.Domain((JDim, UnitRange(0, 10)))), + common._field(np.ones((10,)) * 42.0, domain=common.Domain((JDim, UnitRange(0, 10)))), ), ], ) def test_setitem(index, value): - field = common.field( + field = common._field( np.arange(100).reshape(10, 10), domain=common.Domain(dims=(IDim, JDim), ranges=(UnitRange(0, 10), UnitRange(0, 10))), ) @@ -621,12 +621,12 @@ def test_setitem(index, value): def test_setitem_wrong_domain(): - field = common.field( + field = common._field( np.arange(100).reshape(10, 10), domain=common.Domain(dims=(IDim, JDim), ranges=(UnitRange(0, 10), UnitRange(0, 10))), ) - value_incompatible = common.field( + value_incompatible = common._field( np.ones((10,)) * 42.0, domain=common.Domain((JDim, UnitRange(-5, 5))) )