From a898a3baec6386e83a775b1bec73a5f18cd60110 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Fri, 17 Jan 2025 19:27:01 +0100 Subject: [PATCH 1/7] config descriptors --- src/pystencils/codegen/config.py | 131 ++++++++++++++++++++++++++++++- tests/codegen/test_config.py | 57 ++++++++++++++ 2 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 tests/codegen/test_config.py diff --git a/src/pystencils/codegen/config.py b/src/pystencils/codegen/config.py index 3a7647907..83602dbbc 100644 --- a/src/pystencils/codegen/config.py +++ b/src/pystencils/codegen/config.py @@ -2,10 +2,12 @@ from __future__ import annotations from typing import TYPE_CHECKING from warnings import warn +from abc import ABC from collections.abc import Collection +from copy import copy -from typing import Sequence -from dataclasses import dataclass, InitVar, replace +from typing import Sequence, Generic, TypeVar, Callable, Any, cast +from dataclasses import dataclass, InitVar, replace, fields from .target import Target from ..field import Field, FieldType @@ -28,6 +30,131 @@ class PsOptionsError(Exception): """Indicates an option clash in the `CreateKernelConfig`.""" +Option_T = TypeVar("Option_T") +Arg_T = TypeVar("Arg_T") + + +class Option(Generic[Option_T, Arg_T]): + """Option descriptor. + + This descriptor is used to model configuration options. + It maintains a default value for the option that is used when no value + was specified by the user. + + In configuration options, the value `None` stands for `unset`. + It can therefore not be used to set an option to the meaning "not any", or "empty" + - for these, special values need to be used. + + The Option allows a validator function to be specified, + which will be called to perform sanity checks on user-provided values. + + Through the validator, options may also be set from arguments of a different type (`Arg_T`) + than their value type (`Option_T`). If `Arg_T` is different from `Option_T`, + the validator must perform the conversion from the former to the latter. + """ + + def __init__( + self, + default: Option_T | None = None, + validator: Callable[[Any, Arg_T | None], Option_T | None] | None = None, + ) -> None: + self._default = default + self._validator = validator + self._name: str + self._lookup: str + + def validate(self, validator: Callable[[Any, Any], Any] | None): + self._validator = validator + return validator + + @property + def default(self) -> Option_T | None: + return self._default + + def get(self, obj) -> Option_T | None: + val = getattr(obj, self._lookup, None) + if val is None: + return self._default + else: + return val + + def is_set(self, obj) -> bool: + return getattr(obj, self._lookup, None) is not None + + def __set_name__(self, owner, name: str): + self._name = name + self._lookup = f"_{name}" + + def __get__(self, obj, objtype=None) -> Option_T | None: + if obj is None: + return None + + return getattr(obj, self._lookup, None) + + def __set__(self, obj, arg: Arg_T | None): + if arg is not None and self._validator is not None: + value = self._validator(obj, arg) + else: + value = cast(Option_T, arg) + setattr(obj, self._lookup, value) + + def __delete__(self, obj): + delattr(obj, self._lookup) + + +class SimpleOption(Option[Option_T, Option_T]): + ... + + +class ConfigBase(ABC): + def get_option(self, name: str) -> Any: + """Get the value set for the specified option, or the option's default value if none has been set.""" + descr: Option = type(self).__dict__[name] + return descr.get(self) + + def is_option_set(self, name: str) -> bool: + descr: Option = type(self).__dict__[name] + return descr.is_set(self) + + def override(self, other: ConfigBase): + for f in fields(self): # type: ignore + fvalue = getattr(self, f.name) + if isinstance(fvalue, ConfigBase): # type: ignore + fvalue.override(getattr(other, f.name)) + else: + new_val = getattr(other, f.name) + if new_val is not None: + setattr(self, f.name, new_val) + + +Category_T = TypeVar("Category_T", bound=ConfigBase) + + +class Category(Generic[Category_T]): + """Descriptor for a category of options. + + This descriptor makes sure that when an entire category is set to an object, + that object is copied immediately such that later changes to the original + do not affect this configuration. + """ + + def __init__(self, default: Category_T): + self._default = default + + def __set_name__(self, owner, name: str): + self._name = name + self._lookup = f"_{name}" + + def __get__(self, obj, objtype=None) -> Category_T: + if obj is None: + return self._default + + return cast(Category_T, getattr(obj, self._lookup, None)) + + def __set__(self, obj, cat: Category_T): + setattr(obj, self._lookup, copy(cat)) + + class _AUTO_TYPE: ... # noqa: E701 diff --git a/tests/codegen/test_config.py b/tests/codegen/test_config.py new file mode 100644 index 000000000..613c279c3 --- /dev/null +++ b/tests/codegen/test_config.py @@ -0,0 +1,57 @@ +from dataclasses import dataclass +from pystencils.codegen.config import SimpleOption, Option, Category, ConfigBase + + +def test_descriptors(): + + @dataclass + class SampleCategory(ConfigBase): + val1: SimpleOption[int] = SimpleOption(2) + val2: Option[bool, str] = Option(False) + + @val2.validate + def _val2(self, v: str): + if v.lower() in ("off", "false", "no"): + return False + elif v.lower() in ("on", "true", "yes"): + return True + + raise ValueError() + + @dataclass + class SampleConfig(ConfigBase): + cat: Category[SampleCategory] = Category(SampleCategory()) + val: SimpleOption[str] = SimpleOption("fallback") + + cfg = SampleConfig() + + # Check unset and default values + assert cfg.val is None + assert cfg.get_option("val") == "fallback" + + # Check setting + cfg.val = "test" + assert cfg.val == "test" + assert cfg.get_option("val") == "test" + assert cfg.is_option_set("val") + + # Check unsetting + cfg.val = None + assert not cfg.is_option_set("val") + assert cfg.val is None + + # Check category + assert cfg.cat.val1 is None + assert cfg.cat.get_option("val1") == 2 + assert cfg.cat.val2 is None + assert cfg.cat.get_option("val2") is False + + # Check copy on category setting + c = SampleCategory(32, "on") + cfg.cat = c + assert cfg.cat.val1 == 32 + assert cfg.cat.val2 is True + + assert cfg.cat is not c + c.val1 = 13 + assert cfg.cat.val1 == 32 -- GitLab From 11c30f08cb2a00d8caa6ffb6f7ca47b351b3bda2 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Fri, 17 Jan 2025 20:20:29 +0100 Subject: [PATCH 2/7] Update config classes to use descriptors. Update parts of the codegen driver --- src/pystencils/__init__.py | 12 +- src/pystencils/backend/platforms/cuda.py | 8 +- src/pystencils/backend/platforms/sycl.py | 8 +- .../backend/transformations/add_pragmas.py | 4 +- src/pystencils/codegen/__init__.py | 12 +- src/pystencils/codegen/config.py | 282 ++++++++---------- src/pystencils/codegen/driver.py | 50 +++- tests/codegen/test_config.py | 25 +- tests/fixtures.py | 2 +- tests/kernelcreation/test_iteration_slices.py | 6 +- tests/nbackend/kernelcreation/test_openmp.py | 8 +- tests/test_quicktests.py | 2 +- 12 files changed, 215 insertions(+), 204 deletions(-) diff --git a/src/pystencils/__init__.py b/src/pystencils/__init__.py index 6cb375b61..2bb4aac3d 100644 --- a/src/pystencils/__init__.py +++ b/src/pystencils/__init__.py @@ -3,10 +3,10 @@ from .codegen import ( Target, CreateKernelConfig, - CpuOptimConfig, + CpuOptions, VectorizationConfig, - OpenMpConfig, - GpuIndexingConfig, + OpenMpOptions, + GpuOptions, AUTO ) from .defaults import DEFAULTS @@ -50,10 +50,10 @@ __all__ = [ "create_numeric_type", "make_slice", "CreateKernelConfig", - "CpuOptimConfig", + "CpuOptions", "VectorizationConfig", - "GpuIndexingConfig", - "OpenMpConfig", + "GpuOptions", + "OpenMpOptions", "AUTO", "create_kernel", "create_staggered_kernel", diff --git a/src/pystencils/backend/platforms/cuda.py b/src/pystencils/backend/platforms/cuda.py index f146cfbfd..31686cb18 100644 --- a/src/pystencils/backend/platforms/cuda.py +++ b/src/pystencils/backend/platforms/cuda.py @@ -30,7 +30,7 @@ from ..literals import PsLiteral from ..functions import PsMathFunction, MathFunctions, CFunction if TYPE_CHECKING: - from ...codegen import GpuIndexingConfig, GpuThreadsRange + from ...codegen import GpuOptions, GpuThreadsRange int32 = PsSignedIntegerType(width=32, const=False) @@ -52,13 +52,13 @@ class CudaPlatform(GenericGpu): """Platform for CUDA-based GPUs.""" def __init__( - self, ctx: KernelCreationContext, indexing_cfg: GpuIndexingConfig | None = None + self, ctx: KernelCreationContext, indexing_cfg: GpuOptions | None = None ) -> None: super().__init__(ctx) - from ...codegen.config import GpuIndexingConfig + from ...codegen.config import GpuOptions - self._cfg = indexing_cfg if indexing_cfg is not None else GpuIndexingConfig() + self._cfg = indexing_cfg if indexing_cfg is not None else GpuOptions() self._typify = Typifier(ctx) @property diff --git a/src/pystencils/backend/platforms/sycl.py b/src/pystencils/backend/platforms/sycl.py index 9c04d6074..b5ba7b6c4 100644 --- a/src/pystencils/backend/platforms/sycl.py +++ b/src/pystencils/backend/platforms/sycl.py @@ -30,19 +30,19 @@ from ..exceptions import MaterializationError from ...types import PsCustomType, PsIeeeFloatType, constify, PsIntegerType if TYPE_CHECKING: - from ...codegen import GpuIndexingConfig, GpuThreadsRange + from ...codegen import GpuOptions, GpuThreadsRange class SyclPlatform(GenericGpu): def __init__( - self, ctx: KernelCreationContext, indexing_cfg: GpuIndexingConfig | None = None + self, ctx: KernelCreationContext, indexing_cfg: GpuOptions | None = None ): super().__init__(ctx) - from ...codegen.config import GpuIndexingConfig + from ...codegen.config import GpuOptions - self._cfg = indexing_cfg if indexing_cfg is not None else GpuIndexingConfig() + self._cfg = indexing_cfg if indexing_cfg is not None else GpuOptions() @property def required_headers(self) -> set[str]: diff --git a/src/pystencils/backend/transformations/add_pragmas.py b/src/pystencils/backend/transformations/add_pragmas.py index 78e721f38..b033e4d58 100644 --- a/src/pystencils/backend/transformations/add_pragmas.py +++ b/src/pystencils/backend/transformations/add_pragmas.py @@ -11,7 +11,7 @@ from ..ast.structural import PsBlock, PsLoop, PsPragma from ..ast.expressions import PsExpression if TYPE_CHECKING: - from ...codegen.config import OpenMpConfig + from ...codegen.config import OpenMpOptions __all__ = ["InsertPragmasAtLoops", "LoopPragma", "AddOpenMP"] @@ -105,7 +105,7 @@ class AddOpenMP: `OpenMpConfig` configuration. """ - def __init__(self, ctx: KernelCreationContext, omp_params: OpenMpConfig) -> None: + def __init__(self, ctx: KernelCreationContext, omp_params: OpenMpOptions) -> None: pragma_text = "omp" pragma_text += " parallel" if not omp_params.omit_parallel_construct else "" pragma_text += f" for schedule({omp_params.schedule})" diff --git a/src/pystencils/codegen/__init__.py b/src/pystencils/codegen/__init__.py index e27b94b9e..da33f9ee2 100644 --- a/src/pystencils/codegen/__init__.py +++ b/src/pystencils/codegen/__init__.py @@ -1,10 +1,10 @@ from .target import Target from .config import ( CreateKernelConfig, - CpuOptimConfig, + CpuOptions, VectorizationConfig, - OpenMpConfig, - GpuIndexingConfig, + OpenMpOptions, + GpuOptions, AUTO, ) from .parameters import Parameter @@ -14,10 +14,10 @@ from .driver import create_kernel, get_driver __all__ = [ "Target", "CreateKernelConfig", - "CpuOptimConfig", + "CpuOptions", "VectorizationConfig", - "OpenMpConfig", - "GpuIndexingConfig", + "OpenMpOptions", + "GpuOptions", "AUTO", "Parameter", "Kernel", diff --git a/src/pystencils/codegen/config.py b/src/pystencils/codegen/config.py index 83602dbbc..4e17ccc48 100644 --- a/src/pystencils/codegen/config.py +++ b/src/pystencils/codegen/config.py @@ -77,7 +77,7 @@ class Option(Generic[Option_T, Arg_T]): return self._default else: return val - + def is_set(self, obj) -> bool: return getattr(obj, self._lookup, None) is not None @@ -102,8 +102,7 @@ class Option(Generic[Option_T, Arg_T]): delattr(obj, self._lookup) -class SimpleOption(Option[Option_T, Option_T]): - ... +class BasicOption(Option[Option_T, Option_T]): ... class ConfigBase(ABC): @@ -111,7 +110,7 @@ class ConfigBase(ABC): """Get the value set for the specified option, or the option's default value if none has been set.""" descr: Option = type(self).__dict__[name] return descr.get(self) - + def is_option_set(self, name: str) -> bool: descr: Option = type(self).__dict__[name] return descr.is_set(self) @@ -132,7 +131,7 @@ Category_T = TypeVar("Category_T", bound=ConfigBase) class Category(Generic[Category_T]): """Descriptor for a category of options. - + This descriptor makes sure that when an entire category is set to an object, that object is copied immediately such that later changes to the original do not affect this configuration. @@ -168,22 +167,25 @@ Currently, these options permit `AUTO`: @dataclass -class OpenMpConfig: +class OpenMpOptions(ConfigBase): """Parameters controlling kernel parallelization using OpenMP.""" - nesting_depth: int = 0 + enable: BasicOption[bool] = BasicOption(False) + """Enable OpenMP instrumentation""" + + nesting_depth: BasicOption[int] = BasicOption(0) """Nesting depth of the loop that should be parallelized. Must be a nonnegative number.""" - collapse: int = 0 + collapse: BasicOption[int] = BasicOption() """Argument to the OpenMP ``collapse`` clause""" - schedule: str = "static" + schedule: BasicOption[str] = BasicOption("static") """Argument to the OpenMP ``schedule`` clause""" - num_threads: int | None = None + num_threads: BasicOption[int] = BasicOption() """Set the number of OpenMP threads to execute the parallel region.""" - omit_parallel_construct: bool = False + omit_parallel_construct: BasicOption[bool] = BasicOption(False) """If set to ``True``, the OpenMP ``parallel`` construct is omitted, producing just a ``#pragma omp for``. Use this option only if you intend to wrap the kernel into an external ``#pragma omp parallel`` region. @@ -197,62 +199,17 @@ class OpenMpConfig: @dataclass -class CpuOptimConfig: - """Configuration for the CPU optimizer. - - If any flag in this configuration is set to a value not supported by the CPU specified - in `CreateKernelConfig.target`, an error will be raised. - """ - - openmp: bool | OpenMpConfig = False - """Enable OpenMP parallelization. - - If set to `True`, the kernel will be parallelized using OpenMP according to the default settings in `OpenMpConfig`. - To customize OpenMP parallelization, pass an instance of `OpenMpConfig` instead. - """ - - vectorize: bool | VectorizationConfig = False - """Enable and configure auto-vectorization. - - If set to an instance of `VectorizationConfig` and a CPU target with vector capabilities is selected, - pystencils will attempt to vectorize the kernel according to the given vectorization options. - - If set to `True`, pystencils will infer vectorization options from the given CPU target. - - If set to `False`, no vectorization takes place. - """ - - loop_blocking: None | tuple[int, ...] = None - """Block sizes for loop blocking. - - If set, the kernel's loops will be tiled according to the given block sizes. - """ - - use_cacheline_zeroing: bool = False - """Enable cache-line zeroing. - - If set to `True` and the selected CPU supports cacheline zeroing, the CPU optimizer will attempt - to produce cacheline zeroing instructions where possible. - """ - - def get_vectorization_config(self) -> VectorizationConfig | None: - if self.vectorize is True: - return VectorizationConfig() - elif isinstance(self.vectorize, VectorizationConfig): - return self.vectorize - else: - return None - - -@dataclass -class VectorizationConfig: +class VectorizationConfig(ConfigBase): """Configuration for the auto-vectorizer. If any flag in this configuration is set to a value not supported by the CPU specified in `CreateKernelConfig.target`, an error will be raised. """ - lanes: int | None = None + enable: BasicOption[bool] = BasicOption(False) + """Enable intrinsic vectorization.""" + + lanes: BasicOption[int] = BasicOption() """Number of SIMD lanes to be used in vectorization. If set to `None` (the default), the vector register width will be automatically set to the broadest possible. @@ -261,7 +218,9 @@ class VectorizationConfig: operation contained in the kernel with the given number of lanes, an error will be raised. """ - use_nontemporal_stores: bool | Collection[str | Field] = False + use_nontemporal_stores: BasicOption[bool | Collection[str | Field]] = BasicOption( + False + ) """Enable nontemporal (streaming) stores. If set to `True` and the selected CPU supports streaming stores, the vectorizer will generate @@ -271,14 +230,14 @@ class VectorizationConfig: the given fields. """ - assume_aligned: bool = False + assume_aligned: BasicOption[bool] = BasicOption(False) """Assume field pointer alignment. If set to `True`, the vectorizer will assume that the address of the first inner entry (after ghost layers) of each field is aligned at the necessary byte boundary. """ - assume_inner_stride_one: bool = False + assume_inner_stride_one: BasicOption[bool] = BasicOption(False) """Assume stride associated with the innermost spatial coordinate of all fields is one. If set to `True`, the vectorizer will replace the stride of the innermost spatial coordinate @@ -307,10 +266,48 @@ class VectorizationConfig: @dataclass -class GpuIndexingConfig: +class CpuOptions(ConfigBase): + """Configuration for the CPU optimizer. + + If any flag in this configuration is set to a value not supported by the CPU specified + in `CreateKernelConfig.target`, an error will be raised. + """ + + openmp: Category[OpenMpOptions] = Category(OpenMpOptions()) + """Options governing OpenMP-instrumentation. + """ + + vectorize: Category[OpenMpOptions] = Category(OpenMpOptions()) + """Options governing intrinsic vectorization. + """ + + loop_blocking: BasicOption[tuple[int, ...]] = BasicOption() + """Block sizes for loop blocking. + + If set, the kernel's loops will be tiled according to the given block sizes. + """ + + use_cacheline_zeroing: BasicOption[bool] = BasicOption(False) + """Enable cache-line zeroing. + + If set to `True` and the selected CPU supports cacheline zeroing, the CPU optimizer will attempt + to produce cacheline zeroing instructions where possible. + """ + + def get_vectorization_config(self) -> VectorizationConfig | None: + if self.vectorize is True: + return VectorizationConfig() + elif isinstance(self.vectorize, VectorizationConfig): + return self.vectorize + else: + return None + + +@dataclass +class GpuOptions(ConfigBase): """Configure index translation behaviour for kernels generated for GPU targets.""" - omit_range_check: bool = False + omit_range_check: BasicOption[bool] = BasicOption(False) """If set to `True`, omit the iteration counter range check. By default, the code generator introduces a check if the iteration counters computed from GPU block and thread @@ -318,10 +315,10 @@ class GpuIndexingConfig: This check can be discarded through this option, at your own peril. """ - block_size: tuple[int, int, int] | None = None + block_size: BasicOption[tuple[int, int, int]] = BasicOption() """Desired block size for the execution of GPU kernels. May be overridden later by the runtime system.""" - manual_launch_grid: bool = False + manual_launch_grid: BasicOption[bool] = BasicOption(False) """Always require a manually specified launch grid when running this kernel. If set to `True`, the code generator will not attempt to infer the size of @@ -329,7 +326,7 @@ class GpuIndexingConfig: The launch grid will then have to be specified manually at runtime. """ - sycl_automatic_block_size: bool = True + sycl_automatic_block_size: BasicOption[bool] = BasicOption(True) """If set to `True` while generating for `Target.SYCL`, let the SYCL runtime decide on the block size. If set to `True`, the kernel is generated for execution via @@ -343,24 +340,30 @@ class GpuIndexingConfig: """ +GhostLayerSpec = _AUTO_TYPE | int | Sequence[int | tuple[int, int]] + + +IterationSliceSpec = int | slice | tuple[int | slice] + + @dataclass -class CreateKernelConfig: +class CreateKernelConfig(ConfigBase): """Options for create_kernel.""" - target: Target = Target.GenericCPU + target: BasicOption[Target] = BasicOption(Target.GenericCPU) """The code generation target.""" - jit: JitBase | None = None + jit: BasicOption[JitBase] = BasicOption() """Just-in-time compiler used to compile and load the kernel for invocation from the current Python environment. If left at `None`, a default just-in-time compiler will be inferred from the `target` parameter. To explicitly disable JIT compilation, pass `pystencils.no_jit <pystencils.jit.no_jit>`. """ - function_name: str = "kernel" + function_name: BasicOption[str] = BasicOption("kernel") """Name of the generated function""" - ghost_layers: None | _AUTO_TYPE | int | Sequence[int | tuple[int, int]] = None + ghost_layers: BasicOption[GhostLayerSpec] = BasicOption() """Specifies the number of ghost layers of the iteration region. Options: @@ -376,7 +379,7 @@ class CreateKernelConfig: At most one of `ghost_layers`, `iteration_slice`, and `index_field` may be set. """ - iteration_slice: None | int | slice | tuple[int | slice] = None + iteration_slice: BasicOption[IterationSliceSpec] = BasicOption() """Specifies the kernel's iteration slice. Example: @@ -390,7 +393,7 @@ class CreateKernelConfig: At most one of `ghost_layers`, `iteration_slice`, and `index_field` may be set. """ - index_field: Field | None = None + index_field: BasicOption[Field] = BasicOption() """Index field for a sparse kernel. If this option is set, a sparse kernel with the given field as index field will be generated. @@ -401,10 +404,10 @@ class CreateKernelConfig: """Data Types""" - index_dtype: UserTypeSpec = DEFAULTS.index_dtype + index_dtype: Option[PsIntegerType, UserTypeSpec] = Option(DEFAULTS.index_dtype) """Data type used for all index calculations.""" - default_dtype: UserTypeSpec = PsIeeeFloatType(64) + default_dtype: Option[PsScalarType, UserTypeSpec] = Option(DEFAULTS.numeric_dtype) """Default numeric data type. This data type will be applied to all untyped symbols. @@ -412,14 +415,14 @@ class CreateKernelConfig: """Analysis""" - allow_double_writes: bool = False + allow_double_writes: BasicOption[bool] = BasicOption(False) """ If True, don't check if every field is only written at a single location. This is required for example for kernels that are compiled with loop step sizes > 1, that handle multiple cells at once. Use with care! """ - skip_independence_check: bool = False + skip_independence_check: BasicOption[bool] = BasicOption(False) """ By default the assignment list is checked for read/write independence. This means fields are only written at locations where they are read. Doing so guarantees thread safety. In some cases e.g. for @@ -428,17 +431,33 @@ class CreateKernelConfig: """Target-Specific Options""" - cpu_optim: None | CpuOptimConfig = None - """Configuration of the CPU kernel optimizer. - - If this parameter is set while `target` is a non-CPU target, an error will be raised. - """ - - gpu_indexing: None | GpuIndexingConfig = None - """Configure index translation for GPU kernels. - - It this parameter is set while `target` is not a GPU target, an error will be raised. - """ + cpu_optim: Category[CpuOptions] = Category(CpuOptions()) + """Options for CPU kernels.""" + + gpu_indexing: Category[GpuOptions] = Category(GpuOptions()) + """Options for GPU Kernels.""" + + @index_dtype.validate + def validate_index_type(self, spec: UserTypeSpec): + dtype = create_type(spec) + if not isinstance(dtype, PsIntegerType): + raise ValueError("index_dtype must be an integer type") + return dtype + + @default_dtype.validate + def validate_default_dtype(self, spec: UserTypeSpec): + dtype = create_type(spec) + if not isinstance(dtype, PsScalarType): + raise ValueError("default_dtype must be a scalar numeric type") + return dtype + + @index_field.validate + def validate_index_field(self, idx_field: Field): + if idx_field.field_type != FieldType.INDEXED: + raise ValueError( + "Only fields of type FieldType.INDEXED can be used as index fields" + ) + return idx_field # Deprecated Options @@ -457,20 +476,23 @@ class CreateKernelConfig: # Getters def get_target(self) -> Target: - match self.target: + t: Target = self.get_option("target") + match t: case Target.CurrentCPU: return Target.auto_cpu() case _: - return self.target + return t def get_jit(self) -> JitBase: """Returns either the user-specified JIT compiler, or infers one from the target if none is given.""" - if self.jit is None: - if self.target.is_cpu(): + jit: JitBase | None = self.get_option("jit") + + if jit is None: + if self.get_target().is_cpu(): from ..jit import LegacyCpuJit return LegacyCpuJit() - elif self.target == Target.CUDA: + elif self.get_target() == Target.CUDA: try: from ..jit.gpu_cupy import CupyJit @@ -487,7 +509,7 @@ class CreateKernelConfig: return no_jit - elif self.target == Target.SYCL: + elif self.get_target() == Target.SYCL: from ..jit import no_jit return no_jit @@ -496,64 +518,14 @@ class CreateKernelConfig: f"No default JIT compiler implemented yet for target {self.target}" ) else: - return self.jit + return jit # Postprocessing def __post_init__(self, *args): - # Check deprecated options self._check_deprecations(*args) - # Check index data type - if not isinstance(create_type(self.index_dtype), PsIntegerType): - raise PsOptionsError("`index_dtype` was not an integer type.") - - # Check iteration space argument consistency - if ( - int(self.iteration_slice is not None) - + int(self.ghost_layers is not None) - + int(self.index_field is not None) - > 1 - ): - raise PsOptionsError( - "Parameters `iteration_slice`, `ghost_layers` and 'index_field` are mutually exclusive; " - "at most one of them may be set." - ) - - # Check index field - if ( - self.index_field is not None - and self.index_field.field_type != FieldType.INDEXED - ): - raise PsOptionsError( - "Only fields with `field_type == FieldType.INDEXED` can be specified as `index_field`" - ) - - # Check optim - if self.cpu_optim is not None: - if ( - self.cpu_optim.vectorize is not False - and not self.target.is_vector_cpu() - ): - raise PsOptionsError( - f"Cannot enable auto-vectorization for non-vector CPU target {self.target}" - ) - - if self.gpu_indexing is not None: - if isinstance(self.gpu_indexing, str): - match self.gpu_indexing: - case "block": - self.gpu_indexing = GpuIndexingConfig() - case "line": - raise NotImplementedError( - "GPU line indexing is currently unavailable." - ) - case other: - raise PsOptionsError( - f"Invalid value for option gpu_indexing: {other}" - ) - def _check_deprecations( self, data_type: UserTypeSpec | None, @@ -561,7 +533,7 @@ class CreateKernelConfig: cpu_vectorize_info: dict | None, gpu_indexing_params: dict | None, ): # pragma: no cover - optim: CpuOptimConfig | None = None + optim: CpuOptions | None = None if data_type is not None: _deprecated_option("data_type", "default_dtype") @@ -575,20 +547,20 @@ class CreateKernelConfig: if cpu_openmp is not None: _deprecated_option("cpu_openmp", "cpu_optim.openmp") - deprecated_omp: OpenMpConfig | bool + deprecated_omp: OpenMpOptions | bool match cpu_openmp: case True: - deprecated_omp = OpenMpConfig() + deprecated_omp = OpenMpOptions() case False: deprecated_omp = False case int(): - deprecated_omp = OpenMpConfig(num_threads=cpu_openmp) + deprecated_omp = OpenMpOptions(num_threads=cpu_openmp) case _: raise PsOptionsError( f"Invalid option for `cpu_openmp`: {cpu_openmp}" ) - optim = CpuOptimConfig(openmp=deprecated_omp) + optim = CpuOptions(openmp=deprecated_omp) if cpu_vectorize_info is not None: _deprecated_option("cpu_vectorize_info", "cpu_optim.vectorize") @@ -637,7 +609,7 @@ class CreateKernelConfig: if optim is not None: optim = replace(optim, vectorize=deprecated_vec_opts) else: - optim = CpuOptimConfig(vectorize=deprecated_vec_opts) + optim = CpuOptions(vectorize=deprecated_vec_opts) if optim is not None: if self.cpu_optim is not None: @@ -655,7 +627,7 @@ class CreateKernelConfig: "Cannot specify both `gpu_indexing` and the deprecated `gpu_indexing_params` at the same time." ) - self.gpu_indexing = GpuIndexingConfig( + self.gpu_indexing = GpuOptions( block_size=gpu_indexing_params.get("block_size", None) ) diff --git a/src/pystencils/codegen/driver.py b/src/pystencils/codegen/driver.py index 28b685b55..47bcb905d 100644 --- a/src/pystencils/codegen/driver.py +++ b/src/pystencils/codegen/driver.py @@ -3,12 +3,13 @@ from typing import cast, Sequence, Iterable, TYPE_CHECKING from dataclasses import dataclass, replace from .target import Target -from .config import CreateKernelConfig, OpenMpConfig, VectorizationConfig, AUTO +from .config import CreateKernelConfig, OpenMpOptions, VectorizationConfig, AUTO, GhostLayerSpec, IterationSliceSpec from .kernel import Kernel, GpuKernel, GpuThreadsRange from .properties import PsSymbolProperty, FieldShape, FieldStride, FieldBasePtr from .parameters import Parameter -from ..types import create_numeric_type, PsIntegerType, PsScalarType +from ..field import Field +from ..types import PsIntegerType, PsScalarType from ..backend.memory import PsSymbol from ..backend.ast import PsAstNode @@ -105,15 +106,36 @@ class DefaultKernelCreationDriver: def __init__(self, cfg: CreateKernelConfig, retain_intermediates: bool = False): self._cfg = cfg - idx_dtype = create_numeric_type(self._cfg.index_dtype) - assert isinstance(idx_dtype, PsIntegerType) + # Data Type Options + idx_dtype: PsIntegerType = cfg.get_option("index_dtype") + default_dtype: PsScalarType = cfg.get_option("default_dtype") + + # Iteration Space Options + num_ispace_options_set = ( + int(cfg.is_option_set("ghost_layers")) + + int(cfg.is_option_set("iteration_slice")) + + int(cfg.is_option_set("index_field")) + ) + + if num_ispace_options_set > 1: + raise ValueError( + "At most one of the options 'ghost_layers' 'iteration_slice' and 'index_field' may be set." + ) + + self._ghost_layers: GhostLayerSpec | None = cfg.get_option("ghost_layers") + self._iteration_slice: IterationSliceSpec | None = cfg.get_option("iteration_slice") + self._index_field: Field | None = cfg.get_option("index_field") + + if num_ispace_options_set == 0: + self._ghost_layers = AUTO + # Create the context self._ctx = KernelCreationContext( - default_dtype=create_numeric_type(self._cfg.default_dtype), + default_dtype=default_dtype, index_dtype=idx_dtype, ) - self._target = self._cfg.get_target() + self._target = cfg.get_target() self._platform = self._get_platform() self._intermediates: CodegenIntermediates | None @@ -153,7 +175,7 @@ class DefaultKernelCreationDriver: self._intermediates.constants_eliminated = kernel_ast.clone() # Target-Specific optimizations - if self._cfg.target.is_cpu(): + if self._target.is_cpu(): kernel_ast = self._transform_for_cpu(kernel_ast) # Note: After this point, the AST may contain intrinsics, so type-dependent @@ -174,13 +196,13 @@ class DefaultKernelCreationDriver: canonicalize = CanonicalizeSymbols(self._ctx, True) kernel_ast = cast(PsBlock, canonicalize(kernel_ast)) - if self._cfg.target.is_cpu(): + if self._target.is_cpu(): return create_cpu_kernel_function( self._ctx, self._platform, kernel_ast, - self._cfg.function_name, - self._cfg.target, + self._cfg.get_option("function_name"), + self._target, self._cfg.get_jit(), ) else: @@ -189,8 +211,8 @@ class DefaultKernelCreationDriver: self._platform, kernel_ast, gpu_threads, - self._cfg.function_name, - self._cfg.target, + self._cfg.get_option("function_name"), + self._target, self._cfg.get_jit(), ) @@ -272,8 +294,8 @@ class DefaultKernelCreationDriver: params = ( cpu_cfg.openmp - if isinstance(cpu_cfg.openmp, OpenMpConfig) - else OpenMpConfig() + if isinstance(cpu_cfg.openmp, OpenMpOptions) + else OpenMpOptions() ) add_omp = AddOpenMP(self._ctx, params) kernel_ast = cast(PsBlock, add_omp(kernel_ast)) diff --git a/tests/codegen/test_config.py b/tests/codegen/test_config.py index 613c279c3..715830e70 100644 --- a/tests/codegen/test_config.py +++ b/tests/codegen/test_config.py @@ -1,16 +1,20 @@ +import pytest + from dataclasses import dataclass -from pystencils.codegen.config import SimpleOption, Option, Category, ConfigBase +import numpy as np +from pystencils.codegen.config import BasicOption, Option, Category, ConfigBase, CreateKernelConfig +from pystencils.types.quick import Int, UInt def test_descriptors(): @dataclass class SampleCategory(ConfigBase): - val1: SimpleOption[int] = SimpleOption(2) + val1: BasicOption[int] = BasicOption(2) val2: Option[bool, str] = Option(False) @val2.validate - def _val2(self, v: str): + def validate_val2(self, v: str): if v.lower() in ("off", "false", "no"): return False elif v.lower() in ("on", "true", "yes"): @@ -21,7 +25,7 @@ def test_descriptors(): @dataclass class SampleConfig(ConfigBase): cat: Category[SampleCategory] = Category(SampleCategory()) - val: SimpleOption[str] = SimpleOption("fallback") + val: BasicOption[str] = BasicOption("fallback") cfg = SampleConfig() @@ -55,3 +59,16 @@ def test_descriptors(): assert cfg.cat is not c c.val1 = 13 assert cfg.cat.val1 == 32 + + +def test_config_validation(): + cfg = CreateKernelConfig(index_dtype="int32") + assert cfg.index_dtype == Int(32) + cfg.index_dtype = np.uint64 + assert cfg.index_dtype == UInt(64) + + with pytest.raises(ValueError): + _ = CreateKernelConfig(index_dtype=np.float32) + + with pytest.raises(ValueError): + cfg.index_dtype = "double" diff --git a/tests/fixtures.py b/tests/fixtures.py index 7c9521614..8c7f12015 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -49,7 +49,7 @@ def gen_config(target: ps.Target): if target.is_vector_cpu(): gen_config = replace( gen_config, - cpu_optim=ps.CpuOptimConfig( + cpu_optim=ps.CpuOptions( vectorize=ps.VectorizationConfig(assume_inner_stride_one=True) ), ) diff --git a/tests/kernelcreation/test_iteration_slices.py b/tests/kernelcreation/test_iteration_slices.py index fee3544f8..5c7b4d8cb 100644 --- a/tests/kernelcreation/test_iteration_slices.py +++ b/tests/kernelcreation/test_iteration_slices.py @@ -13,7 +13,7 @@ from pystencils import ( make_slice, Target, CreateKernelConfig, - GpuIndexingConfig, + GpuOptions, DynamicType, ) from pystencils.sympyextensions.integer_functions import int_rem @@ -141,7 +141,7 @@ def test_triangle_pattern(gen_config: CreateKernelConfig, xp): if gen_config.target == Target.CUDA: gen_config = replace( - gen_config, gpu_indexing=GpuIndexingConfig(manual_launch_grid=True) + gen_config, gpu_indexing=GpuOptions(manual_launch_grid=True) ) kernel = create_kernel(update, gen_config).compile() @@ -174,7 +174,7 @@ def test_red_black_pattern(gen_config: CreateKernelConfig, xp): if gen_config.target == Target.CUDA: gen_config = replace( - gen_config, gpu_indexing=GpuIndexingConfig(manual_launch_grid=True) + gen_config, gpu_indexing=GpuOptions(manual_launch_grid=True) ) try: diff --git a/tests/nbackend/kernelcreation/test_openmp.py b/tests/nbackend/kernelcreation/test_openmp.py index d7be8eb98..ae775ca20 100644 --- a/tests/nbackend/kernelcreation/test_openmp.py +++ b/tests/nbackend/kernelcreation/test_openmp.py @@ -4,8 +4,8 @@ from pystencils import ( Assignment, create_kernel, CreateKernelConfig, - CpuOptimConfig, - OpenMpConfig, + CpuOptions, + OpenMpOptions, Target, ) @@ -21,14 +21,14 @@ def test_openmp(nesting_depth, schedule, collapse, omit_parallel_construct): f, g = fields("f, g: [3D]") asm = Assignment(f.center(0), g.center(0)) - omp = OpenMpConfig( + omp = OpenMpOptions( nesting_depth=nesting_depth, schedule=schedule, collapse=collapse, omit_parallel_construct=omit_parallel_construct, ) gen_config = CreateKernelConfig( - target=Target.CPU, cpu_optim=CpuOptimConfig(openmp=omp) + target=Target.CPU, cpu_optim=CpuOptions(openmp=omp) ) kernel = create_kernel(asm, gen_config) diff --git a/tests/test_quicktests.py b/tests/test_quicktests.py index 5d5dba0ea..3e7f4f071 100644 --- a/tests/test_quicktests.py +++ b/tests/test_quicktests.py @@ -74,7 +74,7 @@ def test_basic_vectorization(): ast = ps.create_kernel( update_rule, target=target, - cpu_optim=ps.CpuOptimConfig( + cpu_optim=ps.CpuOptions( vectorize=ps.VectorizationConfig(assume_inner_stride_one=True) ), ) -- GitLab From d8610f6bf4a25fac4dd04aaad1f3cad4b0a484d6 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Mon, 20 Jan 2025 14:09:06 +0100 Subject: [PATCH 3/7] Fix deepcopy of categories. Remove mentions of `config` from the backend. Adapt test suite. --- .../01_tutorial_getting_started.ipynb | 298 +++++++++--------- src/pystencils/__init__.py | 4 +- .../backend/kernelcreation/iteration_space.py | 64 +++- src/pystencils/backend/platforms/cuda.py | 16 +- src/pystencils/backend/platforms/sycl.py | 24 +- .../backend/transformations/add_pragmas.py | 37 ++- src/pystencils/codegen/__init__.py | 4 +- src/pystencils/codegen/config.py | 160 ++++++---- src/pystencils/codegen/driver.py | 107 +++++-- src/pystencils/types/types.py | 2 +- tests/codegen/test_config.py | 88 +++++- tests/fixtures.py | 17 +- tests/kernelcreation/test_buffer_gpu.py | 4 +- tests/kernelcreation/test_gpu.py | 6 +- tests/kernelcreation/test_iteration_slices.py | 13 +- tests/nbackend/kernelcreation/test_openmp.py | 7 +- tests/nbackend/kernelcreation/test_options.py | 28 -- .../transformations/test_add_pragmas.py | 1 + tests/test_quicktests.py | 4 +- 19 files changed, 524 insertions(+), 360 deletions(-) delete mode 100644 tests/nbackend/kernelcreation/test_options.py diff --git a/docs/source/tutorials/01_tutorial_getting_started.ipynb b/docs/source/tutorials/01_tutorial_getting_started.ipynb index 5ce765fce..04dc50e51 100644 --- a/docs/source/tutorials/01_tutorial_getting_started.ipynb +++ b/docs/source/tutorials/01_tutorial_getting_started.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 37, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -52,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -63,14 +63,14 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "4.74 ms ± 1.1 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "3.91 ms ± 88.9 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -88,22 +88,19 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", "text/latex": [ "$\\displaystyle {dst}_{(0,0)} \\leftarrow_{} \\frac{{src}_{(1,0)}}{4} + \\frac{{src}_{(0,1)}}{4} + \\frac{{src}_{(0,-1)}}{4} + \\frac{{src}_{(-1,0)}}{4}$" ], "text/plain": [ - " src_E src_N src_S src_W\n", - "dst_C := ───── + ───── + ───── + ─────\n", - " 4 4 4 4 " + "Assignment(dst_C, src_E/4 + src_N/4 + src_S/4 + src_W/4)" ] }, - "execution_count": 41, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -118,12 +115,12 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "<Figure size 300x300 with 1 Axes>" ] @@ -147,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -165,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -175,14 +172,14 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "548 μs ± 34.7 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" + "448 μs ± 72.8 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" ] } ], @@ -212,7 +209,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -229,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -238,7 +235,7 @@ "sympy.core.symbol.Symbol" ] }, - "execution_count": 47, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -258,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -272,7 +269,7 @@ "x â‹…(x + y + 5) + x " ] }, - "execution_count": 48, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -291,7 +288,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -305,7 +302,7 @@ "x + x â‹…y + 6â‹…x " ] }, - "execution_count": 49, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -316,7 +313,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -330,7 +327,7 @@ "x â‹…(x + y + 6)" ] }, - "execution_count": 50, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -341,7 +338,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -355,7 +352,7 @@ "x â‹…(x + cos(x) + 5) + x " ] }, - "execution_count": 51, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -373,7 +370,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -387,7 +384,7 @@ "x â‹…(x + y + 5) + x = 1" ] }, - "execution_count": 52, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -399,7 +396,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -415,7 +412,7 @@ "⎣ x ⎦" ] }, - "execution_count": 53, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -433,7 +430,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -447,7 +444,7 @@ "x â‹…(x + y + 5) + x " ] }, - "execution_count": 54, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -458,7 +455,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -467,7 +464,7 @@ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", - "<!-- Generated by graphviz version 11.0.0 (0)\n", + "<!-- Generated by graphviz version 12.1.2 (0)\n", " -->\n", "<!-- Pages: 1 -->\n", "<svg width=\"422pt\" height=\"260pt\"\n", @@ -616,10 +613,10 @@ "</svg>\n" ], "text/plain": [ - "<graphviz.sources.Source at 0x7e3154f58d30>" + "<graphviz.sources.Source at 0x7fd4d0f809d0>" ] }, - "execution_count": 55, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -638,7 +635,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -647,7 +644,7 @@ "sympy.core.add.Add" ] }, - "execution_count": 56, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -658,7 +655,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -672,7 +669,7 @@ "âŽx , x â‹…(x + y + 5)⎠" ] }, - "execution_count": 57, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -699,7 +696,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -715,7 +712,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -728,7 +725,7 @@ "f_E__1" ] }, - "execution_count": 59, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -747,7 +744,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -756,7 +753,7 @@ "True" ] }, - "execution_count": 60, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -776,7 +773,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -785,7 +782,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -802,7 +799,7 @@ "_W__2â‹…wâ‚‚) " ] }, - "execution_count": 62, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -824,7 +821,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -841,7 +838,7 @@ " img_W__2â‹…wâ‚‚) " ] }, - "execution_count": 63, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -860,7 +857,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -877,7 +874,7 @@ "g_SW__2 - img_W__2â‹…wâ‚‚) " ] }, - "execution_count": 64, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -897,14 +894,16 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/media/data/fhennig/research-hpc/projects/2024_pystencils_nbackend/pystencils/src/pystencils/config.py:327: FutureWarning: The `cpu_openmp` option of CreateKernelConfig is deprecated and will be removed in pystencils 2.1. Use `cpu_optim.openmp` instead.\n", + "/media/data/fhennig/research-hpc/projects/2024_pystencils_nbackend/pystencils/src/pystencils/codegen/config.py:633: FutureWarning: The `cpu_openmp` option of CreateKernelConfig is deprecated and will be removed in pystencils 2.1. Use `cpu_optim.openmp` instead.\n", + " warn(\n", + "/media/data/fhennig/research-hpc/projects/2024_pystencils_nbackend/pystencils/src/pystencils/codegen/config.py:543: UserWarning: Setting the deprecated `cpu_openmp` option will override any options passed in the `cpu.openmp` category.\n", " warn(\n" ] } @@ -925,15 +924,18 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 30, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "No requests or imageio installed\n" - ] + "data": { + "image/png": "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", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -953,12 +955,12 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 31, "metadata": {}, "outputs": [ { "data": { - "image/png": "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nAqnQcrGpN2rrrrqyPcLp0ehPZxi8c53vnOjs449v+iiizZcn+6e7xj0FI+OVv+TP/mTg5e//OUHV1555ZHO1bc3OB28Bk9/+tOP0Gt8ASy8ngJ25xh827d92+7SSy89/PtTn/rU7iu/8it3L3rRi27VcR0XeP7zn7+7973vfdLvOub4vPPO27361a8+/Ozv//7vtyOQ3/GOd5zFUR4vCD9XX3314d833XTT7i53ucvuV3/1V4/g9nM/93N3V1111fb33/3d322/+/M///PDa/7gD/5gd4c73GH3L//yL7vbO+zjNHjSk560e+QjH3mzv1k4PTV8+MMf3nD71re+9bT3/Bvf+MbdHe94x92HPvShw2te+tKX7r7oi75o9x//8R//jdW97eM1eOADH7j7kR/5kZv9zcLrLcM55flI8/7Lv/zLzYU9D5rr73e84x236tiOE+T+z7V9j3vcY7MUc/0F4TYNfuK3kEwHBS78nj68733vO/jQhz50BI8dB1CIEB57L9TyLd/yLYfXdH30nKdkwcnhuuuu29zcX//1X7+dhH3jjTcefrdwemroSIrgggsuOO093/s3fuM3Hlx44YWH1+TFqx17nrsFn45X8Lu/+7sHd77znQ/uda97HVx22WXbye+TXhdeP0MHy3224SMf+cjmqpqbIOjvf/iHf7jVxnWcIAGYyzTmnRvwBS94wcH973//g7/5m7/ZBOb555+/CcV9/PbdgtMDuDoZnfqu94TohDvd6U4b81q4vvmQS+GATsP+x3/8x4PnPve5Bw972MM2Jv45n/M5C6engJtuumk7Vfy7vuu7NmGIDk+153s/GS1PWr89w8nwGjzxiU88uPvd774Zetdff/3BT//0T295Ib//+7+/fb/weoyUjwX/c4hZg2/6pm/alJE2yKte9aotMXLBgnMVHv/4xx/+P4sx+v2ar/mazRvy4Ac/+FYd23GAchQyMmaO14LPHl5n/lb0WvJ5dJriHN0uuGU4p8Iuua+ycPYzsfv7Lne5y602ruMMWTxf93Vfd/De9753w2GhrY9+9KNHrln4PTNAi7dEp73vJ0lXPVC1xqLl04PChvGEaHfh9Jbh2c9+9pbUfO211x7c9a53PUKrp9rzvZ+Mliet317h5vB6MsjQCya9LrweE+Uj9+A3f/M3H/zRH/3REZdXf9/vfve7Vcd2XKHSzjTxtPJwe9555x3Bb27CckIWfk8fCgvEWCYei4+XywGPvcfwi7mDt7zlLRs9Y1ILbhn++Z//ecv5iHYXTk8O5e4mIK+++uqNvqLNCaez53v/67/+6yPKchUelYh/wzd8w+2STE+F15PBu9/97u190uvC6y3A7hyD3/u939uqBq688sotu/2SSy7ZfcmXfMmRTOwFNw8/8RM/sbvuuut273vf+3Zvf/vbdw95yEN2d77znbds7eAZz3jG7m53u9vuLW95y+4v/uIvdve73/2214Kj8G//9m+7v/qrv9pebZOXvOQl2//f//73b9//8i//8kaXr33ta3fXX3/9VqXx1V/91buPf/zjh/e4+OKLd/e5z312f/qnf7p729vetvvar/3a3ROe8ITbLapvCad995M/+ZNbBUa0+4d/+Ie7+973vhvOPvGJTxzeY+H0KDzzmc/cffEXf/G25z/4wQ8evv793//98JpT7fn/+q//2t3rXvfaXXTRRbt3v/vdu2uuuWb35V/+5bvLLrtsd3uFU+H1ve997+6FL3zhhs/oNT5wj3vcY/eABzzg8B4Lr7cM55zyEVx++eXbZjn//PO30tt3vvOdt/aQjg087nGP233FV3zFhruv+qqv2v5uo4CE47Oe9azdl37pl+6+4Au+YPeoRz1q21QLjsK11167Ccj9V+Wgym2f97zn7S688MJNWX7wgx+8e8973nPkHjfeeOOmbHzhF37hVrb4lKc8ZROyt1e4JZzG1BN+Cb1KQ+9+97vvnv70p3+a0bFwehROhs9ev/M7v3NGe/6f/umfdg972MN2n//5n78ZKxkxn/zkJ3e3VzgVXm+44YZN0bjgggu2/X/Pe95z95znPGf3sY997Mh9Fl5vHu7QP7fkGVmwYMGCBQsWLLjN5nwsWLBgwYIFC277sJSPBQsWLFiwYMFZhaV8LFiwYMGCBQvOKizlY8GCBQsWLFiwlI8FCxYsWLBgwW0XludjwYIFCxYsWHBWYSkfCxYsWLBgwYKzCkv5WLBgwYIFCxacVVjKx4IFCxYsWLDgrMJSPhYsWLBgwYIFZxWW8rFgwYIFCxYsODib8H8BNF0K2lzWUusAAAAASUVORK5CYII=", + "image/png": "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/x/72Me6u+66q19QDMITU1gc9NznPjckxwYbbNBdccUVfflbb721+cKipUQAh7iFN/bjcBr7sg2N/diOxr5sQ2M/tiO6w0Jrb+cN0OA2E55g+trXvtbvRUNrXoCynva0p/X/DzvssP72EITFwAMAAcTgCScQHvMGEPm93/u97hOf+ETP48Mf/nDPu8zCWLTCCit0G264Yf8ddYwKNpzGfmxHY1+2obEf29HYl21o7Md2BD8+r4DmuOOO6/+/7GUvW+b4F77whe4tb3lL//1Tn/pULygyNHg6CU8wfe5zn5st+5SnPKW/XfXOd76zBzpPf/rTu0MPPbT76Ec/2qZFI4000kgjjTTSkqP0LSePnvrUp3bHHnts/9Fo00037b71rW9lqh5ppJFGGmmkkUZafO9ywu2po48+OnybaiSZxn5sR2NftqGxH9vR2JdtaOzHhdGXg3YKHmmkkUYaaaSRRloeaMFmaEYaaaSRRhpppJGIRkAz0kgjjTTSSCMteBoBzUgjjTTSSCONtOBpBDQjjTTSSCONNNKCpwUJaPBI+GabbdY/Ir777rv/xqsUljrhlRQHHXRQv6My3nV16qmnLnMe68CPOuqobv311+83RMTb0a+99tplytx3333dIYcc0m8ihddYYMPEX/ziF91SIrwUddddd+03kcSu1q95zWv6na9Lwus3sCnkWmut1a222mr9/kv0Kg8i7JR94IEHdquuumrP5/3vf3/3+OOPd0uJsIfVjjvuOLsxGfag+va3vz17fuzHOvr4xz/ez3G8KJho7MsYfeQjH5l9gSZ9tt1229nzYz/G6fbbb+9+93d/t+8r+JQddtihu+iii+be50wWGJ100kmTlVdeefKv//qvk8svv3zytre9bbLmmmtO7rzzzvkWbbmhb33rW5M///M/n/zXf/0XnmCbnHLKKcuc//jHPz5ZY401JqeeeurkRz/60eRVr3rVZPPNN5888sgjs2X233//yU477TT5wQ9+MPm///u/yZZbbjl585vfPFlKtN9++02+8IUvTC677LLJpZdeOnnFK14x2WSTTSa/+MUvZsu84x3vmGy88caTM888c3LRRRdNXvSiF01e/OIXz55//PHHJ9tvv/1kn332mfzwhz/sx2bttdeeHHnkkZOlRF//+tcn//3f/z255pprJldfffXkz/7szyYrrbRS37egsR/zdMEFF0w222yzyY477jh5z3veM3t87MsYHX300ZPnPe95k5/97Gezn7vvvnv2/NiPMbrvvvsmm2666eQtb3nL5Pzzz5/ccMMNk9NPP31y3XXXzbnPWXCAZrfddpu8613vmv39xBNPTDbYYIPJMcccM69yLa/EAc2TTz45WW+99SZ/93d/N3vs/vvvn6yyyiqTr3zlK/3vK664or/uwgsvnC3z7W9/ezIzMzO5/fbbJ0uV7rrrrr5fzjnnnNl+g1M++eSTZ8tceeWVfZnzzjuv/w0jt8IKK0zuuOOO2TLHHXfcZPXVV588+uijk6VMz3zmMyef//znx36soIceemiy1VZbTc4444zJS1/60llAM/ZlDtDAgUo09mOcPvjBD0722msv9fxc+pwFdcsJb/K++OKL+3QVEV6zgN/nnXfevMq2UOjGG2/s359V9iHewYVbd9SH+I+U3y677DJbBuXR1+eff363lF+qBqKXqkEXH3vssWX6EilrvHit7EukX9ddd93ZMngdCF52d/nll3dLkZ544onupJNO6h5++OH+1tPYj3nCrRDc6ij7DDT2ZY5w2wO35rfYYov+dgduIYHGfozT17/+9d5XvOENb+hvu73gBS/o/vmf/3lefM6CAjT33HNPbwxLBQLhN70ocySbqJ+sPsR/ehM60Yorrtg78qXaz08++WS/TmHPPffstt9++/4Y+mLllVfuJ6LVl1Jf07mlRD/5yU/6tQjYIfQd73hHd8opp/Qvqh37MUcAg5dcckm/xovT2JdxgkM94YQTutNOO61f4wXH+5KXvKR76KGHxn5M0A033ND331ZbbdWdfvrp/Xsa//iP/7g78cQT59znpN7lNNJIS5UQEV922WXdueeeO9+iLFjaZpttuksvvbTPdP3nf/5n/1Lac845Z77FWlB06623du95z3u6M844o38oYqR6OuCAA2a/Y8E6AA7eM/jVr361X7g6UjzYQ2blb/7mb/rfyNDAVh5//PH9HJ9LWlAZmrXXXrt/WzdfaY7f66233rzJtZCI+snqQ/y/6667ljmPlftYhb4U+/nwww/v3xD/ne98p9too41mj6MvcBv0/vvvN/tS6ms6t5QIEe+WW27Z7bzzzn12Yaeddur+4R/+YezHBOFWCObmC1/4wj6CxQeg8DOf+Uz/HVHv2Jd1hGzM1ltv3V133XWjTiYITy4h01rSdtttN3v7bi59zgoLzSDCGJ555pnLoEP8xr34kXzafPPNewUp+xD3fHGfkvoQ/zGRYTyJzjrrrL6vEcUsFcKaaoAZ3BpB+9F3JUEXV1pppWX6Eo91YyKXfYlbLeVkRXSNRxO5EVhqBH169NFHx35M0N577933AzJd9EF0jPUf9H3syzrCI8LXX39976BHnYwTbsPz7SyuueaaPts15z5nsgAf28bq6BNOOKFfGf32t7+9f2y7XGm+1AlPQOAxQnwwxJ/85Cf77zfffPPsI3Tos6997WuTH//4x5NXv/rV4iN0L3jBC/rH8M4999z+iYql9tj2O9/5zv5Rw7PPPnuZRzt/+ctfLvNoJx7lPuuss/pHO/fYY4/+wx/t3HfffftHv0877bTJOuuss+Qe7fzQhz7UPx1244039jqH33iC4X/+53/682M/1lP5lBNo7MsY/cmf/Ek/t6GT3/ve9/rHr/HYNZ5mBI39GN8+YMUVV5x87GMfm1x77bWTL33pS5NVV1118u///u+zZebK5yw4QAP67Gc/2ysa9qPBY9x4bn2k/0ff+c53eiDDP4ceeujsY3R/8Rd/MVl33XV7cLj33nv3e4OUdO+99/bKtNpqq/WPIb71rW/tgdJSIqkP8cHeNESYkH/0R3/UP4KMSfza1762Bz0l3XTTTZMDDjhg8rSnPa03mDCkjz322GQp0R/8wR/0e1VgzsLoQ+cIzIDGfmwHaMa+jNEb3/jGyfrrr9/r5IYbbtj/LvdOGfsxTt/4xjd6cAd/su22207+6Z/+aZnzc+VzZvAnkV0aaaSRRhpppJFGWu5oQa2hGWmkkUYaaaSRRpJoBDQjjTTSSCONNNKCpxHQjDTSSCONNNJIC55GQDPSSCONNNJIIy14GgHNSCONNNJII4204GkENCONNNJII4000oKnEdCMNNJII4000kgLnkZAM9JII4000kgjLXgaAc1II4000kgjjbTgaQQ0I4000kgjjTTSgqcR0Iw00kgjjTTSSAueRkAz0kgjjTTSSCN1C53+P1Pi0KhaBcrkAAAAAElFTkSuQmCC", "text/plain": [ "<Figure size 640x480 with 1 Axes>" ] @@ -985,7 +987,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -1002,7 +1004,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -1016,9 +1018,9 @@ ".highlight .hll { background-color: #ffffcc }\n", ".highlight { background: #f8f8f8; }\n", ".highlight .c { color: #3D7B7B; font-style: italic } /* Comment */\n", - ".highlight .err { border: 1px solid #FF0000 } /* Error */\n", + ".highlight .err { border: 1px solid #F00 } /* Error */\n", ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n", - ".highlight .o { color: #666666 } /* Operator */\n", + ".highlight .o { color: #666 } /* Operator */\n", ".highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */\n", ".highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */\n", ".highlight .cp { color: #9C6500 } /* Comment.Preproc */\n", @@ -1035,34 +1037,34 @@ ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n", ".highlight .gs { font-weight: bold } /* Generic.Strong */\n", ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n", - ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n", + ".highlight .gt { color: #04D } /* Generic.Traceback */\n", ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n", ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n", ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n", ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n", ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n", ".highlight .kt { color: #B00040 } /* Keyword.Type */\n", - ".highlight .m { color: #666666 } /* Literal.Number */\n", + ".highlight .m { color: #666 } /* Literal.Number */\n", ".highlight .s { color: #BA2121 } /* Literal.String */\n", ".highlight .na { color: #687822 } /* Name.Attribute */\n", ".highlight .nb { color: #008000 } /* Name.Builtin */\n", - ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n", - ".highlight .no { color: #880000 } /* Name.Constant */\n", - ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n", + ".highlight .nc { color: #00F; font-weight: bold } /* Name.Class */\n", + ".highlight .no { color: #800 } /* Name.Constant */\n", + ".highlight .nd { color: #A2F } /* Name.Decorator */\n", ".highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */\n", ".highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */\n", - ".highlight .nf { color: #0000FF } /* Name.Function */\n", + ".highlight .nf { color: #00F } /* Name.Function */\n", ".highlight .nl { color: #767600 } /* Name.Label */\n", - ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n", + ".highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */\n", ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n", ".highlight .nv { color: #19177C } /* Name.Variable */\n", - ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n", - ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n", - ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n", - ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n", - ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n", - ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n", - ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n", + ".highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */\n", + ".highlight .w { color: #BBB } /* Text.Whitespace */\n", + ".highlight .mb { color: #666 } /* Literal.Number.Bin */\n", + ".highlight .mf { color: #666 } /* Literal.Number.Float */\n", + ".highlight .mh { color: #666 } /* Literal.Number.Hex */\n", + ".highlight .mi { color: #666 } /* Literal.Number.Integer */\n", + ".highlight .mo { color: #666 } /* Literal.Number.Oct */\n", ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n", ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n", ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n", @@ -1077,12 +1079,12 @@ ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n", ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n", ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n", - ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n", + ".highlight .fm { color: #00F } /* Name.Function.Magic */\n", ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n", ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n", ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n", ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", - ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style>" + ".highlight .il { color: #666 } /* Literal.Number.Integer.Long */</style>" ], "text/plain": [ "<IPython.core.display.HTML object>" @@ -1094,32 +1096,28 @@ { "data": { "text/html": [ - "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"p\">)</span>\n", + "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"p\">)</span>\n", "<span class=\"p\">{</span>\n", "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", - "<span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">])</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]);</span>\n", + "<span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">])</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]);</span>\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"p\">}</span>\n", "</pre></div>\n" ], "text/plain": [ - "FUNC_PREFIX void kernel (const int64_t _size_dst_0, const int64_t _size_dst_1, const int64_t _stride_dst_0, const int64_t _stride_dst_1, const int64_t _stride_img_0, const int64_t _stride_img_1, const int64_t _stride_img_2, double * const dst_data, double * const img_data, const double w_2)\n", + "FUNC_PREFIX void kernel (double * RESTRICT const _data_dst, double * RESTRICT const _data_img, const int64_t _size_dst_0, const int64_t _size_dst_1, const int64_t _stride_dst_0, const int64_t _stride_dst_1, const int64_t _stride_img_0, const int64_t _stride_img_1, const int64_t _stride_img_2, const double w_2)\n", "{\n", " for(int64_t ctr_0 = 1LL; ctr_0 < _size_dst_0 - 1LL; ctr_0 += 1LL)\n", " {\n", " for(int64_t ctr_1 = 1LL; ctr_1 < _size_dst_1 - 1LL; ctr_1 += 1LL)\n", " {\n", - " dst_data[ctr_0 * _stride_dst_0 + ctr_1 * _stride_dst_1] = (0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * img_data[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * img_data[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]) * (0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * img_data[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * img_data[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]);\n", + " _data_dst[ctr_0 * _stride_dst_0 + ctr_1 * _stride_dst_1] = (0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]) * (0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]);\n", " }\n", - "\n", " }\n", - "\n", "}" ] }, @@ -1140,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -1154,9 +1152,9 @@ ".highlight .hll { background-color: #ffffcc }\n", ".highlight { background: #f8f8f8; }\n", ".highlight .c { color: #3D7B7B; font-style: italic } /* Comment */\n", - ".highlight .err { border: 1px solid #FF0000 } /* Error */\n", + ".highlight .err { border: 1px solid #F00 } /* Error */\n", ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n", - ".highlight .o { color: #666666 } /* Operator */\n", + ".highlight .o { color: #666 } /* Operator */\n", ".highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */\n", ".highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */\n", ".highlight .cp { color: #9C6500 } /* Comment.Preproc */\n", @@ -1173,34 +1171,34 @@ ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n", ".highlight .gs { font-weight: bold } /* Generic.Strong */\n", ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n", - ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n", + ".highlight .gt { color: #04D } /* Generic.Traceback */\n", ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n", ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n", ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n", ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n", ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n", ".highlight .kt { color: #B00040 } /* Keyword.Type */\n", - ".highlight .m { color: #666666 } /* Literal.Number */\n", + ".highlight .m { color: #666 } /* Literal.Number */\n", ".highlight .s { color: #BA2121 } /* Literal.String */\n", ".highlight .na { color: #687822 } /* Name.Attribute */\n", ".highlight .nb { color: #008000 } /* Name.Builtin */\n", - ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n", - ".highlight .no { color: #880000 } /* Name.Constant */\n", - ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n", + ".highlight .nc { color: #00F; font-weight: bold } /* Name.Class */\n", + ".highlight .no { color: #800 } /* Name.Constant */\n", + ".highlight .nd { color: #A2F } /* Name.Decorator */\n", ".highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */\n", ".highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */\n", - ".highlight .nf { color: #0000FF } /* Name.Function */\n", + ".highlight .nf { color: #00F } /* Name.Function */\n", ".highlight .nl { color: #767600 } /* Name.Label */\n", - ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n", + ".highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */\n", ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n", ".highlight .nv { color: #19177C } /* Name.Variable */\n", - ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n", - ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n", - ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n", - ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n", - ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n", - ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n", - ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n", + ".highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */\n", + ".highlight .w { color: #BBB } /* Text.Whitespace */\n", + ".highlight .mb { color: #666 } /* Literal.Number.Bin */\n", + ".highlight .mf { color: #666 } /* Literal.Number.Float */\n", + ".highlight .mh { color: #666 } /* Literal.Number.Hex */\n", + ".highlight .mi { color: #666 } /* Literal.Number.Integer */\n", + ".highlight .mo { color: #666 } /* Literal.Number.Oct */\n", ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n", ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n", ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n", @@ -1215,12 +1213,12 @@ ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n", ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n", ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n", - ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n", + ".highlight .fm { color: #00F } /* Name.Function.Magic */\n", ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n", ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n", ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n", ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", - ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style>" + ".highlight .il { color: #666 } /* Literal.Number.Integer.Long */</style>" ], "text/plain": [ "<IPython.core.display.HTML object>" @@ -1232,34 +1230,30 @@ { "data": { "text/html": [ - "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"p\">)</span>\n", + "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"p\">)</span>\n", "<span class=\"p\">{</span>\n", "<span class=\"w\"> </span><span class=\"cp\">#pragma omp parallel for schedule(static) num_threads(2)</span>\n", "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"n\">_size_dst_0</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"n\">_size_dst_1</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", - "<span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">])</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">img_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]);</span>\n", + "<span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_dst_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">])</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"mf\">0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-0.5</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">w_2</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_img</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_stride_img_2</span><span class=\"p\">]);</span>\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"p\">}</span>\n", "</pre></div>\n" ], "text/plain": [ - "FUNC_PREFIX void kernel (const int64_t _size_dst_0, const int64_t _size_dst_1, const int64_t _stride_dst_0, const int64_t _stride_dst_1, const int64_t _stride_img_0, const int64_t _stride_img_1, const int64_t _stride_img_2, double * const dst_data, double * const img_data, const double w_2)\n", + "FUNC_PREFIX void kernel (double * RESTRICT const _data_dst, double * RESTRICT const _data_img, const int64_t _size_dst_0, const int64_t _size_dst_1, const int64_t _stride_dst_0, const int64_t _stride_dst_1, const int64_t _stride_img_0, const int64_t _stride_img_1, const int64_t _stride_img_2, const double w_2)\n", "{\n", " #pragma omp parallel for schedule(static) num_threads(2)\n", " for(int64_t ctr_0 = 1LL; ctr_0 < _size_dst_0 - 1LL; ctr_0 += 1LL)\n", " {\n", " for(int64_t ctr_1 = 1LL; ctr_1 < _size_dst_1 - 1LL; ctr_1 += 1LL)\n", " {\n", - " dst_data[ctr_0 * _stride_dst_0 + ctr_1 * _stride_dst_1] = (0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * img_data[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * img_data[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]) * (0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] - 0.5 * img_data[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * img_data[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * img_data[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]);\n", + " _data_dst[ctr_0 * _stride_dst_0 + ctr_1 * _stride_dst_1] = (0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]) * (0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + 1LL) * _stride_img_1 + 2LL * _stride_img_2] + -0.5 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + (ctr_1 + -1LL) * _stride_img_1 + 2LL * _stride_img_2] + w_2 * _data_img[(ctr_0 + 1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2] - w_2 * _data_img[(ctr_0 + -1LL) * _stride_img_0 + ctr_1 * _stride_img_1 + 2LL * _stride_img_2]);\n", " }\n", - "\n", " }\n", - "\n", "}" ] }, @@ -1270,8 +1264,8 @@ "source": [ "ast = ps.create_kernel(\n", " update_rule,\n", - " cpu_optim = ps.CpuOptimConfig(\n", - " openmp=ps.OpenMpConfig(num_threads=2))\n", + " cpu = ps.CpuOptions(\n", + " openmp=ps.OpenMpOptions(enable=True, num_threads=2))\n", " )\n", "\n", "ps.show_code(ast)" @@ -1289,7 +1283,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -1303,9 +1297,9 @@ ".highlight .hll { background-color: #ffffcc }\n", ".highlight { background: #f8f8f8; }\n", ".highlight .c { color: #3D7B7B; font-style: italic } /* Comment */\n", - ".highlight .err { border: 1px solid #FF0000 } /* Error */\n", + ".highlight .err { border: 1px solid #F00 } /* Error */\n", ".highlight .k { color: #008000; font-weight: bold } /* Keyword */\n", - ".highlight .o { color: #666666 } /* Operator */\n", + ".highlight .o { color: #666 } /* Operator */\n", ".highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */\n", ".highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */\n", ".highlight .cp { color: #9C6500 } /* Comment.Preproc */\n", @@ -1322,34 +1316,34 @@ ".highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n", ".highlight .gs { font-weight: bold } /* Generic.Strong */\n", ".highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n", - ".highlight .gt { color: #0044DD } /* Generic.Traceback */\n", + ".highlight .gt { color: #04D } /* Generic.Traceback */\n", ".highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n", ".highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n", ".highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n", ".highlight .kp { color: #008000 } /* Keyword.Pseudo */\n", ".highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n", ".highlight .kt { color: #B00040 } /* Keyword.Type */\n", - ".highlight .m { color: #666666 } /* Literal.Number */\n", + ".highlight .m { color: #666 } /* Literal.Number */\n", ".highlight .s { color: #BA2121 } /* Literal.String */\n", ".highlight .na { color: #687822 } /* Name.Attribute */\n", ".highlight .nb { color: #008000 } /* Name.Builtin */\n", - ".highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n", - ".highlight .no { color: #880000 } /* Name.Constant */\n", - ".highlight .nd { color: #AA22FF } /* Name.Decorator */\n", + ".highlight .nc { color: #00F; font-weight: bold } /* Name.Class */\n", + ".highlight .no { color: #800 } /* Name.Constant */\n", + ".highlight .nd { color: #A2F } /* Name.Decorator */\n", ".highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */\n", ".highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */\n", - ".highlight .nf { color: #0000FF } /* Name.Function */\n", + ".highlight .nf { color: #00F } /* Name.Function */\n", ".highlight .nl { color: #767600 } /* Name.Label */\n", - ".highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n", + ".highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */\n", ".highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */\n", ".highlight .nv { color: #19177C } /* Name.Variable */\n", - ".highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n", - ".highlight .w { color: #bbbbbb } /* Text.Whitespace */\n", - ".highlight .mb { color: #666666 } /* Literal.Number.Bin */\n", - ".highlight .mf { color: #666666 } /* Literal.Number.Float */\n", - ".highlight .mh { color: #666666 } /* Literal.Number.Hex */\n", - ".highlight .mi { color: #666666 } /* Literal.Number.Integer */\n", - ".highlight .mo { color: #666666 } /* Literal.Number.Oct */\n", + ".highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */\n", + ".highlight .w { color: #BBB } /* Text.Whitespace */\n", + ".highlight .mb { color: #666 } /* Literal.Number.Bin */\n", + ".highlight .mf { color: #666 } /* Literal.Number.Float */\n", + ".highlight .mh { color: #666 } /* Literal.Number.Hex */\n", + ".highlight .mi { color: #666 } /* Literal.Number.Integer */\n", + ".highlight .mo { color: #666 } /* Literal.Number.Oct */\n", ".highlight .sa { color: #BA2121 } /* Literal.String.Affix */\n", ".highlight .sb { color: #BA2121 } /* Literal.String.Backtick */\n", ".highlight .sc { color: #BA2121 } /* Literal.String.Char */\n", @@ -1364,12 +1358,12 @@ ".highlight .s1 { color: #BA2121 } /* Literal.String.Single */\n", ".highlight .ss { color: #19177C } /* Literal.String.Symbol */\n", ".highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */\n", - ".highlight .fm { color: #0000FF } /* Name.Function.Magic */\n", + ".highlight .fm { color: #00F } /* Name.Function.Magic */\n", ".highlight .vc { color: #19177C } /* Name.Variable.Class */\n", ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n", ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n", ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", - ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */</style>" + ".highlight .il { color: #666 } /* Literal.Number.Integer.Long */</style>" ], "text/plain": [ "<IPython.core.display.HTML object>" @@ -1381,32 +1375,28 @@ { "data": { "text/html": [ - "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">)</span>\n", + "<div class=\"highlight\"><pre><span></span><span class=\"n\">FUNC_PREFIX</span><span class=\"w\"> </span><span class=\"kt\">void</span><span class=\"w\"> </span><span class=\"n\">kernel</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"kt\">double</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">RESTRICT</span><span class=\"w\"> </span><span class=\"k\">const</span><span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">)</span>\n", "<span class=\"p\">{</span>\n", - "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"mf\">81L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", + "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"mf\">202L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", - "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"mf\">289L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", + "<span class=\"w\"> </span><span class=\"k\">for</span><span class=\"p\">(</span><span class=\"kt\">int64_t</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\"><</span><span class=\"w\"> </span><span class=\"mf\">600L</span><span class=\"n\">L</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+=</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span>\n", "<span class=\"w\"> </span><span class=\"p\">{</span>\n", - "<span class=\"w\"> </span><span class=\"n\">dst_data</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">290L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">-1.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"mf\">2.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">I_data</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">1160L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">];</span>\n", + "<span class=\"w\"> </span><span class=\"n\">_data_dst</span><span class=\"p\">[</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">601L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">-1.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">-</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">-2.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">2.0</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">]</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"n\">_data_I</span><span class=\"p\">[(</span><span class=\"n\">ctr_0</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">2404L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"n\">ctr_1</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mi\">-1LL</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">*</span><span class=\"w\"> </span><span class=\"mf\">4L</span><span class=\"n\">L</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1L</span><span class=\"n\">L</span><span class=\"p\">];</span>\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"w\"> </span><span class=\"p\">}</span>\n", - "\n", "<span class=\"p\">}</span>\n", "</pre></div>\n" ], "text/plain": [ - "FUNC_PREFIX void kernel (double * const I_data, double * const dst_data)\n", + "FUNC_PREFIX void kernel (double * RESTRICT const _data_I, double * RESTRICT const _data_dst)\n", "{\n", - " for(int64_t ctr_0 = 1LL; ctr_0 < 81LL; ctr_0 += 1LL)\n", + " for(int64_t ctr_0 = 1LL; ctr_0 < 202LL; ctr_0 += 1LL)\n", " {\n", - " for(int64_t ctr_1 = 1LL; ctr_1 < 289LL; ctr_1 += 1LL)\n", + " for(int64_t ctr_1 = 1LL; ctr_1 < 600LL; ctr_1 += 1LL)\n", " {\n", - " dst_data[ctr_0 * 290LL + ctr_1] = -1.0 * I_data[(ctr_0 + 1LL) * 1160LL + (ctr_1 + 1LL) * 4LL + 1LL] - I_data[(ctr_0 + -1LL) * 1160LL + (ctr_1 + 1LL) * 4LL + 1LL] - I_data[(ctr_0 + -1LL) * 1160LL + (ctr_1 + -1LL) * 4LL + 1LL] - 2.0 * I_data[(ctr_0 + -1LL) * 1160LL + ctr_1 * 4LL + 1LL] + 2.0 * I_data[(ctr_0 + 1LL) * 1160LL + ctr_1 * 4LL + 1LL] + I_data[(ctr_0 + 1LL) * 1160LL + (ctr_1 + -1LL) * 4LL + 1LL];\n", + " _data_dst[ctr_0 * 601LL + ctr_1] = -1.0 * _data_I[(ctr_0 + 1LL) * 2404LL + (ctr_1 + 1LL) * 4LL + 1LL] - _data_I[(ctr_0 + -1LL) * 2404LL + (ctr_1 + 1LL) * 4LL + 1LL] - _data_I[(ctr_0 + -1LL) * 2404LL + (ctr_1 + -1LL) * 4LL + 1LL] + -2.0 * _data_I[(ctr_0 + -1LL) * 2404LL + ctr_1 * 4LL + 1LL] + 2.0 * _data_I[(ctr_0 + 1LL) * 2404LL + ctr_1 * 4LL + 1LL] + _data_I[(ctr_0 + 1LL) * 2404LL + (ctr_1 + -1LL) * 4LL + 1LL];\n", " }\n", - "\n", " }\n", - "\n", "}" ] }, @@ -1443,7 +1433,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -1486,7 +1476,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.14" } }, "nbformat": 4, diff --git a/src/pystencils/__init__.py b/src/pystencils/__init__.py index 2bb4aac3d..8c59f7846 100644 --- a/src/pystencils/__init__.py +++ b/src/pystencils/__init__.py @@ -4,7 +4,7 @@ from .codegen import ( Target, CreateKernelConfig, CpuOptions, - VectorizationConfig, + VectorizationOptions, OpenMpOptions, GpuOptions, AUTO @@ -51,7 +51,7 @@ __all__ = [ "make_slice", "CreateKernelConfig", "CpuOptions", - "VectorizationConfig", + "VectorizationOptions", "GpuOptions", "OpenMpOptions", "AUTO", diff --git a/src/pystencils/backend/kernelcreation/iteration_space.py b/src/pystencils/backend/kernelcreation/iteration_space.py index 031a0d843..f7425c06b 100644 --- a/src/pystencils/backend/kernelcreation/iteration_space.py +++ b/src/pystencils/backend/kernelcreation/iteration_space.py @@ -17,7 +17,6 @@ from ...types import PsStructType from ..exceptions import PsInputError, KernelConstraintsError if TYPE_CHECKING: - from ...codegen.config import _AUTO_TYPE from .context import KernelCreationContext @@ -62,6 +61,7 @@ class FullIterationSpace(IterationSpace): @dataclass class Dimension: """One dimension of a dense iteration space""" + start: PsExpression stop: PsExpression step: PsExpression @@ -196,7 +196,7 @@ class FullIterationSpace(IterationSpace): def dimensions(self): """The dimensions of this iteration space""" return self._dimensions - + @property def counters(self) -> tuple[PsSymbol, ...]: return tuple(dim.counter for dim in self._dimensions) @@ -220,7 +220,7 @@ class FullIterationSpace(IterationSpace): def archetype_field(self) -> Field | None: """Field whose shape and memory layout act as archetypes for this iteration space's dimensions.""" return self._archetype_field - + @property def loop_order(self) -> tuple[int, ...]: """Return the loop order of this iteration space, ordered from slowest to fastest coordinate.""" @@ -242,7 +242,7 @@ class FullIterationSpace(IterationSpace): self, dimension: int | FullIterationSpace.Dimension | None = None ) -> PsExpression: """Construct an expression representing the actual number of unique points inside the iteration space. - + Args: dimension: If an integer or a `Dimension` object is given, the number of iterations in that dimension is computed. If `None`, the total number of iterations inside the entire space @@ -417,14 +417,59 @@ def create_sparse_iteration_space( def create_full_iteration_space( ctx: KernelCreationContext, assignments: AssignmentCollection, - ghost_layers: None | _AUTO_TYPE | int | Sequence[int | tuple[int, int]] = None, + ghost_layers: None | int | Sequence[int | tuple[int, int]] = None, iteration_slice: None | int | slice | tuple[int | slice, ...] = None, + infer_ghost_layers: bool = False, ) -> IterationSpace: + """Create a dense iteration space from a sequence of assignments and iteration slice information. + + This function finds all accesses to fields in the given assignment collection, + analyzes the set of fields involved, + and determines the iteration space bounds from these. + This requires that either all fields are of the same, fixed, shape, or all of them are + variable-shaped. + Also, all fields need to have the same memory layout of their spatial dimensions. + + Args: + ctx: The kernel creation context + assignments: Collection of assignments the iteration space should be inferred from + ghost_layers: If set, strip off that many ghost layers from all sides of the iteration cuboid + iteration_slice: If set, constrain iteration to the given slice. + For details on the parsing of slices, see `AstFactory.parse_slice`. + infer_ghost_layers: If `True`, infer the number of ghost layers from the stencil ranges + used in the kernel. + + Returns: + IterationSpace: The constructed iteration space. + + Raises: + KernelConstraintsError: If field shape or memory layout conflicts are detected + ValueError: If the iteration slice could not be parsed + + .. attention:: + The ``ghost_layers`` and ``iteration_slice`` arguments are mutually exclusive. + Also, if ``infer_ghost_layers=True``, none of them may be set. + """ + assert not ctx.fields.index_fields - if (ghost_layers is not None) and (iteration_slice is not None): + if not ( + (ghost_layers is not None) + or (iteration_slice is not None) + or infer_ghost_layers + ): raise ValueError( - "At most one of `ghost_layers` and `iteration_slice` may be specified." + "One argument of `ghost_layers`, `iteration_slice`, and `infer_ghost_layers` must be set." + ) + + if ( + int(ghost_layers is not None) + + int(iteration_slice is not None) + + int(infer_ghost_layers) + > 1 + ): + raise ValueError( + "At most one of `ghost_layers`, `iteration_slice`, and `infer_ghost_layers` may be set." ) # Collect all relative accesses into domain fields @@ -457,9 +502,7 @@ def create_full_iteration_space( # Otherwise, if an iteration slice was specified, use that # Otherwise, use the inferred ghost layers - from ...codegen.config import AUTO, _AUTO_TYPE - - if ghost_layers is AUTO: + if infer_ghost_layers: if len(domain_field_accesses) > 0: inferred_gls = max( [fa.required_ghost_layers for fa in domain_field_accesses] @@ -472,7 +515,6 @@ def create_full_iteration_space( ctx, inferred_gls, archetype_field ) elif ghost_layers is not None: - assert not isinstance(ghost_layers, _AUTO_TYPE) ctx.metadata["ghost_layers"] = ghost_layers return FullIterationSpace.create_with_ghost_layers( ctx, ghost_layers, archetype_field diff --git a/src/pystencils/backend/platforms/cuda.py b/src/pystencils/backend/platforms/cuda.py index 31686cb18..2559ac6d2 100644 --- a/src/pystencils/backend/platforms/cuda.py +++ b/src/pystencils/backend/platforms/cuda.py @@ -30,7 +30,7 @@ from ..literals import PsLiteral from ..functions import PsMathFunction, MathFunctions, CFunction if TYPE_CHECKING: - from ...codegen import GpuOptions, GpuThreadsRange + from ...codegen import GpuThreadsRange int32 = PsSignedIntegerType(width=32, const=False) @@ -52,13 +52,15 @@ class CudaPlatform(GenericGpu): """Platform for CUDA-based GPUs.""" def __init__( - self, ctx: KernelCreationContext, indexing_cfg: GpuOptions | None = None + self, ctx: KernelCreationContext, + omit_range_check: bool = False, + manual_launch_grid: bool = False, ) -> None: super().__init__(ctx) - from ...codegen.config import GpuOptions + self._omit_range_check = omit_range_check + self._manual_launch_grid = manual_launch_grid - self._cfg = indexing_cfg if indexing_cfg is not None else GpuOptions() self._typify = Typifier(ctx) @property @@ -141,7 +143,7 @@ class CudaPlatform(GenericGpu): ) -> tuple[PsBlock, GpuThreadsRange | None]: dimensions = ispace.dimensions_in_loop_order() - if not self._cfg.manual_launch_grid: + if not self._manual_launch_grid: try: threads_range = self.threads_from_ispace(ispace) except MaterializationError as e: @@ -170,7 +172,7 @@ class CudaPlatform(GenericGpu): ) ) ) - if not self._cfg.omit_range_check: + if not self._omit_range_check: conds.append(PsLt(ctr, dim.stop)) indexing_decls = indexing_decls[::-1] @@ -213,7 +215,7 @@ class CudaPlatform(GenericGpu): ] body.statements = mappings + body.statements - if not self._cfg.omit_range_check: + if not self._omit_range_check: stop = PsExpression.make(ispace.index_list.shape[0]) condition = PsLt(sparse_ctr, stop) ast = PsBlock([sparse_idx_decl, PsConditional(condition, body)]) diff --git a/src/pystencils/backend/platforms/sycl.py b/src/pystencils/backend/platforms/sycl.py index b5ba7b6c4..594c87b14 100644 --- a/src/pystencils/backend/platforms/sycl.py +++ b/src/pystencils/backend/platforms/sycl.py @@ -19,7 +19,7 @@ from ..ast.expressions import ( PsLe, PsTernary, PsLookup, - PsBufferAcc + PsBufferAcc, ) from ..extensions.cpp import CppMethodCall @@ -30,19 +30,21 @@ from ..exceptions import MaterializationError from ...types import PsCustomType, PsIeeeFloatType, constify, PsIntegerType if TYPE_CHECKING: - from ...codegen import GpuOptions, GpuThreadsRange + from ...codegen import GpuThreadsRange class SyclPlatform(GenericGpu): def __init__( - self, ctx: KernelCreationContext, indexing_cfg: GpuOptions | None = None + self, + ctx: KernelCreationContext, + omit_range_check: bool = False, + automatic_block_size: bool = False ): super().__init__(ctx) - from ...codegen.config import GpuOptions - - self._cfg = indexing_cfg if indexing_cfg is not None else GpuOptions() + self._omit_range_check = omit_range_check + self._automatic_block_size = automatic_block_size @property def required_headers(self) -> set[str]: @@ -138,7 +140,7 @@ class SyclPlatform(GenericGpu): indexing_decls.append( PsDeclaration(ctr, dim.start + work_item_idx * dim.step) ) - if not self._cfg.omit_range_check: + if not self._omit_range_check: conds.append(PsLt(ctr, dim.stop)) if conds: @@ -156,7 +158,7 @@ class SyclPlatform(GenericGpu): self, body: PsBlock, ispace: SparseIterationSpace ) -> tuple[PsBlock, GpuThreadsRange]: factory = AstFactory(self._ctx) - + id_type = PsCustomType("sycl::id< 1 >", const=True) id_symbol = PsExpression.make(self._ctx.get_symbol("id", id_type)) @@ -184,7 +186,7 @@ class SyclPlatform(GenericGpu): ] body.statements = mappings + body.statements - if not self._cfg.omit_range_check: + if not self._omit_range_check: stop = PsExpression.make(ispace.index_list.shape[0]) condition = PsLt(sparse_ctr, stop) ast = PsBlock([sparse_idx_decl, PsConditional(condition, body)]) @@ -195,7 +197,7 @@ class SyclPlatform(GenericGpu): return ast, self.threads_from_ispace(ispace) def _item_type(self, rank: int): - if not self._cfg.sycl_automatic_block_size: + if not self._automatic_block_size: return PsCustomType(f"sycl::nd_item< {rank} >", const=True) else: return PsCustomType(f"sycl::item< {rank} >", const=True) @@ -207,7 +209,7 @@ class SyclPlatform(GenericGpu): item_type = self._item_type(rank) item = PsExpression.make(self._ctx.get_symbol("sycl_item", item_type)) - if not self._cfg.sycl_automatic_block_size: + if not self._automatic_block_size: rhs = CppMethodCall(item, "get_global_id", self._id_type(rank)) else: rhs = CppMethodCall(item, "get_id", self._id_type(rank)) diff --git a/src/pystencils/backend/transformations/add_pragmas.py b/src/pystencils/backend/transformations/add_pragmas.py index b033e4d58..f44b89c72 100644 --- a/src/pystencils/backend/transformations/add_pragmas.py +++ b/src/pystencils/backend/transformations/add_pragmas.py @@ -1,6 +1,5 @@ from __future__ import annotations from dataclasses import dataclass -from typing import TYPE_CHECKING from typing import Sequence from collections import defaultdict @@ -10,8 +9,6 @@ from ..ast import PsAstNode from ..ast.structural import PsBlock, PsLoop, PsPragma from ..ast.expressions import PsExpression -if TYPE_CHECKING: - from ...codegen.config import OpenMpOptions __all__ = ["InsertPragmasAtLoops", "LoopPragma", "AddOpenMP"] @@ -105,19 +102,37 @@ class AddOpenMP: `OpenMpConfig` configuration. """ - def __init__(self, ctx: KernelCreationContext, omp_params: OpenMpOptions) -> None: + def __init__( + self, + ctx: KernelCreationContext, + nesting_depth: int = 0, + num_threads: int | None = None, + schedule: str | None = None, + collapse: int | None = None, + omit_parallel: bool = False, + ) -> None: pragma_text = "omp" - pragma_text += " parallel" if not omp_params.omit_parallel_construct else "" - pragma_text += f" for schedule({omp_params.schedule})" - if omp_params.num_threads is not None: - pragma_text += f" num_threads({str(omp_params.num_threads)})" + if not omit_parallel: + pragma_text += " parallel" + + pragma_text += " for" + + if schedule is not None: + pragma_text += f" schedule({schedule})" + + if num_threads is not None: + pragma_text += f" num_threads({str(num_threads)})" - if omp_params.collapse > 0: - pragma_text += f" collapse({str(omp_params.collapse)})" + if collapse is not None: + if collapse <= 0: + raise ValueError( + f"Invalid value for OpenMP `collapse` clause: {collapse}" + ) + pragma_text += f" collapse({str(collapse)})" self._insert_pragmas = InsertPragmasAtLoops( - ctx, [LoopPragma(pragma_text, omp_params.nesting_depth)] + ctx, [LoopPragma(pragma_text, nesting_depth)] ) def __call__(self, node: PsAstNode) -> PsAstNode: diff --git a/src/pystencils/codegen/__init__.py b/src/pystencils/codegen/__init__.py index da33f9ee2..3780527c6 100644 --- a/src/pystencils/codegen/__init__.py +++ b/src/pystencils/codegen/__init__.py @@ -2,7 +2,7 @@ from .target import Target from .config import ( CreateKernelConfig, CpuOptions, - VectorizationConfig, + VectorizationOptions, OpenMpOptions, GpuOptions, AUTO, @@ -15,7 +15,7 @@ __all__ = [ "Target", "CreateKernelConfig", "CpuOptions", - "VectorizationConfig", + "VectorizationOptions", "OpenMpOptions", "GpuOptions", "AUTO", diff --git a/src/pystencils/codegen/config.py b/src/pystencils/codegen/config.py index 4e17ccc48..9abf51222 100644 --- a/src/pystencils/codegen/config.py +++ b/src/pystencils/codegen/config.py @@ -4,10 +4,9 @@ from typing import TYPE_CHECKING from warnings import warn from abc import ABC from collections.abc import Collection -from copy import copy from typing import Sequence, Generic, TypeVar, Callable, Any, cast -from dataclasses import dataclass, InitVar, replace, fields +from dataclasses import dataclass, InitVar, fields from .target import Target from ..field import Field, FieldType @@ -15,7 +14,6 @@ from ..field import Field, FieldType from ..types import ( PsIntegerType, UserTypeSpec, - PsIeeeFloatType, PsScalarType, create_type, ) @@ -26,10 +24,6 @@ if TYPE_CHECKING: from ..jit import JitBase -class PsOptionsError(Exception): - """Indicates an option clash in the `CreateKernelConfig`.""" - - Option_T = TypeVar("Option_T") Arg_T = TypeVar("Arg_T") @@ -51,6 +45,9 @@ class Option(Generic[Option_T, Arg_T]): Through the validator, options may also be set from arguments of a different type (`Arg_T`) than their value type (`Option_T`). If `Arg_T` is different from `Option_T`, the validator must perform the conversion from the former to the latter. + + .. note:: + `Arg_T` must always be a supertype of `Option_T`. """ def __init__( @@ -102,10 +99,32 @@ class Option(Generic[Option_T, Arg_T]): delattr(obj, self._lookup) -class BasicOption(Option[Option_T, Option_T]): ... +class BasicOption(Option[Option_T, Option_T]): ... # noqa: E701 class ConfigBase(ABC): + """Base class for configuration categories. + + This class implements query and retrieval mechanism for configuration options, + as well as deepcopy functionality for categories. + + Subclasses of `ConfigBase` must be `dataclasses`, + and all of their instance fields must have one of two descriptors types: + - Either `Option`, for scalar options; + - Or `Category` for option subcategories. + + `Option` fields must be assigned immutable values, but are otherwise unconstrained. + `Category` subobjects must be subclasses of `ConfigBase`. + + **Retrieval** Options set to `None` are considered *unset*, i.e. the user has not provided a value. + Through the `Option` descriptor, these options can still have a default value. + To retrieve either the user-set value if one exists, or the default value otherwise, use `get_option`. + + **Deep-Copy** When a configuration object is copied, all of its subcategories must be copied along with it, + such that changes in the original do no affect the copy, and vice versa. + Such a deep copy is performed by the `copy <ConfigBase.copy>` method. + """ + def get_option(self, name: str) -> Any: """Get the value set for the specified option, or the option's default value if none has been set.""" descr: Option = type(self).__dict__[name] @@ -125,6 +144,31 @@ class ConfigBase(ABC): if new_val is not None: setattr(self, f.name, new_val) + def copy(self): + """Perform a semi-deep copy of this configuration object. + + This will recursively copy any config subobjects + (categories, i.e. subclasses of `ConfigBase` wrapped in the `Category` descriptor) + nested in this configuration object. Any other fields will be copied by reference. + """ + + # IMPLEMENTATION NOTES + # + # We do not need to call `copy` on any subcategories here, since the `Category` + # descriptor already calls `copy` in its `__set__` method, + # which is invoked during the constructor call in the `return` statement. + # Calling `copy` here would result in copying category objects twice. + # + # We cannot use the standard library `copy.copy` here, since it merely duplicates + # the instance dictionary and does not call the constructor. + + config_fields = fields(self) # type: ignore + kwargs = dict() + for field in config_fields: + val = getattr(self, field.name) + kwargs[field.name] = val + return type(self)(**kwargs) + Category_T = TypeVar("Category_T", bound=ConfigBase) @@ -151,7 +195,7 @@ class Category(Generic[Category_T]): return cast(Category_T, getattr(obj, self._lookup, None)) def __set__(self, obj, cat: Category_T): - setattr(obj, self._lookup, copy(cat)) + setattr(obj, self._lookup, cat.copy()) class _AUTO_TYPE: ... # noqa: E701 @@ -191,15 +235,9 @@ class OpenMpOptions(ConfigBase): Use this option only if you intend to wrap the kernel into an external ``#pragma omp parallel`` region. """ - def __post_init__(self): - if self.omit_parallel_construct and self.num_threads is not None: - raise PsOptionsError( - "Cannot specify `num_threads` if `omit_parallel_construct` is set." - ) - @dataclass -class VectorizationConfig(ConfigBase): +class VectorizationOptions(ConfigBase): """Configuration for the auto-vectorizer. If any flag in this configuration is set to a value not supported by the CPU specified @@ -277,7 +315,7 @@ class CpuOptions(ConfigBase): """Options governing OpenMP-instrumentation. """ - vectorize: Category[OpenMpOptions] = Category(OpenMpOptions()) + vectorize: Category[VectorizationOptions] = Category(VectorizationOptions()) """Options governing intrinsic vectorization. """ @@ -294,14 +332,6 @@ class CpuOptions(ConfigBase): to produce cacheline zeroing instructions where possible. """ - def get_vectorization_config(self) -> VectorizationConfig | None: - if self.vectorize is True: - return VectorizationConfig() - elif isinstance(self.vectorize, VectorizationConfig): - return self.vectorize - else: - return None - @dataclass class GpuOptions(ConfigBase): @@ -326,8 +356,13 @@ class GpuOptions(ConfigBase): The launch grid will then have to be specified manually at runtime. """ - sycl_automatic_block_size: BasicOption[bool] = BasicOption(True) - """If set to `True` while generating for `Target.SYCL`, let the SYCL runtime decide on the block size. + +@dataclass +class SyclOptions(ConfigBase): + """Options specific to the `SYCL <Target.SYCL>` target.""" + + automatic_block_size: BasicOption[bool] = BasicOption(True) + """If set to `True`, let the SYCL runtime decide on the block size. If set to `True`, the kernel is generated for execution via `parallel_for <https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#_parallel_for_invoke>`_ @@ -431,12 +466,15 @@ class CreateKernelConfig(ConfigBase): """Target-Specific Options""" - cpu_optim: Category[CpuOptions] = Category(CpuOptions()) + cpu: Category[CpuOptions] = Category(CpuOptions()) """Options for CPU kernels.""" - gpu_indexing: Category[GpuOptions] = Category(GpuOptions()) + gpu: Category[GpuOptions] = Category(GpuOptions()) """Options for GPU Kernels.""" + sycl: Category[SyclOptions] = Category(SyclOptions()) + """Options for SYCL kernels.""" + @index_dtype.validate def validate_index_type(self, spec: UserTypeSpec): dtype = create_type(spec) @@ -496,11 +534,8 @@ class CreateKernelConfig(ConfigBase): try: from ..jit.gpu_cupy import CupyJit - if ( - self.gpu_indexing is not None - and self.gpu_indexing.block_size is not None - ): - return CupyJit(self.gpu_indexing.block_size) + if self.gpu is not None and self.gpu.block_size is not None: + return CupyJit(self.gpu.block_size) else: return CupyJit() @@ -533,8 +568,6 @@ class CreateKernelConfig(ConfigBase): cpu_vectorize_info: dict | None, gpu_indexing_params: dict | None, ): # pragma: no cover - optim: CpuOptions | None = None - if data_type is not None: _deprecated_option("data_type", "default_dtype") warn( @@ -546,27 +579,33 @@ class CreateKernelConfig(ConfigBase): if cpu_openmp is not None: _deprecated_option("cpu_openmp", "cpu_optim.openmp") + warn( + "Setting the deprecated `cpu_openmp` option will override any options " + "passed in the `cpu.openmp` category.", + UserWarning, + ) - deprecated_omp: OpenMpOptions | bool + deprecated_omp = OpenMpOptions() match cpu_openmp: case True: - deprecated_omp = OpenMpOptions() + deprecated_omp.enable = False case False: - deprecated_omp = False + deprecated_omp.enable = False case int(): - deprecated_omp = OpenMpOptions(num_threads=cpu_openmp) + deprecated_omp.enable = True + deprecated_omp.num_threads = cpu_openmp case _: - raise PsOptionsError( + raise ValueError( f"Invalid option for `cpu_openmp`: {cpu_openmp}" ) - optim = CpuOptions(openmp=deprecated_omp) + self.cpu.openmp = deprecated_omp if cpu_vectorize_info is not None: _deprecated_option("cpu_vectorize_info", "cpu_optim.vectorize") if "instruction_set" in cpu_vectorize_info: if self.target != Target.GenericCPU: - raise PsOptionsError( + raise ValueError( "Setting 'instruction_set' in the deprecated 'cpu_vectorize_info' option is only " "valid if `target == Target.CPU`." ) @@ -585,7 +624,7 @@ class CreateKernelConfig(ConfigBase): case "avx512vl": vec_target = Target.X86_AVX512 | Target._VL case _: - raise PsOptionsError( + raise ValueError( f'Value {isa} in `cpu_vectorize_info["instruction_set"]` is not supported.' ) @@ -598,7 +637,14 @@ class CreateKernelConfig(ConfigBase): self.target = vec_target - deprecated_vec_opts = VectorizationConfig( + warn( + "Setting the deprecated `cpu_vectorize_info` will override any options " + "passed in the `cpu.vectorize` category.", + UserWarning, + ) + + deprecated_vec_opts = VectorizationOptions( + enable=True, assume_inner_stride_one=cpu_vectorize_info.get( "assume_inner_stride_one", False ), @@ -606,28 +652,16 @@ class CreateKernelConfig(ConfigBase): use_nontemporal_stores=cpu_vectorize_info.get("nontemporal", False), ) - if optim is not None: - optim = replace(optim, vectorize=deprecated_vec_opts) - else: - optim = CpuOptions(vectorize=deprecated_vec_opts) - - if optim is not None: - if self.cpu_optim is not None: - raise PsOptionsError( - "Cannot specify both `cpu_optim` and a deprecated legacy optimization option at the same time." - ) - else: - self.cpu_optim = optim + self.cpu.vectorize = deprecated_vec_opts if gpu_indexing_params is not None: _deprecated_option("gpu_indexing_params", "gpu_indexing") + warn( + "Setting the deprecated `gpu_indexing_params` will override any options " + "passed in the `gpu` category." + ) - if self.gpu_indexing is not None: - raise PsOptionsError( - "Cannot specify both `gpu_indexing` and the deprecated `gpu_indexing_params` at the same time." - ) - - self.gpu_indexing = GpuOptions( + self.gpu = GpuOptions( block_size=gpu_indexing_params.get("block_size", None) ) diff --git a/src/pystencils/codegen/driver.py b/src/pystencils/codegen/driver.py index 47bcb905d..6f44e718d 100644 --- a/src/pystencils/codegen/driver.py +++ b/src/pystencils/codegen/driver.py @@ -3,7 +3,14 @@ from typing import cast, Sequence, Iterable, TYPE_CHECKING from dataclasses import dataclass, replace from .target import Target -from .config import CreateKernelConfig, OpenMpOptions, VectorizationConfig, AUTO, GhostLayerSpec, IterationSliceSpec +from .config import ( + CreateKernelConfig, + VectorizationOptions, + AUTO, + _AUTO_TYPE, + GhostLayerSpec, + IterationSliceSpec, +) from .kernel import Kernel, GpuKernel, GpuThreadsRange from .properties import PsSymbolProperty, FieldShape, FieldStride, FieldBasePtr from .parameters import Parameter @@ -115,15 +122,17 @@ class DefaultKernelCreationDriver: int(cfg.is_option_set("ghost_layers")) + int(cfg.is_option_set("iteration_slice")) + int(cfg.is_option_set("index_field")) - ) - + ) + if num_ispace_options_set > 1: raise ValueError( "At most one of the options 'ghost_layers' 'iteration_slice' and 'index_field' may be set." ) - + self._ghost_layers: GhostLayerSpec | None = cfg.get_option("ghost_layers") - self._iteration_slice: IterationSliceSpec | None = cfg.get_option("iteration_slice") + self._iteration_slice: IterationSliceSpec | None = cfg.get_option( + "iteration_slice" + ) self._index_field: Field | None = cfg.get_option("index_field") if num_ispace_options_set == 0: @@ -235,22 +244,26 @@ class DefaultKernelCreationDriver: ) analysis(assignments) - if self._cfg.index_field is not None: + if self._index_field is not None: ispace = create_sparse_iteration_space( self._ctx, assignments, index_field=self._cfg.index_field ) else: - gls = self._cfg.ghost_layers - islice = self._cfg.iteration_slice - - if gls is None and islice is None: - gls = AUTO + gls: GhostLayerSpec | None + if self._ghost_layers == AUTO: + infer_gls = True + gls = None + else: + assert not isinstance(self._ghost_layers, _AUTO_TYPE) + infer_gls = False + gls = self._ghost_layers ispace = create_full_iteration_space( self._ctx, assignments, ghost_layers=gls, - iteration_slice=islice, + iteration_slice=self._iteration_slice, + infer_ghost_layers=infer_gls, ) self._ctx.set_iteration_space(ispace) @@ -279,7 +292,7 @@ class DefaultKernelCreationDriver: if self._intermediates is not None: self._intermediates.cpu_hoist_invariants = kernel_ast.clone() - cpu_cfg = self._cfg.cpu_optim + cpu_cfg = self._cfg.cpu if cpu_cfg is None: return kernel_ast @@ -288,30 +301,41 @@ class DefaultKernelCreationDriver: raise NotImplementedError("Loop blocking not implemented yet.") kernel_ast = self._vectorize(kernel_ast) + kernel_ast = self._add_openmp(kernel_ast) + + if cpu_cfg.use_cacheline_zeroing: + raise NotImplementedError("CL-zeroing not implemented yet") + + return kernel_ast + + def _add_openmp(self, kernel_ast: PsBlock) -> PsBlock: + omp_options = self._cfg.cpu.openmp + enable_omp: bool = omp_options.get_option("enable") - if cpu_cfg.openmp is not False: + if enable_omp: from ..backend.transformations import AddOpenMP - params = ( - cpu_cfg.openmp - if isinstance(cpu_cfg.openmp, OpenMpOptions) - else OpenMpOptions() + add_omp = AddOpenMP( + self._ctx, + nesting_depth=omp_options.get_option("nesting_depth"), + num_threads=omp_options.get_option("num_threads"), + schedule=omp_options.get_option("schedule"), + collapse=omp_options.get_option("collapse"), + omit_parallel=omp_options.get_option("omit_parallel_construct"), ) - add_omp = AddOpenMP(self._ctx, params) kernel_ast = cast(PsBlock, add_omp(kernel_ast)) if self._intermediates is not None: self._intermediates.cpu_openmp = kernel_ast.clone() - if cpu_cfg.use_cacheline_zeroing: - raise NotImplementedError("CL-zeroing not implemented yet") - return kernel_ast def _vectorize(self, kernel_ast: PsBlock) -> PsBlock: - assert self._cfg.cpu_optim is not None - vec_config = self._cfg.cpu_optim.get_vectorization_config() - if vec_config is None: + vec_options = self._cfg.cpu.vectorize + + enable_vec = vec_options.get_option("enable") + + if not enable_vec: return kernel_ast from ..backend.transformations import LoopVectorizer, SelectIntrinsics @@ -328,7 +352,9 @@ class DefaultKernelCreationDriver: inner_loop_dim = ispace.dimensions[inner_loop_coord] # Apply stride (TODO: and alignment) assumptions - if vec_config.assume_inner_stride_one: + assume_unit_stride: bool = vec_options.get_option("assume_inner_stride_one") + + if assume_unit_stride: for field in self._ctx.fields: buf = self._ctx.get_buffer(field) inner_stride = buf.strides[inner_loop_coord] @@ -344,14 +370,14 @@ class DefaultKernelCreationDriver: # TODO: Communicate assumption to runtime system via a precondition # Call loop vectorizer - if vec_config.lanes is None: - lanes = VectorizationConfig.default_lanes( + num_lanes: int | None = vec_options.get_option("lanes") + + if num_lanes is None: + num_lanes = VectorizationOptions.default_lanes( self._target, cast(PsScalarType, self._ctx.default_dtype) ) - else: - lanes = vec_config.lanes - vectorizer = LoopVectorizer(self._ctx, lanes) + vectorizer = LoopVectorizer(self._ctx, num_lanes) def loop_predicate(loop: PsLoop): return loop.counter.symbol == inner_loop_dim.counter @@ -397,15 +423,30 @@ class DefaultKernelCreationDriver: ) elif Target._GPU in self._target: + gpu_opts = self._cfg.gpu + omit_range_check: bool = gpu_opts.get_option("omit_range_check") + match self._target: case Target.SYCL: from ..backend.platforms import SyclPlatform - return SyclPlatform(self._ctx, self._cfg.gpu_indexing) + auto_block_size: bool = self._cfg.sycl.get_option("automatic_block_size") + + return SyclPlatform( + self._ctx, + omit_range_check=omit_range_check, + automatic_block_size=auto_block_size, + ) case Target.CUDA: from ..backend.platforms import CudaPlatform - return CudaPlatform(self._ctx, self._cfg.gpu_indexing) + manual_grid = gpu_opts.get_option("manual_launch_grid") + + return CudaPlatform( + self._ctx, + omit_range_check=omit_range_check, + manual_launch_grid=manual_grid, + ) raise NotImplementedError( f"Code generation for target {self._target} not implemented" diff --git a/src/pystencils/types/types.py b/src/pystencils/types/types.py index 7645a452f..825ac1d5d 100644 --- a/src/pystencils/types/types.py +++ b/src/pystencils/types/types.py @@ -35,7 +35,7 @@ class PsCustomType(PsType): return self._name def c_string(self) -> str: - return f"{self._const_string()} {self._name}" + return f"{self._const_string()}{self._name}" def __repr__(self) -> str: return f"CustomType( {self.name}, const={self.const} )" diff --git a/tests/codegen/test_config.py b/tests/codegen/test_config.py index 715830e70..0f7591b3e 100644 --- a/tests/codegen/test_config.py +++ b/tests/codegen/test_config.py @@ -2,8 +2,17 @@ import pytest from dataclasses import dataclass import numpy as np -from pystencils.codegen.config import BasicOption, Option, Category, ConfigBase, CreateKernelConfig -from pystencils.types.quick import Int, UInt +from pystencils.codegen.config import ( + BasicOption, + Option, + Category, + ConfigBase, + CreateKernelConfig, + CpuOptions +) +from pystencils.field import Field, FieldType +from pystencils.types.quick import Int, UInt, Fp, Ptr +from pystencils.types import PsVectorType def test_descriptors(): @@ -11,16 +20,19 @@ def test_descriptors(): @dataclass class SampleCategory(ConfigBase): val1: BasicOption[int] = BasicOption(2) - val2: Option[bool, str] = Option(False) + val2: Option[bool, str | bool] = Option(False) @val2.validate - def validate_val2(self, v: str): - if v.lower() in ("off", "false", "no"): - return False - elif v.lower() in ("on", "true", "yes"): - return True - - raise ValueError() + def validate_val2(self, v: str | bool): + if isinstance(v, str): + if v.lower() in ("off", "false", "no"): + return False + elif v.lower() in ("on", "true", "yes"): + return True + + raise ValueError() + else: + return v @dataclass class SampleConfig(ConfigBase): @@ -28,7 +40,7 @@ def test_descriptors(): val: BasicOption[str] = BasicOption("fallback") cfg = SampleConfig() - + # Check unset and default values assert cfg.val is None assert cfg.get_option("val") == "fallback" @@ -55,13 +67,41 @@ def test_descriptors(): cfg.cat = c assert cfg.cat.val1 == 32 assert cfg.cat.val2 is True - + assert cfg.cat is not c c.val1 = 13 assert cfg.cat.val1 == 32 + # Check that category objects on two config objects are not the same + cfg1 = SampleConfig() + cfg2 = SampleConfig() + + assert cfg1.cat is not cfg2.cat + + +def test_category_init(): + cfg1 = CreateKernelConfig() + cfg2 = CreateKernelConfig() + + assert cfg1.cpu is not cfg2.cpu + assert cfg1.cpu.openmp is not cfg2.cpu.openmp + assert cfg1.cpu.vectorize is not cfg2.cpu.vectorize + assert cfg1.gpu is not cfg2.gpu + + +def test_category_copy(): + cfg = CreateKernelConfig() + cpu_repl = CpuOptions() + cpu_repl.openmp.num_threads = 42 + + cfg.cpu = cpu_repl + assert cfg.cpu.openmp.num_threads == 42 + assert cfg.cpu is not cpu_repl + assert cfg.cpu.openmp is not cpu_repl.openmp + def test_config_validation(): + # Check index dtype validation cfg = CreateKernelConfig(index_dtype="int32") assert cfg.index_dtype == Int(32) cfg.index_dtype = np.uint64 @@ -72,3 +112,27 @@ def test_config_validation(): with pytest.raises(ValueError): cfg.index_dtype = "double" + + # Check default dtype validation + cfg = CreateKernelConfig(default_dtype="float32") + assert cfg.default_dtype == Fp(32) + cfg.default_dtype = np.int64 + assert cfg.default_dtype == Int(64) + + with pytest.raises(ValueError): + cfg.default_dtype = PsVectorType(Fp(64), 4) + + with pytest.raises(ValueError): + _ = CreateKernelConfig(default_dtype=Ptr(Fp(32))) + + # Check index field validation + idx_field = Field.create_generic( + "idx", spatial_dimensions=1, field_type=FieldType.INDEXED + ) + cfg.index_field = idx_field + assert cfg.index_field == idx_field + + with pytest.raises(ValueError): + cfg.index_field = Field.create_generic( + "idx", spatial_dimensions=1, field_type=FieldType.GENERIC + ) diff --git a/tests/fixtures.py b/tests/fixtures.py index 8c7f12015..71e54bad8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -31,15 +31,17 @@ except ImportError: AVAILABLE_TARGETS += ps.Target.available_vector_cpu_targets() TARGET_IDS = [t.name for t in AVAILABLE_TARGETS] + @pytest.fixture(params=AVAILABLE_TARGETS, ids=TARGET_IDS) def target(request) -> ps.Target: """Provides all code generation targets available on the current hardware""" return request.param + @pytest.fixture def gen_config(target: ps.Target): """Default codegen configuration for the current target. - + For GPU targets, set default indexing options. For vector-CPU targets, set default vectorization config. """ @@ -47,25 +49,24 @@ def gen_config(target: ps.Target): gen_config = ps.CreateKernelConfig(target=target) if target.is_vector_cpu(): - gen_config = replace( - gen_config, - cpu_optim=ps.CpuOptions( - vectorize=ps.VectorizationConfig(assume_inner_stride_one=True) - ), - ) + gen_config.cpu.vectorize.enable = True + gen_config.cpu.vectorize.assume_inner_stride_one = True return gen_config + @pytest.fixture() def xp(target: ps.Target) -> ModuleType: """Primary array module for the current target. - + Returns: `cupy` if `target == Target.CUDA`, and `numpy` otherwise """ if target == ps.Target.CUDA: import cupy as xp + return xp else: import numpy as np + return np diff --git a/tests/kernelcreation/test_buffer_gpu.py b/tests/kernelcreation/test_buffer_gpu.py index 873cc1213..db8cc2596 100644 --- a/tests/kernelcreation/test_buffer_gpu.py +++ b/tests/kernelcreation/test_buffer_gpu.py @@ -300,7 +300,7 @@ def test_iteration_slices(gpu_indexing): gpu_dst_arr.fill(0) config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice, - gpu_indexing=gpu_indexing) + gpu=gpu_indexing) pack_code = create_kernel(pack_eqs, config=config) pack_kernel = pack_code.compile() @@ -313,7 +313,7 @@ def test_iteration_slices(gpu_indexing): unpack_eqs.append(eq) config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice, - gpu_indexing=gpu_indexing) + gpu=gpu_indexing) unpack_code = create_kernel(unpack_eqs, config=config) unpack_kernel = unpack_code.compile() diff --git a/tests/kernelcreation/test_gpu.py b/tests/kernelcreation/test_gpu.py index 57de84b7a..97f0c0fa9 100644 --- a/tests/kernelcreation/test_gpu.py +++ b/tests/kernelcreation/test_gpu.py @@ -112,7 +112,7 @@ def test_ghost_layer(): update_rule = Assignment(dst_field[0, 0], src_field[0, 0]) ghost_layers = [(1, 2), (2, 1)] - config = CreateKernelConfig(target=Target.GPU, ghost_layers=ghost_layers, gpu_indexing="line") + config = CreateKernelConfig(target=Target.GPU, ghost_layers=ghost_layers, gpu="line") ast = create_kernel(sympy_cse_on_assignment_list([update_rule]), config=config) kernel = ast.compile() @@ -135,7 +135,7 @@ def test_setting_value(): f = Field.create_generic("f", 2) update_rule = [Assignment(f(0), sp.Symbol("value"))] - config = CreateKernelConfig(target=Target.GPU, gpu_indexing="line", iteration_slice=iteration_slice) + config = CreateKernelConfig(target=Target.GPU, gpu="line", iteration_slice=iteration_slice) ast = create_kernel(sympy_cse_on_assignment_list(update_rule), config=config) kernel = ast.compile() @@ -207,7 +207,7 @@ def test_four_dimensional_kernel(gpu_indexing, layout, shape): f = Field.create_from_numpy_array("f", arr_cpu) update_rule = [Assignment(f.center, sp.Symbol("value"))] - config = CreateKernelConfig(target=Target.GPU, gpu_indexing=gpu_indexing, iteration_slice=iteration_slice) + config = CreateKernelConfig(target=Target.GPU, gpu=gpu_indexing, iteration_slice=iteration_slice) ast = create_kernel(update_rule, config=config) kernel = ast.compile() diff --git a/tests/kernelcreation/test_iteration_slices.py b/tests/kernelcreation/test_iteration_slices.py index 5c7b4d8cb..892d2d949 100644 --- a/tests/kernelcreation/test_iteration_slices.py +++ b/tests/kernelcreation/test_iteration_slices.py @@ -104,6 +104,9 @@ def test_symbolic_slice(gen_config: CreateKernelConfig, xp): update = Assignment(f.center(), 1) islice = make_slice[sy:ey, sx:ex] gen_config = replace(gen_config, iteration_slice=islice) + + print(repr(gen_config)) + kernel = create_kernel(update, gen_config).compile() for slic in [make_slice[:, :], make_slice[1:-1, 2:-2], make_slice[8:14, 7:11]]: @@ -140,9 +143,7 @@ def test_triangle_pattern(gen_config: CreateKernelConfig, xp): gen_config = replace(gen_config, iteration_slice=islice) if gen_config.target == Target.CUDA: - gen_config = replace( - gen_config, gpu_indexing=GpuOptions(manual_launch_grid=True) - ) + gen_config.gpu.manual_launch_grid = True kernel = create_kernel(update, gen_config).compile() @@ -170,12 +171,10 @@ def test_red_black_pattern(gen_config: CreateKernelConfig, xp): outer_counter = DEFAULTS.spatial_counters[0] start = sp.Piecewise((0, sp.Eq(int_rem(outer_counter, 2), 0)), (1, True)) islice = make_slice[:, start::2] - gen_config = replace(gen_config, iteration_slice=islice) + gen_config.iteration_slice = islice if gen_config.target == Target.CUDA: - gen_config = replace( - gen_config, gpu_indexing=GpuOptions(manual_launch_grid=True) - ) + gen_config.gpu.manual_launch_grid = True try: kernel = create_kernel(update, gen_config).compile() diff --git a/tests/nbackend/kernelcreation/test_openmp.py b/tests/nbackend/kernelcreation/test_openmp.py index ae775ca20..07a2f1026 100644 --- a/tests/nbackend/kernelcreation/test_openmp.py +++ b/tests/nbackend/kernelcreation/test_openmp.py @@ -15,20 +15,21 @@ from pystencils.backend.ast.structural import PsLoop, PsPragma @pytest.mark.parametrize("nesting_depth", range(3)) @pytest.mark.parametrize("schedule", ["static", "static,16", "dynamic", "auto"]) -@pytest.mark.parametrize("collapse", range(3)) +@pytest.mark.parametrize("collapse", [None, 1, 2]) @pytest.mark.parametrize("omit_parallel_construct", range(3)) def test_openmp(nesting_depth, schedule, collapse, omit_parallel_construct): f, g = fields("f, g: [3D]") asm = Assignment(f.center(0), g.center(0)) omp = OpenMpOptions( + enable=True, nesting_depth=nesting_depth, schedule=schedule, collapse=collapse, omit_parallel_construct=omit_parallel_construct, ) gen_config = CreateKernelConfig( - target=Target.CPU, cpu_optim=CpuOptions(openmp=omp) + target=Target.CPU, cpu=CpuOptions(openmp=omp) ) kernel = create_kernel(asm, gen_config) @@ -55,7 +56,7 @@ def test_openmp(nesting_depth, schedule, collapse, omit_parallel_construct): expected_tokens = {"omp", "for", f"schedule({omp.schedule})"} if not omp.omit_parallel_construct: expected_tokens.add("parallel") - if omp.collapse > 0: + if omp.collapse is not None: expected_tokens.add(f"collapse({omp.collapse})") assert tokens == expected_tokens diff --git a/tests/nbackend/kernelcreation/test_options.py b/tests/nbackend/kernelcreation/test_options.py deleted file mode 100644 index fefcc98fe..000000000 --- a/tests/nbackend/kernelcreation/test_options.py +++ /dev/null @@ -1,28 +0,0 @@ -import pytest - -from pystencils.field import Field, FieldType -from pystencils.types.quick import * -from pystencils.codegen.config import ( - CreateKernelConfig, - PsOptionsError, -) - - -def test_invalid_iteration_region_options(): - idx_field = Field.create_generic( - "idx", spatial_dimensions=1, field_type=FieldType.INDEXED - ) - with pytest.raises(PsOptionsError): - CreateKernelConfig( - ghost_layers=2, iteration_slice=(slice(1, -1), slice(1, -1)) - ) - with pytest.raises(PsOptionsError): - CreateKernelConfig(ghost_layers=2, index_field=idx_field) - - -def test_index_field_options(): - with pytest.raises(PsOptionsError): - idx_field = Field.create_generic( - "idx", spatial_dimensions=1, field_type=FieldType.GENERIC - ) - CreateKernelConfig(index_field=idx_field) diff --git a/tests/nbackend/transformations/test_add_pragmas.py b/tests/nbackend/transformations/test_add_pragmas.py index 1d8dd1ded..c1749fe28 100644 --- a/tests/nbackend/transformations/test_add_pragmas.py +++ b/tests/nbackend/transformations/test_add_pragmas.py @@ -12,6 +12,7 @@ from pystencils.backend.ast import dfs_preorder from pystencils.backend.ast.structural import PsBlock, PsPragma, PsLoop from pystencils.backend.transformations import InsertPragmasAtLoops, LoopPragma + def test_insert_pragmas(): ctx = KernelCreationContext() factory = AstFactory(ctx) diff --git a/tests/test_quicktests.py b/tests/test_quicktests.py index 3e7f4f071..d27a5e61b 100644 --- a/tests/test_quicktests.py +++ b/tests/test_quicktests.py @@ -74,8 +74,8 @@ def test_basic_vectorization(): ast = ps.create_kernel( update_rule, target=target, - cpu_optim=ps.CpuOptions( - vectorize=ps.VectorizationConfig(assume_inner_stride_one=True) + cpu=ps.CpuOptions( + vectorize=ps.VectorizationOptions(enable=True, assume_inner_stride_one=True) ), ) -- GitLab From 315cc8c0cdafb0b2f91a559c680f246cfb8b62e7 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Mon, 20 Jan 2025 15:02:11 +0100 Subject: [PATCH 4/7] Update documentation pages. - Update docs on config system - Strictly separate API reference from user guides - Rename "Reference Guides" to "User Manual" - Clean up API docs - Fix various docstring errors --- docs/Makefile | 2 +- docs/source/api/codegen.rst | 38 ++++++++--- docs/source/api/jit.rst | 4 +- docs/source/api/symbolic/assignments.md | 16 +++++ docs/source/api/{ => symbolic}/field.rst | 4 +- docs/source/api/symbolic/index.md | 9 +++ .../api/{ => symbolic}/sympyextensions.rst | 4 +- docs/source/{reference => api}/types.rst | 0 docs/source/contributing/index.md | 2 +- docs/source/index.rst | 15 ++--- docs/source/{migration.rst => migration.md} | 67 +++++++++++++------ .../{reference => user_manual}/gpu_kernels.md | 9 +-- .../kernelcreation.md | 9 +-- .../symbolic_language.rst | 7 +- src/pystencils/__init__.py | 8 --- .../backend/transformations/add_pragmas.py | 3 +- src/pystencils/codegen/__init__.py | 8 --- src/pystencils/codegen/config.py | 44 ++++++------ 18 files changed, 144 insertions(+), 105 deletions(-) create mode 100644 docs/source/api/symbolic/assignments.md rename docs/source/api/{ => symbolic}/field.rst (97%) create mode 100644 docs/source/api/symbolic/index.md rename docs/source/api/{ => symbolic}/sympyextensions.rst (97%) rename docs/source/{reference => api}/types.rst (100%) rename docs/source/{migration.rst => migration.md} (54%) rename docs/source/{reference => user_manual}/gpu_kernels.md (97%) rename docs/source/{reference => user_manual}/kernelcreation.md (99%) rename docs/source/{reference => user_manual}/symbolic_language.rst (96%) diff --git a/docs/Makefile b/docs/Makefile index a293f14ee..0cfe1ab8b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -22,7 +22,7 @@ html: @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) clean: - rm -rf source/reference/generated rm -rf source/api/generated + rm -rf source/api/symbolic/generated rm -rf source/backend/generated @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/source/api/codegen.rst b/docs/source/api/codegen.rst index d65e9a358..1fb83fe5f 100644 --- a/docs/source/api/codegen.rst +++ b/docs/source/api/codegen.rst @@ -1,5 +1,5 @@ -pystencils.codegen -================== +Code Generation +=============== .. module:: pystencils.codegen @@ -15,16 +15,19 @@ Invocation Configuration ------------- +.. module:: pystencils.codegen.config + .. autosummary:: :toctree: generated :nosignatures: - :template: autosummary/entire_class.rst + :template: autosummary/recursive_class.rst CreateKernelConfig - CpuOptimConfig - OpenMpConfig - VectorizationConfig - GpuIndexingConfig + CpuOptions + OpenMpOptions + VectorizationOptions + GpuOptions + SyclOptions .. autosummary:: :toctree: generated @@ -32,9 +35,24 @@ Configuration AUTO +.. dropdown:: Configuration System Implementation Details + + .. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/entire_class.rst + + Option + BasicOption + Category + ConfigBase + + Target Specification -------------------- +.. module:: pystencils.codegen.target + .. autosummary:: :toctree: generated :nosignatures: @@ -45,12 +63,14 @@ Target Specification Code Generation Drivers ----------------------- +.. module:: pystencils.codegen.driver + .. autosummary:: :toctree: generated :nosignatures: :template: autosummary/entire_class.rst - driver.DefaultKernelCreationDriver + DefaultKernelCreationDriver .. autosummary:: :toctree: generated @@ -61,6 +81,8 @@ Code Generation Drivers Output Code Objects ------------------- +.. currentmodule:: pystencils.codegen + .. autosummary:: :toctree: generated :nosignatures: diff --git a/docs/source/api/jit.rst b/docs/source/api/jit.rst index 7bcd9989c..f2e271db3 100644 --- a/docs/source/api/jit.rst +++ b/docs/source/api/jit.rst @@ -1,5 +1,5 @@ -pystencils.jit -============== +JIT Compilation +=============== .. module:: pystencils.jit diff --git a/docs/source/api/symbolic/assignments.md b/docs/source/api/symbolic/assignments.md new file mode 100644 index 000000000..69446a8a5 --- /dev/null +++ b/docs/source/api/symbolic/assignments.md @@ -0,0 +1,16 @@ +# Assignments and AssignmentCollection + +```{eval-rst} + +.. py:class:: pystencils.Assignment + + Monkeypatched variant of `sympy.codegen.ast.Assignment`. + Represents an assignment of an expression to a symbol. + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/recursive_class.rst + + pystencils.AssignmentCollection +``` diff --git a/docs/source/api/field.rst b/docs/source/api/symbolic/field.rst similarity index 97% rename from docs/source/api/field.rst rename to docs/source/api/symbolic/field.rst index 79cc12a3a..33219c059 100644 --- a/docs/source/api/field.rst +++ b/docs/source/api/symbolic/field.rst @@ -1,5 +1,5 @@ -pystencils.field -================ +Fields +====== .. module:: pystencils.field diff --git a/docs/source/api/symbolic/index.md b/docs/source/api/symbolic/index.md new file mode 100644 index 000000000..fad3df20b --- /dev/null +++ b/docs/source/api/symbolic/index.md @@ -0,0 +1,9 @@ +# Symbolic Toolbox + +:::{toctree} +:maxdepth: 1 + +field +assignments +sympyextensions +::: diff --git a/docs/source/api/sympyextensions.rst b/docs/source/api/symbolic/sympyextensions.rst similarity index 97% rename from docs/source/api/sympyextensions.rst rename to docs/source/api/symbolic/sympyextensions.rst index d377f998e..e3d10fbdf 100644 --- a/docs/source/api/sympyextensions.rst +++ b/docs/source/api/symbolic/sympyextensions.rst @@ -1,5 +1,5 @@ -pystencils.sympyextensions -========================== +Extensions to SymPy +=================== .. module:: pystencils.sympyextensions diff --git a/docs/source/reference/types.rst b/docs/source/api/types.rst similarity index 100% rename from docs/source/reference/types.rst rename to docs/source/api/types.rst diff --git a/docs/source/contributing/index.md b/docs/source/contributing/index.md index 39e68b06f..04ad821ce 100644 --- a/docs/source/contributing/index.md +++ b/docs/source/contributing/index.md @@ -1,4 +1,4 @@ -# Contributor Guide +# Contribution Guide Welcome to the Contributor's Guide to pystencils! If you are interested in contributing to the development of pystencils, this is the place to start. diff --git a/docs/source/index.rst b/docs/source/index.rst index 5ddec09f2..cb455c8b4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -77,19 +77,18 @@ Topics .. toctree:: :maxdepth: 1 - :caption: Reference Guides + :caption: User Manual - reference/symbolic_language - reference/kernelcreation - reference/gpu_kernels - reference/types + user_manual/symbolic_language + user_manual/kernelcreation + user_manual/gpu_kernels .. toctree:: :maxdepth: 1 - :caption: API + :caption: API Reference - api/field - api/sympyextensions + api/symbolic/index + api/types api/codegen api/jit diff --git a/docs/source/migration.rst b/docs/source/migration.md similarity index 54% rename from docs/source/migration.rst rename to docs/source/migration.md index ea59d8881..c3cb17d0f 100644 --- a/docs/source/migration.rst +++ b/docs/source/migration.md @@ -1,36 +1,62 @@ -.. _page_v2_migration: +--- +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +mystnb: + execution_mode: cache +--- -*************************** -Version 2.0 Migration Guide -*************************** +(_page_v2_migration)= +# Version 2.0 Migration Guide With version 2.0, many APIs of *pystencils* will be changed; old interfaces are being deprecated and new systems are put in place. This page is a still-incomplete list of these changes, with advice on how to migrate your code from pystencils 1.x to pystencils 2.0. -Kernel Creation -=============== +```{code-cell} ipython3 +:tags: [remove-cell] -Configuration -------------- +import pystencils as ps +``` -The API of `create_kernel`, and the configuration options of the `CreateKernelConfig`, have changed significantly: + +## Kernel Creation + +### Configuration + +The API of {any}`create_kernel`, and the configuration options of the {any}`CreateKernelConfig`, have changed significantly. +The `CreateKernelConfig` class has been refined to be safe to copy and edit incrementally. +The recommended way of setting up the code generator is now *incremental configuration*: + +```{code-cell} ipython3 +cfg = ps.CreateKernelConfig() +cfg.default_dtype = "float32" +cfg.cpu.openmp.enable = True +cfg.cpu.openmp.num_threads = 8 +cfg.ghost_layers = 2 +``` - *Data Types:* `CreateKernelConfig` now takes to parameters to control data types in your kernels: the ``default_dtype`` is applied to all numerical computations, while the ``index_dtype`` is used for all index calculations and loop counters. +- *CPU Optimization Options:* Should now be set via the {any}`cpu <CpuOptions>` option category and its subcategories. .. dropdown:: Deprecated options of `CreateKernelConfig` - ``data_type``: Use ``default_dtype`` instead - - ``cpu_openmp``: Set OpenMP-Options via an `OpenMpConfig` in the ``cpu_optim`` (`CpuOptimConfig`) instead. - - ``cpu_vectorize_info``: Set vectorization options via a `VectorizationConfig` in the ``cpu_optim`` option instead - - ``gpu_indexing_params``: Set GPU indexing options via a `GpuIndexingConfig` in the ``gpu_indexing`` option instead + - ``cpu_openmp``: Set OpenMP-Options in the `cpu.openmp <OpenMpOptions>` category instead. + - ``cpu_vectorize_info``: Set vectorization options in the `cpu.vectorize <VectorizationOptions>` category instead + - ``gpu_indexing_params``: Set GPU indexing options in the `gpu <GpuOptions>` category instead -Type Checking -------------- +### Type Checking The old type checking system of pystencils' code generator has been replaced by a new type inference and validation mechanism whose rules are much stricter than before. @@ -38,24 +64,23 @@ While running `create_kernel`, you may now encounter a `TypificationError` where If this happens, it is probable that you have been doing some illegal, maybe dangerous, or at least unsafe things with data types (like inserting integers into a floating-point context without casting them, or mixing types of different precisions or signedness). If you are sure the error is not your fault, please file an issue at our -`bug tracker <https://i10git.cs.fau.de/pycodegen/pystencils/-/issues>`_. +[bug tracker](https://i10git.cs.fau.de/pycodegen/pystencils/-/issues). -Type System -=========== +### Type System -The ``pystencils.typing`` module has been entirely replaced by the new `pystencils.types` module, +The ``pystencils.typing`` module has been entirely replaced by the new {any}`pystencils.types` module, which is home to a completely new type system. -The primary interaction points with this system are still the `TypedSymbol` class and the `create_type` routine. +The primary interaction points with this system are still the {any}`TypedSymbol` class and the {any}`create_type` routine. Code using any of these two should not require any changes, except: - *Importing `TypedSymbol` and `create_type`:* Both `TypedSymbol` and `create_type` should now be imported directly from the ``pystencils`` namespace. - *Custom data types:* `TypedSymbol` used to accept arbitrary strings as data types. - This is no longer possible; instead, import `pystencils.types.PsCustomType` and use it to describe + This is no longer possible; instead, import {any}`pystencils.types.PsCustomType` and use it to describe custom data types unknown to pystencils, as in ``TypedSymbol("xs", PsCustomType("std::vector< int >"))`` All old data type classes (such as ``BasicType``, ``PointerType``, ``StructType``, etc.) have been removed -and replaced by the class hierarchy below `PsType`. +and replaced by the class hierarchy below {any}`PsType`. Directly using any of these type classes in the frontend is discouraged unless absolutely necessary; in most cases, `create_type` suffices. diff --git a/docs/source/reference/gpu_kernels.md b/docs/source/user_manual/gpu_kernels.md similarity index 97% rename from docs/source/reference/gpu_kernels.md rename to docs/source/user_manual/gpu_kernels.md index 786840d18..4db2d7944 100644 --- a/docs/source/reference/gpu_kernels.md +++ b/docs/source/user_manual/gpu_kernels.md @@ -159,15 +159,10 @@ kernel = ps.create_kernel(assignments, cfg).compile() ``` This warns us that the threads range could not be determined automatically. -We can disable this warning by setting `manual_launch_grid` in the GPU indexing options: +We can disable this warning by setting `manual_launch_grid` in the GPU option category: ```{code-cell} -cfg = ps.CreateKernelConfig( - # ... other options ... - gpu_indexing=ps.GpuIndexingConfig( - manual_launch_grid=True - ) -) +cfg.gpu.manual_launch_grid = True ``` Now, to execute our kernel, we have to manually specify its launch grid: diff --git a/docs/source/reference/kernelcreation.md b/docs/source/user_manual/kernelcreation.md similarity index 99% rename from docs/source/reference/kernelcreation.md rename to docs/source/user_manual/kernelcreation.md index 248855fc1..c85c8f99d 100644 --- a/docs/source/reference/kernelcreation.md +++ b/docs/source/user_manual/kernelcreation.md @@ -485,13 +485,10 @@ h = sp.Symbol("h") cfg = ps.CreateKernelConfig( target=ps.Target.X86_AVX512, default_dtype="float32", - cpu_optim=ps.CpuOptimConfig( - openmp=True, - vectorize=ps.VectorizationConfig( - assume_inner_stride_one=True - ) - ) ) +cfg.cpu.openmp.enable = True +cfg.cpu.vectorize.enable = True +cfg.cpu.vectorize.assume_inner_stride_one = True assignments = [ ps.Assignment( diff --git a/docs/source/reference/symbolic_language.rst b/docs/source/user_manual/symbolic_language.rst similarity index 96% rename from docs/source/reference/symbolic_language.rst rename to docs/source/user_manual/symbolic_language.rst index 63b94e04d..6d219306e 100644 --- a/docs/source/reference/symbolic_language.rst +++ b/docs/source/user_manual/symbolic_language.rst @@ -42,10 +42,6 @@ Assignments are the fundamental components of pystencils kernels; they are used both for assigning expressions to symbols and for writing values to fields. -.. py:class:: pystencils.Assignment - - Slightly monkey-patched version of `sympy.codegen.ast.Assignment`. - Assignments are combined and structured inside `assignment collections <pystencils.AssignmentCollection>`. An assignment collection contains two separate lists of assignments: @@ -56,10 +52,9 @@ An assignment collection contains two separate lists of assignments: into fields. .. autosummary:: - :toctree: generated :nosignatures: - :template: autosummary/recursive_class.rst + pystencils.Assignment pystencils.AssignmentCollection diff --git a/src/pystencils/__init__.py b/src/pystencils/__init__.py index 8c59f7846..a23ce185d 100644 --- a/src/pystencils/__init__.py +++ b/src/pystencils/__init__.py @@ -3,10 +3,6 @@ from .codegen import ( Target, CreateKernelConfig, - CpuOptions, - VectorizationOptions, - OpenMpOptions, - GpuOptions, AUTO ) from .defaults import DEFAULTS @@ -50,10 +46,6 @@ __all__ = [ "create_numeric_type", "make_slice", "CreateKernelConfig", - "CpuOptions", - "VectorizationOptions", - "GpuOptions", - "OpenMpOptions", "AUTO", "create_kernel", "create_staggered_kernel", diff --git a/src/pystencils/backend/transformations/add_pragmas.py b/src/pystencils/backend/transformations/add_pragmas.py index f44b89c72..0e6d314ac 100644 --- a/src/pystencils/backend/transformations/add_pragmas.py +++ b/src/pystencils/backend/transformations/add_pragmas.py @@ -98,8 +98,7 @@ class InsertPragmasAtLoops: class AddOpenMP: """Apply OpenMP directives to loop nests. - This transformation augments the AST with OpenMP pragmas according to the given - `OpenMpConfig` configuration. + This transformation augments the AST with OpenMP pragmas according to the given configuration. """ def __init__( diff --git a/src/pystencils/codegen/__init__.py b/src/pystencils/codegen/__init__.py index 3780527c6..e13f911dd 100644 --- a/src/pystencils/codegen/__init__.py +++ b/src/pystencils/codegen/__init__.py @@ -1,10 +1,6 @@ from .target import Target from .config import ( CreateKernelConfig, - CpuOptions, - VectorizationOptions, - OpenMpOptions, - GpuOptions, AUTO, ) from .parameters import Parameter @@ -14,10 +10,6 @@ from .driver import create_kernel, get_driver __all__ = [ "Target", "CreateKernelConfig", - "CpuOptions", - "VectorizationOptions", - "OpenMpOptions", - "GpuOptions", "AUTO", "Parameter", "Kernel", diff --git a/src/pystencils/codegen/config.py b/src/pystencils/codegen/config.py index 9abf51222..cb457f673 100644 --- a/src/pystencils/codegen/config.py +++ b/src/pystencils/codegen/config.py @@ -25,7 +25,11 @@ if TYPE_CHECKING: Option_T = TypeVar("Option_T") +"""Type variable for option values""" + + Arg_T = TypeVar("Arg_T") +"""Type variable for option arguments""" class Option(Generic[Option_T, Arg_T]): @@ -35,19 +39,19 @@ class Option(Generic[Option_T, Arg_T]): It maintains a default value for the option that is used when no value was specified by the user. - In configuration options, the value `None` stands for `unset`. + In configuration options, the value `None` stands for ``unset``. It can therefore not be used to set an option to the meaning "not any", or "empty" - for these, special values need to be used. The Option allows a validator function to be specified, which will be called to perform sanity checks on user-provided values. - Through the validator, options may also be set from arguments of a different type (`Arg_T`) - than their value type (`Option_T`). If `Arg_T` is different from `Option_T`, + Through the validator, options may also be set from arguments of a different type (``Arg_T``) + than their value type (``Option_T``). If ``Arg_T`` is different from ``Option_T``, the validator must perform the conversion from the former to the latter. .. note:: - `Arg_T` must always be a supertype of `Option_T`. + ``Arg_T`` must always be a supertype of ``Option_T``. """ def __init__( @@ -99,7 +103,8 @@ class Option(Generic[Option_T, Arg_T]): delattr(obj, self._lookup) -class BasicOption(Option[Option_T, Option_T]): ... # noqa: E701 +class BasicOption(Option[Option_T, Option_T]): + "Subclass of Option where ``Arg_T == Option_T``." class ConfigBase(ABC): @@ -171,6 +176,7 @@ class ConfigBase(ABC): Category_T = TypeVar("Category_T", bound=ConfigBase) +"""Type variable for option categories.""" class Category(Generic[Category_T]): @@ -212,7 +218,7 @@ Currently, these options permit `AUTO`: @dataclass class OpenMpOptions(ConfigBase): - """Parameters controlling kernel parallelization using OpenMP.""" + """Configuration options controlling automatic OpenMP instrumentation.""" enable: BasicOption[bool] = BasicOption(False) """Enable OpenMP instrumentation""" @@ -238,11 +244,7 @@ class OpenMpOptions(ConfigBase): @dataclass class VectorizationOptions(ConfigBase): - """Configuration for the auto-vectorizer. - - If any flag in this configuration is set to a value not supported by the CPU specified - in `CreateKernelConfig.target`, an error will be raised. - """ + """Configuration for the auto-vectorizer.""" enable: BasicOption[bool] = BasicOption(False) """Enable intrinsic vectorization.""" @@ -305,11 +307,7 @@ class VectorizationOptions(ConfigBase): @dataclass class CpuOptions(ConfigBase): - """Configuration for the CPU optimizer. - - If any flag in this configuration is set to a value not supported by the CPU specified - in `CreateKernelConfig.target`, an error will be raised. - """ + """Configuration options specific to CPU targets.""" openmp: Category[OpenMpOptions] = Category(OpenMpOptions()) """Options governing OpenMP-instrumentation. @@ -335,7 +333,7 @@ class CpuOptions(ConfigBase): @dataclass class GpuOptions(ConfigBase): - """Configure index translation behaviour for kernels generated for GPU targets.""" + """Configuration options specific to GPU targets.""" omit_range_check: BasicOption[bool] = BasicOption(False) """If set to `True`, omit the iteration counter range check. @@ -467,13 +465,13 @@ class CreateKernelConfig(ConfigBase): """Target-Specific Options""" cpu: Category[CpuOptions] = Category(CpuOptions()) - """Options for CPU kernels.""" + """Options for CPU kernels. See `CpuOptions`.""" gpu: Category[GpuOptions] = Category(GpuOptions()) - """Options for GPU Kernels.""" + """Options for GPU Kernels. See `GpuOptions`.""" sycl: Category[SyclOptions] = Category(SyclOptions()) - """Options for SYCL kernels.""" + """Options for SYCL kernels. See `SyclOptions`.""" @index_dtype.validate def validate_index_type(self, spec: UserTypeSpec): @@ -503,13 +501,13 @@ class CreateKernelConfig(ConfigBase): """Deprecated; use `default_dtype` instead""" cpu_openmp: InitVar[bool | int | None] = None - """Deprecated; use `cpu_optim.openmp <CpuOptimConfig.openmp>` instead.""" + """Deprecated; use `cpu.openmp <CpuOptions.openmp>` instead.""" cpu_vectorize_info: InitVar[dict | None] = None - """Deprecated; use `cpu_optim.vectorize <CpuOptimConfig.vectorize>` instead.""" + """Deprecated; use `cpu.vectorize <CpuOptions.vectorize>` instead.""" gpu_indexing_params: InitVar[dict | None] = None - """Deprecated; use `gpu_indexing` instead.""" + """Deprecated; set options in the `gpu` category instead.""" # Getters -- GitLab From 958a499f4a529c4ca1636e306fdecc327da13759 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Mon, 20 Jan 2025 15:27:51 +0100 Subject: [PATCH 5/7] fix testsuite --- .../01_tutorial_getting_started.ipynb | 10 ++++--- tests/kernelcreation/test_buffer_gpu.py | 6 ++-- tests/kernelcreation/test_iteration_slices.py | 5 ++-- tests/nbackend/kernelcreation/test_openmp.py | 26 +++++++----------- .../test_data/datahandling_save_test.npz | Bin 428 -> 410 bytes tests/test_quicktests.py | 11 +++----- 6 files changed, 24 insertions(+), 34 deletions(-) diff --git a/docs/source/tutorials/01_tutorial_getting_started.ipynb b/docs/source/tutorials/01_tutorial_getting_started.ipynb index 04dc50e51..f6c92a6bb 100644 --- a/docs/source/tutorials/01_tutorial_getting_started.ipynb +++ b/docs/source/tutorials/01_tutorial_getting_started.ipynb @@ -1138,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1262,11 +1262,13 @@ } ], "source": [ + "cfg = ps.CreateKernelConfig()\n", + "cfg.cpu.openmp.enable = True\n", + "cfg.cpu.openmp.num_threads = 2\n", "ast = ps.create_kernel(\n", " update_rule,\n", - " cpu = ps.CpuOptions(\n", - " openmp=ps.OpenMpOptions(enable=True, num_threads=2))\n", - " )\n", + " cfg\n", + ")\n", "\n", "ps.show_code(ast)" ] diff --git a/tests/kernelcreation/test_buffer_gpu.py b/tests/kernelcreation/test_buffer_gpu.py index db8cc2596..0b5019fba 100644 --- a/tests/kernelcreation/test_buffer_gpu.py +++ b/tests/kernelcreation/test_buffer_gpu.py @@ -299,8 +299,7 @@ def test_iteration_slices(gpu_indexing): gpu_src_arr.set(src_arr) gpu_dst_arr.fill(0) - config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice, - gpu=gpu_indexing) + config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice) pack_code = create_kernel(pack_eqs, config=config) pack_kernel = pack_code.compile() @@ -312,8 +311,7 @@ def test_iteration_slices(gpu_indexing): eq = Assignment(dst_field(idx), buffer(idx)) unpack_eqs.append(eq) - config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice, - gpu=gpu_indexing) + config = CreateKernelConfig(target=Target.GPU, iteration_slice=pack_slice) unpack_code = create_kernel(unpack_eqs, config=config) unpack_kernel = unpack_code.compile() diff --git a/tests/kernelcreation/test_iteration_slices.py b/tests/kernelcreation/test_iteration_slices.py index 892d2d949..02b6b9922 100644 --- a/tests/kernelcreation/test_iteration_slices.py +++ b/tests/kernelcreation/test_iteration_slices.py @@ -13,7 +13,6 @@ from pystencils import ( make_slice, Target, CreateKernelConfig, - GpuOptions, DynamicType, ) from pystencils.sympyextensions.integer_functions import int_rem @@ -81,7 +80,7 @@ def test_numerical_slices(gen_config: CreateKernelConfig, xp, islice): try: kernel = create_kernel(update, gen_config).compile() except NotImplementedError: - if gen_config.target.is_vector_cpu(): + if gen_config.get_target().is_vector_cpu(): # TODO Gather/Scatter not implemented yet pytest.xfail("Gather/Scatter not available yet") @@ -179,7 +178,7 @@ def test_red_black_pattern(gen_config: CreateKernelConfig, xp): try: kernel = create_kernel(update, gen_config).compile() except NotImplementedError: - if gen_config.target.is_vector_cpu(): + if gen_config.get_target().is_vector_cpu(): pytest.xfail("Gather/Scatter not implemented yet") if isinstance(kernel, CupyKernelWrapper): diff --git a/tests/nbackend/kernelcreation/test_openmp.py b/tests/nbackend/kernelcreation/test_openmp.py index 07a2f1026..4e24cd1b2 100644 --- a/tests/nbackend/kernelcreation/test_openmp.py +++ b/tests/nbackend/kernelcreation/test_openmp.py @@ -4,8 +4,6 @@ from pystencils import ( Assignment, create_kernel, CreateKernelConfig, - CpuOptions, - OpenMpOptions, Target, ) @@ -21,16 +19,12 @@ def test_openmp(nesting_depth, schedule, collapse, omit_parallel_construct): f, g = fields("f, g: [3D]") asm = Assignment(f.center(0), g.center(0)) - omp = OpenMpOptions( - enable=True, - nesting_depth=nesting_depth, - schedule=schedule, - collapse=collapse, - omit_parallel_construct=omit_parallel_construct, - ) - gen_config = CreateKernelConfig( - target=Target.CPU, cpu=CpuOptions(openmp=omp) - ) + gen_config = CreateKernelConfig(target=Target.CPU) + gen_config.cpu.openmp.enable = True + gen_config.cpu.openmp.nesting_depth = nesting_depth + gen_config.cpu.openmp.schedule = schedule + gen_config.cpu.openmp.collapse = collapse + gen_config.cpu.openmp.omit_parallel_construct = omit_parallel_construct kernel = create_kernel(asm, gen_config) ast = kernel.body @@ -53,10 +47,10 @@ def test_openmp(nesting_depth, schedule, collapse, omit_parallel_construct): pragma = find_omp_pragma(ast) tokens = set(pragma.text.split()) - expected_tokens = {"omp", "for", f"schedule({omp.schedule})"} - if not omp.omit_parallel_construct: + expected_tokens = {"omp", "for", f"schedule({schedule})"} + if not omit_parallel_construct: expected_tokens.add("parallel") - if omp.collapse is not None: - expected_tokens.add(f"collapse({omp.collapse})") + if collapse is not None: + expected_tokens.add(f"collapse({collapse})") assert tokens == expected_tokens diff --git a/tests/runtime/test_data/datahandling_save_test.npz b/tests/runtime/test_data/datahandling_save_test.npz index 22202358a4fa1d1cea4db89c0889f5bca636598b..d363a8a0aba1bb78a06314a19b887eb4c4975334 100644 GIT binary patch literal 410 zcmWIWW@Zs#U|`??Vnv4TVm_%5Ad7*Ofq|VtgrT@7Sud}kl953GECiAPO9ScIZ^U0o z3!FR=a4cZ$yh%}WVwU7BU6409ZQ;7b3+7FW4+)wwLwtVxlu2Ad{F++6tX$&hDq>5R zc1o#PaXF-{T)8-4wS(G&B!*`GZ;QWZ*n0I}`m&5M0Iy?Gic9G07)B-$W?W$d3JM5l iU<A?7kP7f7R#Puf6Vyim-mGjOGnjxd3rI_WO#}cXWnnh} literal 428 zcmWIWW@Zs#U|`??Vnv4TVm_%@Ad7*Ofq|VtgrT@7Sud}kl953GECiAPO9ScIZ^U0o z3!FR=a4cZ$yh%}WVwU7BU6409ZQ;7b3+7FW4+)wwLwtVxlu2Ad{F++6R|5m|&w1#0 zgl-SIU~n-Ih&Eig61XB%LAb!M;UN3(z$z0Nx621J>=G6f+c7W%;B{3>amfI=ijhf# q8CPh50tW&b7(p~N>;k-r)ie#L3F@@~Z&o&t8B9Q!1*CPrCISFllx#cz diff --git a/tests/test_quicktests.py b/tests/test_quicktests.py index d27a5e61b..9cefc84c5 100644 --- a/tests/test_quicktests.py +++ b/tests/test_quicktests.py @@ -71,13 +71,10 @@ def test_basic_vectorization(): update_rule = [ ps.Assignment(g[0, 0], f[0, 0] + f[-1, 0] + f[1, 0] + f[0, 1] + f[0, -1] + 42.0) ] - ast = ps.create_kernel( - update_rule, - target=target, - cpu=ps.CpuOptions( - vectorize=ps.VectorizationOptions(enable=True, assume_inner_stride_one=True) - ), - ) + cfg = ps.CreateKernelConfig(target=target) + cfg.cpu.vectorize.enable = True + cfg.cpu.vectorize.assume_inner_stride_one = True + ast = ps.create_kernel(update_rule, cfg) func = ast.compile() -- GitLab From 3e1cf7b09fd5b409027b01cb08d19072d329fd5c Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Mon, 20 Jan 2025 15:36:32 +0100 Subject: [PATCH 6/7] add test_override --- tests/codegen/test_config.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/codegen/test_config.py b/tests/codegen/test_config.py index 0f7591b3e..f7f29b760 100644 --- a/tests/codegen/test_config.py +++ b/tests/codegen/test_config.py @@ -136,3 +136,26 @@ def test_config_validation(): cfg.index_field = Field.create_generic( "idx", spatial_dimensions=1, field_type=FieldType.GENERIC ) + + +def test_override(): + cfg1 = CreateKernelConfig() + cfg1.function_name = "test" + cfg1.cpu.openmp.schedule = "dynamic" + cfg1.gpu.manual_launch_grid = False + cfg1.allow_double_writes = True + + cfg2 = CreateKernelConfig() + cfg2.function_name = "func" + cfg2.cpu.openmp.schedule = "static(5)" + cfg2.cpu.vectorize.lanes = 12 + cfg2.allow_double_writes = False + + cfg1.override(cfg2) + + assert cfg1.function_name == "func" + assert cfg1.cpu.openmp.schedule == "static(5)" + assert cfg1.cpu.openmp.enable is None + assert cfg1.cpu.vectorize.lanes == 12 + assert cfg1.cpu.vectorize.assume_aligned is None + assert cfg1.allow_double_writes is False -- GitLab From 9c97cccb51c5596922cd027f0c9fc95bbf65e3a7 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Wed, 22 Jan 2025 13:47:09 +0100 Subject: [PATCH 7/7] some minor code + docs cleanup --- docs/source/api/codegen.md | 180 ++++++++++++++++++ docs/source/api/codegen.rst | 94 --------- docs/source/migration.md | 12 +- .../backend/kernelcreation/iteration_space.py | 6 +- src/pystencils/codegen/config.py | 19 +- .../test_data/datahandling_save_test.npz | Bin 410 -> 420 bytes 6 files changed, 195 insertions(+), 116 deletions(-) create mode 100644 docs/source/api/codegen.md delete mode 100644 docs/source/api/codegen.rst diff --git a/docs/source/api/codegen.md b/docs/source/api/codegen.md new file mode 100644 index 000000000..b739a4f33 --- /dev/null +++ b/docs/source/api/codegen.md @@ -0,0 +1,180 @@ +# Code Generation + +## Invocation + +```{eval-rst} +.. module:: pystencils.codegen + +.. autosummary:: + :toctree: generated + :nosignatures: + + create_kernel +``` + +## Configuration + +```{eval-rst} +.. module:: pystencils.codegen.config +``` + +The code generation driver (`create_kernel`, but also `DefaultKernelCreationDriver`) can be configured by +passing it a `CreateKernelConfig` object. +This object can be constructed incrementally: + +```Python +cfg = ps.CreateKernelConfig() +cfg.default_dtype = "float32" +cfg.target = ps.Target.X86_AVX +cfg.cpu.openmp.enable = True +cfg.cpu.vectorize.enable = True +cfg.cpu.vectorize.assume_inner_stride_one = True +``` + +### Options and Option Categories + +The following options and option categories are exposed by the configuration object: + +#### Target Specification + +```{eval-rst} +.. current + +.. autosummary:: + + ~CreateKernelConfig.target +``` + +#### Data Types + +```{eval-rst} +.. autosummary:: + + ~CreateKernelConfig.default_dtype + ~CreateKernelConfig.index_dtype +``` + +#### Iteration Space + +```{eval-rst} +.. autosummary:: + + ~CreateKernelConfig.ghost_layers + ~CreateKernelConfig.iteration_slice + ~CreateKernelConfig.index_field +``` + +#### Kernel Constraint Checks + +```{eval-rst} +.. autosummary:: + + ~CreateKernelConfig.allow_double_writes + ~CreateKernelConfig.skip_independence_check +``` + +#### Target-Specific Options + +The following categories with target-specific options are exposed: + +| | | +|---------------------------|--------------------------| +| {any}`cpu <CpuOptions>` | Options for CPU kernels | +| {any}`gpu <GpuOptions>` | Options for GPU kernels | +| {any}`sycl <SyclOptions>` | Options for SYCL kernels | + + +#### Kernel Object and Just-In-Time Compilation + +```{eval-rst} +.. autosummary:: + + ~CreateKernelConfig.function_name + ~CreateKernelConfig.jit +``` + +### Configuration System Classes + +```{eval-rst} + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/recursive_class.rst + + CreateKernelConfig + CpuOptions + OpenMpOptions + VectorizationOptions + GpuOptions + SyclOptions + +.. autosummary:: + :toctree: generated + :nosignatures: + + AUTO + +.. dropdown:: Implementation Details + + .. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/entire_class.rst + + Option + BasicOption + Category + ConfigBase + +``` + +## Target Specification + +```{eval-rst} + +.. module:: pystencils.codegen.target + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/recursive_class.rst + + Target + +``` + +## Code Generation Drivers + +```{eval-rst} +.. module:: pystencils.codegen.driver + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/entire_class.rst + + DefaultKernelCreationDriver + +.. autosummary:: + :toctree: generated + :nosignatures: + + get_driver +``` + +## Output Code Objects + +```{eval-rst} +.. currentmodule:: pystencils.codegen + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: autosummary/entire_class.rst + + Kernel + GpuKernel + Parameter + GpuThreadsRange +``` diff --git a/docs/source/api/codegen.rst b/docs/source/api/codegen.rst deleted file mode 100644 index 1fb83fe5f..000000000 --- a/docs/source/api/codegen.rst +++ /dev/null @@ -1,94 +0,0 @@ -Code Generation -=============== - -.. module:: pystencils.codegen - -Invocation ----------- - -.. autosummary:: - :toctree: generated - :nosignatures: - - create_kernel - -Configuration -------------- - -.. module:: pystencils.codegen.config - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: autosummary/recursive_class.rst - - CreateKernelConfig - CpuOptions - OpenMpOptions - VectorizationOptions - GpuOptions - SyclOptions - -.. autosummary:: - :toctree: generated - :nosignatures: - - AUTO - -.. dropdown:: Configuration System Implementation Details - - .. autosummary:: - :toctree: generated - :nosignatures: - :template: autosummary/entire_class.rst - - Option - BasicOption - Category - ConfigBase - - -Target Specification --------------------- - -.. module:: pystencils.codegen.target - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: autosummary/recursive_class.rst - - Target - -Code Generation Drivers ------------------------ - -.. module:: pystencils.codegen.driver - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: autosummary/entire_class.rst - - DefaultKernelCreationDriver - -.. autosummary:: - :toctree: generated - :nosignatures: - - get_driver - -Output Code Objects -------------------- - -.. currentmodule:: pystencils.codegen - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: autosummary/entire_class.rst - - Kernel - GpuKernel - Parameter - GpuThreadsRange diff --git a/docs/source/migration.md b/docs/source/migration.md index c3cb17d0f..bb4a2cffb 100644 --- a/docs/source/migration.md +++ b/docs/source/migration.md @@ -48,12 +48,14 @@ cfg.ghost_layers = 2 for all index calculations and loop counters. - *CPU Optimization Options:* Should now be set via the {any}`cpu <CpuOptions>` option category and its subcategories. -.. dropdown:: Deprecated options of `CreateKernelConfig` +:::{dropdown} Deprecated options of `CreateKernelConfig` - - ``data_type``: Use ``default_dtype`` instead - - ``cpu_openmp``: Set OpenMP-Options in the `cpu.openmp <OpenMpOptions>` category instead. - - ``cpu_vectorize_info``: Set vectorization options in the `cpu.vectorize <VectorizationOptions>` category instead - - ``gpu_indexing_params``: Set GPU indexing options in the `gpu <GpuOptions>` category instead +- ``data_type``: Use ``default_dtype`` instead +- ``cpu_openmp``: Set OpenMP-Options in the `cpu.openmp <OpenMpOptions>` category instead. +- ``cpu_vectorize_info``: Set vectorization options in the `cpu.vectorize <VectorizationOptions>` category instead +- ``gpu_indexing_params``: Set GPU indexing options in the `gpu <GpuOptions>` category instead + +::: ### Type Checking diff --git a/src/pystencils/backend/kernelcreation/iteration_space.py b/src/pystencils/backend/kernelcreation/iteration_space.py index f7425c06b..313377fd8 100644 --- a/src/pystencils/backend/kernelcreation/iteration_space.py +++ b/src/pystencils/backend/kernelcreation/iteration_space.py @@ -453,11 +453,7 @@ def create_full_iteration_space( assert not ctx.fields.index_fields - if not ( - (ghost_layers is not None) - or (iteration_slice is not None) - or infer_ghost_layers - ): + if (ghost_layers is None) and (iteration_slice is None) and not infer_ghost_layers: raise ValueError( "One argument of `ghost_layers`, `iteration_slice`, and `infer_ghost_layers` must be set." ) diff --git a/src/pystencils/codegen/config.py b/src/pystencils/codegen/config.py index cb457f673..cbb3f4f32 100644 --- a/src/pystencils/codegen/config.py +++ b/src/pystencils/codegen/config.py @@ -82,17 +82,17 @@ class Option(Generic[Option_T, Arg_T]): def is_set(self, obj) -> bool: return getattr(obj, self._lookup, None) is not None - def __set_name__(self, owner, name: str): + def __set_name__(self, owner: ConfigBase, name: str): self._name = name self._lookup = f"_{name}" - def __get__(self, obj, objtype=None) -> Option_T | None: + def __get__(self, obj: ConfigBase, objtype: type[ConfigBase] | None = None) -> Option_T | None: if obj is None: return None return getattr(obj, self._lookup, None) - def __set__(self, obj, arg: Arg_T | None): + def __set__(self, obj: ConfigBase, arg: Arg_T | None): if arg is not None and self._validator is not None: value = self._validator(obj, arg) else: @@ -190,17 +190,17 @@ class Category(Generic[Category_T]): def __init__(self, default: Category_T): self._default = default - def __set_name__(self, owner, name: str): + def __set_name__(self, owner: ConfigBase, name: str): self._name = name self._lookup = f"_{name}" - def __get__(self, obj, objtype=None) -> Category_T: + def __get__(self, obj: ConfigBase, objtype: type[ConfigBase] | None = None) -> Category_T: if obj is None: return self._default return cast(Category_T, getattr(obj, self._lookup, None)) - def __set__(self, obj, cat: Category_T): + def __set__(self, obj: ConfigBase, cat: Category_T): setattr(obj, self._lookup, cat.copy()) @@ -208,12 +208,7 @@ class _AUTO_TYPE: ... # noqa: E701 AUTO = _AUTO_TYPE() -"""Special value that can be passed to some options for invoking automatic behaviour. - -Currently, these options permit `AUTO`: - -- `ghost_layers <CreateKernelConfig.ghost_layers>` -""" +"""Special value that can be passed to some options for invoking automatic behaviour.""" @dataclass diff --git a/tests/runtime/test_data/datahandling_save_test.npz b/tests/runtime/test_data/datahandling_save_test.npz index d363a8a0aba1bb78a06314a19b887eb4c4975334..486c7ee74d4421d563c3b1c2e3739d8db6308b07 100644 GIT binary patch literal 420 zcmWIWW@Zs#U|`??Vnv4TVm_%zAd7*Ofq|VtgrT@7Sud}kl953GECiAPO9ScIZ^U0o z3!FR=a4cZ$yh%}WVwU7BU6409ZQ;7b3+7FW4+)wwLwtVxlu2Ad{F++6R|5m|&w1#0 zgl-SIU~n-Ih&Eig61XB%LAb!M;UN3(z^W;7OWiZstS&PI;B`?-amfI<h>=Nz8CNKQ m0tEsZ7(p~N%mTcL)iepH3F@r?Z&o&t8B9Q!1*BELCISH25ot;Q literal 410 zcmWIWW@Zs#U|`??Vnv4TVm_%5Ad7*Ofq|VtgrT@7Sud}kl953GECiAPO9ScIZ^U0o z3!FR=a4cZ$yh%}WVwU7BU6409ZQ;7b3+7FW4+)wwLwtVxlu2Ad{F++6tX$&hDq>5R zc1o#PaXF-{T)8-4wS(G&B!*`GZ;QWZ*n0I}`m&5M0Iy?Gic9G07)B-$W?W$d3JM5l iU<A?7kP7f7R#Puf6Vyim-mGjOGnjxd3rI_WO#}cXWnnh} -- GitLab