xobjects 0.6.4__tar.gz → 0.6.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xobjects-0.6.6/.github/workflows/black.yml +10 -0
- xobjects-0.6.6/.gitignore +13 -0
- xobjects-0.6.6/.pre-commit-config.yaml +12 -0
- xobjects-0.6.6/.readthedocs.yaml +20 -0
- xobjects-0.6.6/PKG-INFO +404 -0
- xobjects-0.6.6/README.md +375 -0
- xobjects-0.6.6/contributors_and_credits.txt +7 -0
- xobjects-0.6.6/docs/Makefile +20 -0
- xobjects-0.6.6/docs/architecture/contexts.rst +164 -0
- xobjects-0.6.6/docs/architecture/types.rst +179 -0
- xobjects-0.6.6/docs/architecture.rst +16 -0
- xobjects-0.6.6/docs/conf.py +278 -0
- xobjects-0.6.6/docs/index.rst +79 -0
- xobjects-0.6.6/docs/make.bat +35 -0
- xobjects-0.6.6/docs/quickstart.rst +2 -0
- xobjects-0.6.6/docs/reference.rst +8 -0
- xobjects-0.6.6/examples/api_def.py +28 -0
- xobjects-0.6.6/examples/api_init.py +101 -0
- xobjects-0.6.6/examples/capi_example.c +100 -0
- xobjects-0.6.6/examples/check_assembly.c +101 -0
- xobjects-0.6.6/examples/ex_array_init_perf.py +42 -0
- xobjects-0.6.6/examples/ex_cpugpu.py +46 -0
- xobjects-0.6.6/examples/ex_nested_ref.py +49 -0
- xobjects-0.6.6/examples/ex_ref_cpu.py +52 -0
- xobjects-0.6.6/examples/ex_struct_cpu.py +39 -0
- xobjects-0.6.6/examples/ex_unionref_cpu.py +49 -0
- xobjects-0.6.6/examples/ex_unionref_method.py +120 -0
- xobjects-0.6.6/examples/init_soa.py +67 -0
- xobjects-0.6.6/examples/issue_with_two_pyopencl_contexts.py +18 -0
- xobjects-0.6.6/examples/kernel_basics/kernel_cffi.py +54 -0
- xobjects-0.6.6/examples/kernel_basics/kernel_cupy.py +42 -0
- xobjects-0.6.6/examples/kernel_basics/kernel_pyopencl.py +48 -0
- xobjects-0.6.6/examples/opencl_offsets.py +38 -0
- xobjects-0.6.6/examples/simple_kernel.py +59 -0
- xobjects-0.6.6/examples/sixtracklib.py +80 -0
- xobjects-0.6.6/mktest +1 -0
- xobjects-0.6.6/release.sh +20 -0
- xobjects-0.6.6/requirements.txt +3 -0
- xobjects-0.6.6/tests/__init__.py +4 -0
- xobjects-0.6.6/tests/notest_capi.py +208 -0
- xobjects-0.6.6/update_cprght_statement.py +49 -0
- xobjects-0.6.6/xobjects/_version.py +1 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/context_cpu.py +57 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/context_cupy.py +100 -12
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/headers/atomicadd.h +17 -1
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/struct.py +28 -15
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/test_helpers.py +33 -0
- xobjects-0.6.6/xobjects.egg-info/PKG-INFO +404 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects.egg-info/SOURCES.txt +39 -0
- xobjects-0.6.4/PKG-INFO +0 -39
- xobjects-0.6.4/README.md +0 -10
- xobjects-0.6.4/xobjects/_version.py +0 -1
- xobjects-0.6.4/xobjects.egg-info/PKG-INFO +0 -39
- {xobjects-0.6.4 → xobjects-0.6.6}/Architecture.md +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/LICENSE +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/MANIFEST.in +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/pyproject.toml +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/setup.cfg +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/setup.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_align.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_array.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_buffer.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_capi.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_chunk.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_common.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_context_opencl.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_hybrid_class.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_kernel.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_linked_array.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_nplike_arrays.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_ref.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_scalars.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_shared_memory.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_strides.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_string.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_struct.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_to_dict.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_typeutils.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/tests/test_unionref.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/__init__.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/_patch_pyopencl_array.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/array.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/capi.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/context.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/context_pyopencl.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/general.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/headers/common.h +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/hybrid_class.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/linkedarray.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/ref.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/scalar.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/specialize_source.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/string.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/typeutils.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects/union.py +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects.egg-info/dependency_links.txt +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects.egg-info/entry_points.txt +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects.egg-info/requires.txt +0 -0
- {xobjects-0.6.4 → xobjects-0.6.6}/xobjects.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.5.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-yaml
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- repo: https://github.com/psf/black
|
|
9
|
+
rev: 23.10.1
|
|
10
|
+
hooks:
|
|
11
|
+
- id: black
|
|
12
|
+
language_version: python3 # Should be a command that runs python3.6+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# .readthedocs.yaml
|
|
2
|
+
# Read the Docs configuration file
|
|
3
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
4
|
+
|
|
5
|
+
# Required
|
|
6
|
+
version: 2
|
|
7
|
+
|
|
8
|
+
# Build documentation in the docs/ directory with Sphinx
|
|
9
|
+
sphinx:
|
|
10
|
+
configuration: docs/conf.py
|
|
11
|
+
|
|
12
|
+
# Optionally build your docs in additional formats such as PDF
|
|
13
|
+
formats:
|
|
14
|
+
- pdf
|
|
15
|
+
|
|
16
|
+
# Optionally set the version of Python and requirements required to build your docs
|
|
17
|
+
python:
|
|
18
|
+
version: 3.7
|
|
19
|
+
# install:
|
|
20
|
+
# - requirements: docs/requirements.txt
|
xobjects-0.6.6/PKG-INFO
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xobjects
|
|
3
|
+
Version: 0.6.6
|
|
4
|
+
Summary: In-memory serialization and code generator for CPU and GPU
|
|
5
|
+
Home-page: https://xsuite.readthedocs.io/
|
|
6
|
+
Download-URL: https://pypi.python.org/pypi/xobjects
|
|
7
|
+
Author: Riccardo De Maria
|
|
8
|
+
Author-email: Riccardo De Maria <riccardo.de.maria@cern.ch>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Project-URL: Homepage, https://xsuite.readthedocs.io/
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/xsuite/xsuite/issues
|
|
12
|
+
Project-URL: Documentation, https://xsuite.readthedocs.io/
|
|
13
|
+
Project-URL: Source Code, https://github.com/xsuite/xobjects
|
|
14
|
+
Project-URL: Download, https://pypi.python.org/pypi/xobjects
|
|
15
|
+
Requires-Python: >=3.7
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: cffi
|
|
20
|
+
Requires-Dist: scipy
|
|
21
|
+
Provides-Extra: tests
|
|
22
|
+
Requires-Dist: pytest; extra == "tests"
|
|
23
|
+
Requires-Dist: pytest-mock; extra == "tests"
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: download-url
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
|
|
30
|
+
# Xobjects
|
|
31
|
+
|
|
32
|
+
Xobjects is a low-level Python package for defining structured data that can be
|
|
33
|
+
stored in CPU or GPU memory and accessed from generated C APIs. It is used by
|
|
34
|
+
the Xsuite ecosystem as the memory, serialization, and kernel-compilation layer,
|
|
35
|
+
but it can also be used directly when a project needs the same data layout from
|
|
36
|
+
Python and from compiled CPU, CUDA, or OpenCL code.
|
|
37
|
+
|
|
38
|
+
The main building blocks are:
|
|
39
|
+
|
|
40
|
+
- contexts, which select the execution and memory backend;
|
|
41
|
+
- buffers, which own memory allocated by a context;
|
|
42
|
+
- xobject data types, which describe the binary layout of scalars, arrays,
|
|
43
|
+
structures, strings, and references;
|
|
44
|
+
- kernels, which bind C-like source code to typed Python callables.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python -m pip install xobjects
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For local development:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git clone https://github.com/xsuite/xobjects.git
|
|
56
|
+
cd xobjects
|
|
57
|
+
python -m pip install -e ".[tests]"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
GPU contexts require their corresponding runtime packages and drivers:
|
|
61
|
+
|
|
62
|
+
- `ContextCpu` uses NumPy arrays and CFFI-compiled CPU kernels.
|
|
63
|
+
- `ContextCupy` uses CuPy arrays and CUDA kernels.
|
|
64
|
+
- `ContextPyopencl` uses PyOpenCL arrays and OpenCL kernels.
|
|
65
|
+
|
|
66
|
+
OpenMP support for CPU kernels requires an OpenMP-capable compiler and runtime
|
|
67
|
+
to be installed in the environment.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Development
|
|
71
|
+
|
|
72
|
+
Run the test suite with:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pytest tests
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Some tests and examples need optional GPU dependencies or a configured GPU
|
|
79
|
+
runtime. CPU-only development can still run the regular test subset that does
|
|
80
|
+
not require those contexts. Which tests are run is specified by the `XOBJECTS_TEST_CONTEXTS` flag. By default all contexts supported by the system are used, but this can be narrowed down, e.g. only the CPU serial and OpenMP tests will be run if `XOBJECTS_TEST_CONTEXTS=ContextCpu;ContextCpu:auto` is set.
|
|
81
|
+
|
|
82
|
+
## Contributing
|
|
83
|
+
|
|
84
|
+
The project uses Black for Python formatting and provides a pre-commit
|
|
85
|
+
configuration. Install the pre-commit hooks before committing:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install pre-commit
|
|
89
|
+
pre-commit install
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This will run formatting hooks on every commit.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Contexts and Buffers
|
|
96
|
+
|
|
97
|
+
A context represents where data lives and where kernels execute. A buffer is a
|
|
98
|
+
memory allocation owned by a context. Xobjects can allocate a new buffer
|
|
99
|
+
implicitly, or they can be placed explicitly in an existing buffer so that
|
|
100
|
+
several objects share one memory range.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import xobjects as xo
|
|
104
|
+
|
|
105
|
+
ctx = xo.ContextCpu()
|
|
106
|
+
buffer = ctx.new_buffer(capacity=1024)
|
|
107
|
+
|
|
108
|
+
class Point(xo.Struct):
|
|
109
|
+
x = xo.Float64
|
|
110
|
+
y = xo.Float64
|
|
111
|
+
|
|
112
|
+
p1 = Point(x=1.0, y=2.0, _context=ctx) # new buffer on ctx
|
|
113
|
+
p2 = Point(x=3.0, y=4.0, _buffer=buffer) # explicit buffer
|
|
114
|
+
|
|
115
|
+
print(p1._context)
|
|
116
|
+
print(p2._buffer is buffer)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If no `_context` or `_buffer` is supplied, Xobjects uses the default CPU
|
|
120
|
+
context. Passing `_context` creates a new buffer in that context. Passing
|
|
121
|
+
`_buffer` places the object in that buffer. Passing `_offset` is supported for
|
|
122
|
+
advanced use, but it must be used carefully because overlapping allocations can
|
|
123
|
+
corrupt objects in the same buffer.
|
|
124
|
+
|
|
125
|
+
Context arrays can be converted to and from NumPy arrays through the context:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import numpy as np
|
|
129
|
+
import xobjects as xo
|
|
130
|
+
|
|
131
|
+
ctx = xo.ContextCpu()
|
|
132
|
+
host = np.arange(8, dtype=np.float64)
|
|
133
|
+
|
|
134
|
+
on_context = ctx.nparray_to_context_array(host)
|
|
135
|
+
back_on_host = ctx.nparray_from_context_array(on_context)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
On GPU contexts, array attributes expose NumPy-like GPU arrays. Some NumPy
|
|
139
|
+
operations require an explicit copy back to host memory with
|
|
140
|
+
`nparray_from_context_array`.
|
|
141
|
+
|
|
142
|
+
`ContextCpu` can compile kernels with OpenMP support when it is created with
|
|
143
|
+
`omp_num_threads` different from `0`:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import xobjects as xo
|
|
147
|
+
|
|
148
|
+
ctx = xo.ContextCpu(omp_num_threads="auto") # let OpenMP choose the thread count
|
|
149
|
+
ctx = xo.ContextCpu(omp_num_threads=4) # use four OpenMP threads
|
|
150
|
+
ctx = xo.ContextCpu(omp_num_threads=0) # serial CPU execution
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Defining Data
|
|
154
|
+
|
|
155
|
+
Xobjects data types describe a stable binary layout. The same definition drives
|
|
156
|
+
Python accessors, buffer allocation, serialization helpers, and generated C
|
|
157
|
+
accessors.
|
|
158
|
+
|
|
159
|
+
### Scalars
|
|
160
|
+
|
|
161
|
+
Scalar types wrap fixed-width NumPy dtypes and C types:
|
|
162
|
+
|
|
163
|
+
- floating point: `Float64`, `Float32`;
|
|
164
|
+
- signed integers: `Int64`, `Int32`, `Int16`, `Int8`;
|
|
165
|
+
- unsigned integers: `UInt64`, `UInt32`, `UInt16`, `UInt8`.
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
import xobjects as xo
|
|
169
|
+
|
|
170
|
+
value = xo.Float64(3.5)
|
|
171
|
+
index = xo.Int64(7)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Arrays
|
|
175
|
+
|
|
176
|
+
Arrays are declared by indexing an item type:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
import xobjects as xo
|
|
180
|
+
|
|
181
|
+
Vector = xo.Float64[3] # fixed-length array
|
|
182
|
+
Series = xo.Float64[:] # one dynamic dimension
|
|
183
|
+
Matrix = xo.Float64[:, 3]
|
|
184
|
+
|
|
185
|
+
v = Vector([1.0, 2.0, 3.0])
|
|
186
|
+
s = Series([0.0, 1.0, 2.0, 3.0])
|
|
187
|
+
m = Matrix([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Arrays can contain scalars, structs, strings, and references. Fixed-size arrays
|
|
191
|
+
have a static footprint; dynamic arrays store size and layout metadata in the
|
|
192
|
+
buffer before their data.
|
|
193
|
+
|
|
194
|
+
### Structs
|
|
195
|
+
|
|
196
|
+
`Struct` classes define named fields stored together in one xobject.
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import xobjects as xo
|
|
200
|
+
|
|
201
|
+
class Particle(xo.Struct):
|
|
202
|
+
x = xo.Float64
|
|
203
|
+
px = xo.Float64
|
|
204
|
+
state = xo.Int64
|
|
205
|
+
|
|
206
|
+
particle = Particle(x=1.0, px=0.01, state=1)
|
|
207
|
+
particle.x += 0.5
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Fields can be scalars, arrays, nested structs, strings, `Ref` values, or
|
|
211
|
+
`UnionRef` values. Dynamic fields are stored through offsets inside the same
|
|
212
|
+
buffer-backed object.
|
|
213
|
+
|
|
214
|
+
### Hybrid Classes
|
|
215
|
+
|
|
216
|
+
`HybridClass` is a convenience layer for user-facing Python classes that keep
|
|
217
|
+
regular Python methods and attributes while storing selected fields in an
|
|
218
|
+
underlying xobject. The C-visible fields are declared in `_xofields`.
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
import xobjects as xo
|
|
222
|
+
|
|
223
|
+
class Record(xo.HybridClass):
|
|
224
|
+
_xofields = {
|
|
225
|
+
"value": xo.Float64,
|
|
226
|
+
"samples": xo.Float64[:],
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
def add_offset(self, offset):
|
|
230
|
+
self.value += offset
|
|
231
|
+
|
|
232
|
+
record = Record(value=1.5, samples=[0.0, 1.0, 2.0])
|
|
233
|
+
record.add_offset(0.5)
|
|
234
|
+
|
|
235
|
+
print(record.value)
|
|
236
|
+
print(record._xobject.samples)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The attributes listed in `_xofields` are backed by the same memory as
|
|
240
|
+
`record._xobject`. Array fields are exposed as NumPy or NumPy-like arrays on the
|
|
241
|
+
owning context.
|
|
242
|
+
|
|
243
|
+
### Strings and References
|
|
244
|
+
|
|
245
|
+
`String` stores a null-terminated variable-length string. `Ref(T)` stores a
|
|
246
|
+
reference to another object of type `T` in the same buffer. `UnionRef` stores a
|
|
247
|
+
reference that can point to one of several declared xobject types.
|
|
248
|
+
|
|
249
|
+
References are useful when multiple objects need to share data without copying
|
|
250
|
+
it. They must refer to objects in the same buffer because the stored value is a
|
|
251
|
+
relative offset.
|
|
252
|
+
|
|
253
|
+
## The C Programming Model
|
|
254
|
+
|
|
255
|
+
Every xobject type can generate C declarations and accessor functions for its
|
|
256
|
+
buffer layout. This lets kernel code read and write fields without hard-coding
|
|
257
|
+
offsets.
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
import xobjects as xo
|
|
261
|
+
|
|
262
|
+
class Point(xo.Struct):
|
|
263
|
+
x = xo.Float64
|
|
264
|
+
y = xo.Float64
|
|
265
|
+
|
|
266
|
+
print(Point._gen_c_api().source)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The generated API includes functions such as:
|
|
270
|
+
|
|
271
|
+
```c
|
|
272
|
+
double Point_get_x(const Point obj);
|
|
273
|
+
void Point_set_x(Point obj, double value);
|
|
274
|
+
double* Point_getp_x(Point obj);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
For array fields, the generated API also includes length, getter, setter, and
|
|
278
|
+
pointer helpers. For references and unions, it includes helpers that recover the
|
|
279
|
+
referenced object and type information.
|
|
280
|
+
|
|
281
|
+
Handwritten C code should use the generated accessors instead of assuming a
|
|
282
|
+
particular byte layout. That keeps the source portable across CPU, CUDA, and
|
|
283
|
+
OpenCL backends and across static or dynamic field layouts.
|
|
284
|
+
|
|
285
|
+
Xobjects provides portability macros through `xobjects/headers/common.h`, for
|
|
286
|
+
example `GPUFUN`, `GPUKERN`, `GPUGLMEM`, and `RESTRICT`.
|
|
287
|
+
|
|
288
|
+
## Defining Kernels
|
|
289
|
+
|
|
290
|
+
A kernel is a compiled function registered on a context. Its source is C-like
|
|
291
|
+
code and its Python-side signature is described with `xo.Kernel` and `xo.Arg`.
|
|
292
|
+
Xobjects uses the signature to generate the required type API, compile the
|
|
293
|
+
source for the selected backend, and expose the result through `context.kernels`.
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
import numpy as np
|
|
297
|
+
import xobjects as xo
|
|
298
|
+
|
|
299
|
+
ctx = xo.ContextCpu()
|
|
300
|
+
|
|
301
|
+
source = r"""
|
|
302
|
+
GPUKERN
|
|
303
|
+
void scale_and_add(const int32_t n,
|
|
304
|
+
const double a,
|
|
305
|
+
GPUGLMEM const double* x,
|
|
306
|
+
GPUGLMEM double* y)
|
|
307
|
+
{
|
|
308
|
+
VECTORIZE_OVER(i, n)
|
|
309
|
+
y[i] = a * x[i] + y[i];
|
|
310
|
+
END_VECTORIZE
|
|
311
|
+
}
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
kernels = {
|
|
315
|
+
"scale_and_add": xo.Kernel(
|
|
316
|
+
args=[
|
|
317
|
+
xo.Arg(xo.Int32, name="n"),
|
|
318
|
+
xo.Arg(xo.Float64, name="a"),
|
|
319
|
+
xo.Arg(xo.Float64, pointer=True, const=True, name="x"),
|
|
320
|
+
xo.Arg(xo.Float64, pointer=True, name="y"),
|
|
321
|
+
],
|
|
322
|
+
n_threads="n",
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
ctx.add_kernels(
|
|
327
|
+
sources=[source],
|
|
328
|
+
kernels=kernels,
|
|
329
|
+
extra_headers=['#include "xobjects/headers/common.h"'],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
x = ctx.nparray_to_context_array(np.arange(5, dtype=np.float64))
|
|
333
|
+
y = ctx.nparray_to_context_array(np.ones(5, dtype=np.float64))
|
|
334
|
+
|
|
335
|
+
ctx.kernels.scale_and_add(n=len(x), a=2.0, x=x, y=y)
|
|
336
|
+
print(ctx.nparray_from_context_array(y))
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Kernels can also take xobject structs or arrays as arguments:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
import xobjects as xo
|
|
343
|
+
|
|
344
|
+
class Point(xo.Struct):
|
|
345
|
+
x = xo.Float64
|
|
346
|
+
y = xo.Float64
|
|
347
|
+
|
|
348
|
+
source = r"""
|
|
349
|
+
GPUFUN
|
|
350
|
+
double norm2(Point p)
|
|
351
|
+
{
|
|
352
|
+
return Point_get_x(p) * Point_get_x(p)
|
|
353
|
+
+ Point_get_y(p) * Point_get_y(p);
|
|
354
|
+
}
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
kernels = {
|
|
358
|
+
"norm2": xo.Kernel(
|
|
359
|
+
args=[xo.Arg(Point, name="p")],
|
|
360
|
+
ret=xo.Arg(xo.Float64),
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
ctx = xo.ContextCpu()
|
|
365
|
+
ctx.add_kernels(
|
|
366
|
+
sources=[source],
|
|
367
|
+
kernels=kernels,
|
|
368
|
+
extra_headers=['#include "xobjects/headers/common.h"'],
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
point = Point(x=3.0, y=4.0, _context=ctx)
|
|
372
|
+
print(ctx.kernels.norm2(p=point))
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
For reusable classes, kernel descriptions can be attached to a `Struct` or
|
|
376
|
+
`HybridClass` through `_kernels` and compiled with `compile_kernels()`.
|
|
377
|
+
|
|
378
|
+
Loops written with `VECTORIZE_OVER` use the execution policy of the selected
|
|
379
|
+
context. On a serial CPU context they become regular `for` loops. On an OpenMP
|
|
380
|
+
CPU context they become OpenMP parallel loops. On GPU contexts they map to the
|
|
381
|
+
backend thread index.
|
|
382
|
+
|
|
383
|
+
## Moving and Copying
|
|
384
|
+
|
|
385
|
+
Hybrid objects support moving or copying data across buffers and contexts:
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
import xobjects as xo
|
|
389
|
+
|
|
390
|
+
class Item(xo.HybridClass):
|
|
391
|
+
_xofields = {
|
|
392
|
+
"value": xo.Float64,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
cpu_item = Item(value=1.0, _context=xo.ContextCpu())
|
|
396
|
+
|
|
397
|
+
other_cpu_ctx = xo.ContextCpu()
|
|
398
|
+
other_item = cpu_item.copy(_context=other_cpu_ctx)
|
|
399
|
+
cpu_item.move(_buffer=xo.context_default.new_buffer())
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Objects with references have stricter rules because references are buffer-local.
|
|
403
|
+
When using `Ref` or `UnionRef`, keep the referencing object and the referenced
|
|
404
|
+
object in the same buffer.
|