Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
pystencils-sfg
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pycodegen
pystencils-sfg
Commits
53e73419
Commit
53e73419
authored
4 months ago
by
Frederik Hennig
Browse files
Options
Downloads
Patches
Plain Diff
finish documentation section
parent
0b493f17
No related branches found
No related tags found
1 merge request
!23
Introduce field and vector extraction protocols
Pipeline
#74892
passed
4 months ago
Stage: Code Quality
Stage: Tests
Stage: Documentation
Stage: deploy
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
docs/source/usage/api_modelling.md
+85
-17
85 additions, 17 deletions
docs/source/usage/api_modelling.md
docs/source/usage/how_to_composer.md
+2
-2
2 additions, 2 deletions
docs/source/usage/how_to_composer.md
with
87 additions
and
19 deletions
docs/source/usage/api_modelling.md
+
85
−
17
View file @
53e73419
...
...
@@ -7,6 +7,21 @@ kernelspec:
(how_to_cpp_api_modelling)=
# How To Reflect C++ APIs
```
{code-cell} ipython3
:tags: [remove-cell]
from __future__ import annotations
import sys
from pathlib import Path
mockup_path = Path("../_util").resolve()
sys.path.append(str(mockup_path))
from sfg_monkeypatch import DocsPatchedGenerator # monkeypatch SFG for docs
from pystencilssfg import SourceFileGenerator
```
Pystencils-SFG is designed to help you generate C++ code that interfaces with pystencils on the one side,
and with your handwritten code on the other side.
This requires that the C++ classes and APIs of your framework or application be represented within the SFG system.
...
...
@@ -253,7 +268,11 @@ def _extract_stride(self, coordinate: int) -> AugExpr | None: ...
The first, `_extract_ptr`, must return an expression that evaluates
to the base pointer of the field's memory buffer.
This pointer has to point at the field entry which pystencils accesses
at all-zero index and offsets.
at all-zero index and offsets (see [](#note-on-ghost-layers)).
The other two, when called with a coordinate $c \ge 0$, shall return
the size and linearization stride of the field in that direction.
If the coordinate is equal or larger than the field's dimensionality,
return `None` instead.
### Sample Field API Reflection
...
...
@@ -265,9 +284,9 @@ and exposes its data pointer, shape, and strides through public methods:
template
<
std::floating_point
ElemType
,
size_t
DIM
>
class MyField {
public:
size_t shape(size_t coord);
size_t stride(size_t coord);
ElemType
*
data();
size_t
get_
shape(size_t coord);
size_t
get_
stride(size_t coord);
ElemType
*
data
_ptr
();
}
```
...
...
@@ -297,40 +316,89 @@ class MyField(lang.CppClass, SupportsFieldExtraction):
) -> None:
self._elem_type = elem_type
self._dim = dim
super().__init__(ElemType=
d
type, DIM=dim, **kwargs)
super().__init__(ElemType=
elem_
type, DIM=dim, **kwargs)
# Reflection of Public Methods
def shape(self, coord: int | lang.AugExpr) -> lang.AugExpr:
return lang.AugExpr.format("{}.shape({})", self, coord)
def
get_
shape(self, coord: int | lang.AugExpr) -> lang.AugExpr:
return lang.AugExpr.format("{}.
get_
shape({})", self, coord)
def stride(self, coord: int | lang.AugExpr) -> lang.AugExpr:
return lang.AugExpr.format("{}.stride({})", self, coord)
def
get_
stride(self, coord: int | lang.AugExpr) -> lang.AugExpr:
return lang.AugExpr.format("{}.
get_
stride({})", self, coord)
def data(self) -> lang.AugExpr:
return lang.AugExpr.format("{}.data()", self
, coord
)
def data
_ptr
(self) -> lang.AugExpr:
return lang.AugExpr.format("{}.data
_ptr
()", self)
# Field Extraction Protocol that uses the above interface
def _extract_ptr(self) -> lang.AugExpr:
return self.data()
return self.data
_ptr
()
def _extract_size(self, coordinate: int) -> lang.AugExpr | None:
if coordinate > self._dim:
return None
else:
return self.shape(coordinate)
return self.
get_
shape(coordinate)
def _extract_stride(self, coordinate: int) -> lang.AugExpr | None:
if coordinate > self._dim:
return None
else:
return self.stride(coordinate)
return self.get_stride(coordinate)
```
Our custom field reflection is now ready to be used.
The following generator script demonstrates what code is generated when an instance of `MyField`
is passed to `sfg.map_field`:
```
{code-cell} ipython3
import pystencils as ps
from pystencilssfg.lang.cpp import std
with SourceFileGenerator() as sfg:
# Create symbolic fields
f = ps.fields("f: double[3D]")
f_myfield = MyField(f.dtype, f.ndim, ref=True).var(f.name)
# Create the kernel
asm = ps.Assignment(f(0), 2 * f(0))
khandle = sfg.kernels.create(asm)
# Create the wrapper function
sfg.function("invoke")(
sfg.map_field(f, f_myfield),
sfg.call(khandle)
)
```
### Add a Factory Function
In the above example, an instance of `MyField` representing the field `f` is created by the
slightly verbose expression `MyField(f.dtype, f.ndim, ref=True).var(f.name)`.
Having to write this sequence every time, for every field, introduces unnecessary
cognitive load and lots of potential sources of error.
Whenever it is possible to create a field reflection using just information contained in a
pystencils {any}`Field <pystencils.field.Field>` object,
the API reflection should therefore implement a factory method `from_field`:
```
{code-cell} ipython3
class MyField(lang.CppClass, SupportsFieldExtraction):
...
@classmethod
def from_field(cls, field: ps.Field, const: bool = False, ref: bool = False) -> MyField:
return cls(f.dtype, f.ndim, const=const, ref=ref).var(f.name)
```
:::{admonition} To Do
The above signature is idiomatic for `from_field`, and you should stick to it as far as possible.
We can now use it inside the generator script:
Demonstrate in a generator script
:::
```
{code-block} python
f = ps.fields("f: double[3D]")
f_myfield = MyField.from_field(f)
```
(note-on-ghost-layers)=
### A Note on Ghost Layers
Some care has to be taken when reflecting data structures that model the notion
...
...
This diff is collapsed.
Click to expand it.
docs/source/usage/how_to_composer.md
+
2
−
2
View file @
53e73419
...
...
@@ -352,8 +352,8 @@ computing landscape, including [Kokkos Views][kokkos_view], [C++ std::mdspan][md
[
SYCL buffers
][
sycl_buffer
]
, and many framework-specific custom-built classes.
Using the protocols behind {any}
`sfg.map_field <SfgBasicComposer.map_field>`
,
it is possible to automatically emit code
that extracts the indexing information required by a kernel from any of these classes
-
provided
a suitable API reflection is available.
that extracts the indexing information required by a kernel from any of these classes
,
as long as
a suitable API reflection is available.
:::{seealso}
[](
#field_data_structure_reflection
)
for instructions on how to set up field API
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment