xobjects 0.5.15__tar.gz → 0.6.1__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.
Files changed (58) hide show
  1. {xobjects-0.5.15/xobjects.egg-info → xobjects-0.6.1}/PKG-INFO +1 -1
  2. {xobjects-0.5.15 → xobjects-0.6.1}/pyproject.toml +1 -1
  3. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_kernel.py +39 -0
  4. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_struct.py +32 -0
  5. xobjects-0.6.1/tests/test_to_dict.py +319 -0
  6. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/__init__.py +2 -2
  7. xobjects-0.6.1/xobjects/_version.py +1 -0
  8. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/array.py +18 -5
  9. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/context_cpu.py +4 -2
  10. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/context_cupy.py +2 -0
  11. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/context_pyopencl.py +2 -0
  12. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/hybrid_class.py +5 -13
  13. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/ref.py +9 -2
  14. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/struct.py +45 -2
  15. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/test_helpers.py +2 -1
  16. {xobjects-0.5.15 → xobjects-0.6.1/xobjects.egg-info}/PKG-INFO +1 -1
  17. xobjects-0.5.15/tests/test_to_dict.py +0 -41
  18. xobjects-0.5.15/xobjects/_version.py +0 -1
  19. {xobjects-0.5.15 → xobjects-0.6.1}/Architecture.md +0 -0
  20. {xobjects-0.5.15 → xobjects-0.6.1}/LICENSE +0 -0
  21. {xobjects-0.5.15 → xobjects-0.6.1}/MANIFEST.in +0 -0
  22. {xobjects-0.5.15 → xobjects-0.6.1}/README.md +0 -0
  23. {xobjects-0.5.15 → xobjects-0.6.1}/setup.cfg +0 -0
  24. {xobjects-0.5.15 → xobjects-0.6.1}/setup.py +0 -0
  25. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_align.py +0 -0
  26. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_array.py +0 -0
  27. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_buffer.py +0 -0
  28. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_capi.py +0 -0
  29. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_chunk.py +0 -0
  30. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_common.py +0 -0
  31. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_context_opencl.py +0 -0
  32. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_hybrid_class.py +0 -0
  33. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_linked_array.py +0 -0
  34. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_nplike_arrays.py +0 -0
  35. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_ref.py +0 -0
  36. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_scalars.py +0 -0
  37. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_shared_memory.py +0 -0
  38. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_strides.py +0 -0
  39. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_string.py +0 -0
  40. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_typeutils.py +0 -0
  41. {xobjects-0.5.15 → xobjects-0.6.1}/tests/test_unionref.py +0 -0
  42. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/_patch_pyopencl_array.py +0 -0
  43. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/capi.py +0 -0
  44. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/context.py +0 -0
  45. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/general.py +0 -0
  46. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/headers/atomicadd.h +0 -0
  47. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/headers/common.h +0 -0
  48. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/linkedarray.py +0 -0
  49. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/scalar.py +0 -0
  50. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/specialize_source.py +0 -0
  51. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/string.py +0 -0
  52. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/typeutils.py +0 -0
  53. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects/union.py +0 -0
  54. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects.egg-info/SOURCES.txt +0 -0
  55. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects.egg-info/dependency_links.txt +0 -0
  56. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects.egg-info/entry_points.txt +0 -0
  57. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects.egg-info/requires.txt +0 -0
  58. {xobjects-0.5.15 → xobjects-0.6.1}/xobjects.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.5.15
3
+ Version: 0.6.1
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -44,7 +44,7 @@ include = '\.pyi?$'
44
44
  [project.entry-points.xobjects]
45
45
  include = "xobjects"
46
46
 
47
- [pytest]
47
+ [tool.pytest]
48
48
  markers = [
49
49
  "context_dependent: marks test as one that depends on the execution context",
50
50
  ]
@@ -44,6 +44,45 @@ double my_mul(const int n, const double* x1,
44
44
  assert y == 285.0
45
45
 
46
46
 
47
+ @for_all_test_contexts
48
+ def test_kernel_pointer_none_maps_to_null(test_context):
49
+ src_code = """
50
+ #include "xobjects/headers/common.h"
51
+
52
+ GPUKERN void ptr_is_null(
53
+ GPUGLMEM const double* x,
54
+ GPUGLMEM double* out
55
+ ) {
56
+ VECTORIZE_OVER(tid, 1)
57
+ out[tid] = x == NULL ? 1.0 : 0.0;
58
+ END_VECTORIZE
59
+ }
60
+ """
61
+
62
+ kernel_descriptions = {
63
+ "ptr_is_null": xo.Kernel(
64
+ args=[
65
+ xo.Arg(xo.Float64, pointer=True, const=True, name="x"),
66
+ xo.Arg(xo.Float64, pointer=True, name="out"),
67
+ ],
68
+ n_threads=1,
69
+ )
70
+ }
71
+
72
+ test_context.add_kernels(
73
+ sources=[src_code],
74
+ kernels=kernel_descriptions,
75
+ save_source_as=None,
76
+ compile=True,
77
+ )
78
+
79
+ out_dev = test_context.zeros(1, dtype=np.float64)
80
+ test_context.kernels.ptr_is_null(x=None, out=out_dev)
81
+ out_host = test_context.nparray_from_context_array(out_dev)
82
+
83
+ assert out_host[0] == 1.0
84
+
85
+
47
86
  @for_all_test_contexts
48
87
  def test_kernels(test_context):
49
88
  src_code = """
@@ -333,6 +333,8 @@ def test_compile_kernels_only_if_needed(tmp_path, mocker):
333
333
  def myfun(self):
334
334
  return self._context.kernels.myfun(tc=self)
335
335
 
336
+ assert TestClass._kernels["myfun"].args[0].atype is TestClass._XoStruct
337
+
336
338
  tc = TestClass(x=3, y=4, _context=test_context)
337
339
  tc.compile_kernels(only_if_needed=True)
338
340
  assert tc.myfun() == 12
@@ -343,3 +345,33 @@ def test_compile_kernels_only_if_needed(tmp_path, mocker):
343
345
  tc.compile_kernels(only_if_needed=True)
344
346
  assert tc.myfun() == 35
345
347
  cffi_compile.assert_not_called()
348
+
349
+
350
+ @requires_context("ContextCpu")
351
+ def test_thisclass_placeholder_on_struct():
352
+ test_context = xo.ContextCpu()
353
+
354
+ class TestStruct(xo.Struct):
355
+ x = xo.Float64
356
+ y = xo.Float64
357
+
358
+ _extra_c_sources = ["""
359
+ /*gpufun*/ double myfun(TestStruct tc){
360
+ double x = TestStruct_get_x(tc);
361
+ double y = TestStruct_get_y(tc);
362
+ return x * y;
363
+ }
364
+ """]
365
+ _kernels = {
366
+ "myfun": xo.Kernel(
367
+ args=[xo.Arg(xo.ThisClass, name="tc")],
368
+ ret=xo.Arg(xo.Float64),
369
+ ),
370
+ }
371
+
372
+ assert TestStruct._kernels["myfun"].args[0].atype is TestStruct
373
+
374
+ ts = TestStruct(x=3, y=4, _context=test_context)
375
+ ts.compile_kernels()
376
+
377
+ assert test_context.kernels.myfun(tc=ts) == 12
@@ -0,0 +1,319 @@
1
+ import xobjects as xo
2
+
3
+
4
+ def test_to_dict():
5
+ class A(xo.Struct):
6
+ a = xo.Float64[:]
7
+ b = xo.Int64
8
+
9
+ class Uref(xo.UnionRef):
10
+ _reftypes = (A,)
11
+
12
+ x = A(a=[2, 3], b=1)
13
+ u = Uref(x)
14
+ v = Uref(*u._to_dict())
15
+
16
+ assert v.get().a[0] == 2
17
+ assert v.get().a[1] == 3
18
+ assert v.get().b == 1
19
+
20
+
21
+ def test_to_dict_array():
22
+ class A(xo.Struct):
23
+ a = xo.Float64[:]
24
+
25
+ class B(xo.Struct):
26
+ c = xo.Float64[:]
27
+ d = xo.Int64
28
+
29
+ class Uref(xo.UnionRef):
30
+ _reftypes = A, B
31
+
32
+ AUref = Uref[:]
33
+
34
+ a = AUref(10)
35
+ a[1] = A(a=[3])
36
+ a[5] = B(c=2, d=1)
37
+
38
+ b = AUref(a._to_dict())
39
+
40
+ assert b[1].a[0] == 3
41
+ assert b[5].d == 1
42
+
43
+
44
+ def test_to_dict_unionref_none_roundtrip():
45
+ class A(xo.Struct):
46
+ x = xo.Int64
47
+
48
+ class B(xo.Struct):
49
+ y = xo.Int64
50
+
51
+ class Uref(xo.UnionRef):
52
+ _reftypes = (A, B)
53
+
54
+ uref = Uref(None)
55
+
56
+ assert uref._to_dict() is None
57
+ rebuilt = Uref(uref._to_dict())
58
+ assert rebuilt.get() is None
59
+
60
+
61
+ def test_to_dict_array_of_unionrefs_with_mixed_none_and_values():
62
+ class A(xo.Struct):
63
+ x = xo.Int64
64
+
65
+ class B(xo.Struct):
66
+ y = xo.Int64
67
+
68
+ class Uref(xo.UnionRef):
69
+ _reftypes = (A, B)
70
+
71
+ array_type = Uref[:]
72
+ items = array_type(4)
73
+ items[1] = A(x=5)
74
+ items[3] = B(y=9)
75
+
76
+ expected = [
77
+ None,
78
+ ("A", {"x": 5}),
79
+ None,
80
+ ("B", {"y": 9}),
81
+ ]
82
+
83
+ assert items._to_dict() == expected
84
+ rebuilt = array_type(items._to_dict())
85
+ assert rebuilt._to_dict() == expected
86
+
87
+
88
+ def test_to_dict_array_multidimensional_static_shape():
89
+ array_type = xo.Float64[2, 3]
90
+ array = array_type([[1, 2, 3], [4, 5, 6]])
91
+
92
+ assert array._to_dict() == [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
93
+ rebuilt = array_type(array._to_dict())
94
+ assert rebuilt._to_dict() == array._to_dict()
95
+
96
+
97
+ def test_to_dict_array_multidimensional_dynamic_shape():
98
+ array_type = xo.Float64[:, :]
99
+ array = array_type(2, 3)
100
+ array[0, 0] = 1
101
+ array[0, 1] = 2
102
+ array[0, 2] = 3
103
+ array[1, 0] = 4
104
+ array[1, 1] = 5
105
+ array[1, 2] = 6
106
+
107
+ assert array._to_dict() == [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
108
+ rebuilt = array_type(array._to_dict())
109
+ assert rebuilt._to_dict() == array._to_dict()
110
+
111
+
112
+ def test_to_dict_array_of_structs():
113
+ class Item(xo.Struct):
114
+ value = xo.Int64
115
+ coords = xo.Float64[2]
116
+
117
+ array_type = Item[:]
118
+ items = array_type(
119
+ [
120
+ {"value": 3, "coords": [1, 2]},
121
+ {"value": 7, "coords": [4, 5]},
122
+ ]
123
+ )
124
+
125
+ assert items._to_dict() == [
126
+ {"value": 3, "coords": [1.0, 2.0]},
127
+ {"value": 7, "coords": [4.0, 5.0]},
128
+ ]
129
+ rebuilt = array_type(items._to_dict())
130
+ assert rebuilt._to_dict() == items._to_dict()
131
+
132
+
133
+ def test_to_dict_multidimensional_array_of_structs():
134
+ class Item(xo.Struct):
135
+ value = xo.Int64
136
+ coords = xo.Float64[2]
137
+
138
+ array_type = Item[:, :]
139
+ items = array_type(
140
+ [
141
+ [
142
+ {"value": 1, "coords": [1, 2]},
143
+ {"value": 2, "coords": [3, 4]},
144
+ ],
145
+ [
146
+ {"value": 3, "coords": [5, 6]},
147
+ {"value": 4, "coords": [7, 8]},
148
+ ],
149
+ ]
150
+ )
151
+
152
+ expected = [
153
+ [
154
+ {"value": 1, "coords": [1.0, 2.0]},
155
+ {"value": 2, "coords": [3.0, 4.0]},
156
+ ],
157
+ [
158
+ {"value": 3, "coords": [5.0, 6.0]},
159
+ {"value": 4, "coords": [7.0, 8.0]},
160
+ ],
161
+ ]
162
+
163
+ assert items._to_dict() == expected
164
+ rebuilt = array_type(items._to_dict())
165
+ assert rebuilt._to_dict() == expected
166
+
167
+
168
+ def test_to_dict_multidimensional_array_of_structs_with_refs():
169
+ class Item(xo.Struct):
170
+ values = xo.Ref[xo.Float64[:]]
171
+ weight = xo.Int64
172
+
173
+ array_type = Item[:, :]
174
+ items = array_type(
175
+ [
176
+ [
177
+ {"values": [1, 2], "weight": 3},
178
+ {"values": [4, 5, 6], "weight": 7},
179
+ ],
180
+ [
181
+ {"values": [8], "weight": 9},
182
+ {"values": [10, 11], "weight": 12},
183
+ ],
184
+ ]
185
+ )
186
+
187
+ expected = [
188
+ [
189
+ {"values": [1.0, 2.0], "weight": 3},
190
+ {"values": [4.0, 5.0, 6.0], "weight": 7},
191
+ ],
192
+ [
193
+ {"values": [8.0], "weight": 9},
194
+ {"values": [10.0, 11.0], "weight": 12},
195
+ ],
196
+ ]
197
+
198
+ assert items._to_dict() == expected
199
+ rebuilt = array_type(items._to_dict())
200
+ assert rebuilt._to_dict() == expected
201
+
202
+
203
+ def test_to_dict_struct_with_unionref_field():
204
+ class A(xo.Struct):
205
+ a = xo.Float64[:]
206
+ b = xo.Int64
207
+
208
+ class B(xo.Struct):
209
+ c = xo.Float64[:]
210
+ d = xo.Int64
211
+
212
+ class Uref(xo.UnionRef):
213
+ _reftypes = (A, B)
214
+
215
+ class Item(xo.Struct):
216
+ ref = Uref
217
+ weight = xo.Int64
218
+
219
+ item = Item(ref=("A", {"a": [1, 2], "b": 3}), weight=11)
220
+
221
+ expected = {
222
+ "ref": ("A", {"a": [1.0, 2.0], "b": 3}),
223
+ "weight": 11,
224
+ }
225
+
226
+ assert item._to_dict() == expected
227
+ rebuilt = Item(item._to_dict())
228
+ assert rebuilt._to_dict() == expected
229
+
230
+
231
+ def test_to_dict_struct_containing_array_of_structs_with_unionref_fields():
232
+ class A(xo.Struct):
233
+ a = xo.Float64[:]
234
+ b = xo.Int64
235
+
236
+ class B(xo.Struct):
237
+ c = xo.Float64[:]
238
+ d = xo.Int64
239
+
240
+ class Uref(xo.UnionRef):
241
+ _reftypes = (A, B)
242
+
243
+ class Item(xo.Struct):
244
+ ref = Uref
245
+ weight = xo.Int64
246
+
247
+ class Container(xo.Struct):
248
+ items = Item[:]
249
+ tag = xo.Int64
250
+
251
+ container = Container(
252
+ items=[
253
+ {"ref": ("A", {"a": [1, 2], "b": 3}), "weight": 11},
254
+ {"ref": ("B", {"c": [4, 5], "d": 6}), "weight": 12},
255
+ ],
256
+ tag=99,
257
+ )
258
+
259
+ expected = {
260
+ "items": [
261
+ {"ref": ("A", {"a": [1.0, 2.0], "b": 3}), "weight": 11},
262
+ {"ref": ("B", {"c": [4.0, 5.0], "d": 6}), "weight": 12},
263
+ ],
264
+ "tag": 99,
265
+ }
266
+
267
+ assert container._to_dict() == expected
268
+ rebuilt = Container(container._to_dict())
269
+ assert rebuilt._to_dict() == expected
270
+
271
+
272
+ def test_to_dict_struct_containing_multidimensional_array_of_structs_with_unionref_fields():
273
+ class A(xo.Struct):
274
+ a = xo.Float64[:]
275
+ b = xo.Int64
276
+
277
+ class B(xo.Struct):
278
+ c = xo.Float64[:]
279
+ d = xo.Int64
280
+
281
+ class Uref(xo.UnionRef):
282
+ _reftypes = (A, B)
283
+
284
+ class Item(xo.Struct):
285
+ ref = Uref
286
+ weight = xo.Int64
287
+
288
+ class Container(xo.Struct):
289
+ items = Item[:, :]
290
+
291
+ container = Container(
292
+ items=[
293
+ [
294
+ {"ref": ("A", {"a": [1, 2], "b": 3}), "weight": 11},
295
+ {"ref": ("B", {"c": [4, 5], "d": 6}), "weight": 12},
296
+ ],
297
+ [
298
+ {"ref": ("B", {"c": [7], "d": 8}), "weight": 13},
299
+ {"ref": ("A", {"a": [9, 10], "b": 14}), "weight": 15},
300
+ ],
301
+ ]
302
+ )
303
+
304
+ expected = {
305
+ "items": [
306
+ [
307
+ {"ref": ("A", {"a": [1.0, 2.0], "b": 3}), "weight": 11},
308
+ {"ref": ("B", {"c": [4.0, 5.0], "d": 6}), "weight": 12},
309
+ ],
310
+ [
311
+ {"ref": ("B", {"c": [7.0], "d": 8}), "weight": 13},
312
+ {"ref": ("A", {"a": [9.0, 10.0], "b": 14}), "weight": 15},
313
+ ],
314
+ ]
315
+ }
316
+
317
+ assert container._to_dict() == expected
318
+ rebuilt = Container(container._to_dict())
319
+ assert rebuilt._to_dict() == expected
@@ -17,7 +17,7 @@ from .scalar import (
17
17
  )
18
18
  from .array import Array
19
19
  from .string import String
20
- from .struct import Struct, Field
20
+ from .struct import Struct, Field, ThisClass
21
21
  from .ref import Ref, UnionRef
22
22
 
23
23
  from .context_cpu import ContextCpu
@@ -30,7 +30,7 @@ from .specialize_source import specialize_source
30
30
 
31
31
  from .typeutils import context_default, get_a_buffer
32
32
 
33
- from .hybrid_class import JEncoder, HybridClass, MetaHybridClass, ThisClass
33
+ from .hybrid_class import JEncoder, HybridClass, MetaHybridClass
34
34
 
35
35
  from .linkedarray import BypassLinked
36
36
 
@@ -0,0 +1 @@
1
+ __version__ = "0.6.1"
@@ -15,6 +15,7 @@ from .typeutils import (
15
15
  _to_slot_size,
16
16
  default_conf,
17
17
  )
18
+ from .ref import is_ref, is_unionref
18
19
  from .scalar import Int64, is_scalar
19
20
 
20
21
  log = logging.getLogger(__name__)
@@ -693,16 +694,28 @@ class Array(metaclass=MetaArray):
693
694
  raise NameError("`_to_json` has been removed. Use `_to_dict` instead.")
694
695
 
695
696
  def _to_dict(self):
696
- out = []
697
- for v in self: # TODO does not support multidimensional arrays
697
+ if hasattr(self._itemtype, "_dtype"):
698
+ return self.to_nparray().tolist()
699
+
700
+ out = np.empty(dtype=object, shape=self._shape)
701
+
702
+ for idx in self._iter_index():
703
+ idx = idx if isinstance(idx, tuple) else (idx,)
704
+ v = self[idx]
705
+
698
706
  if hasattr(v, "_to_dict"):
699
707
  vdata = v._to_dict()
700
708
  else:
701
709
  vdata = v
702
- if self._has_refs and v is not None:
710
+
711
+ if v is not None and (
712
+ is_ref(self._itemtype) or is_unionref(self._itemtype)
713
+ ):
703
714
  vdata = (v.__class__.__name__, vdata)
704
- out.append(vdata)
705
- return out
715
+
716
+ out[idx] = vdata
717
+
718
+ return out.tolist()
706
719
 
707
720
 
708
721
  def is_index(atype):
@@ -316,7 +316,7 @@ class ContextCpu(XContext):
316
316
  if _forbid_compile:
317
317
  raise RuntimeError("Compilation is forbidden")
318
318
 
319
- if os.environ.get('XOBJECTS_FORBID_COMPILE'):
319
+ if os.environ.get("XOBJECTS_FORBID_COMPILE"):
320
320
  raise RuntimeError(
321
321
  "Compilation is forbidden by the environment variable "
322
322
  "XOBJECTS_FORBID_COMPILE"
@@ -466,7 +466,7 @@ class ContextCpu(XContext):
466
466
  return Path(output_file)
467
467
  finally:
468
468
  # Clean temp files
469
- if 'XOBJECTS_KEEP_BUILD_FILES' not in os.environ:
469
+ if "XOBJECTS_KEEP_BUILD_FILES" not in os.environ:
470
470
  files_to_remove = [
471
471
  module_name + ".c",
472
472
  module_name + ".o",
@@ -799,6 +799,8 @@ class KernelCpu:
799
799
 
800
800
  def to_function_arg(self, arg, value):
801
801
  if arg.pointer:
802
+ if value is None:
803
+ return self.ffi_interface.NULL
802
804
  if hasattr(arg.atype, "_dtype"): # it is numerical scalar
803
805
  if hasattr(value, "dtype"): # nparray
804
806
  slice_first_elem = value[tuple(value.ndim * [slice(0, 1)])]
@@ -661,6 +661,8 @@ class KernelCupy(object):
661
661
 
662
662
  def to_function_arg(self, arg, value):
663
663
  if arg.pointer:
664
+ if value is None:
665
+ return 0
664
666
  if hasattr(arg.atype, "_dtype"): # it is numerical scalar
665
667
  if hasattr(value, "dtype"): # nparray
666
668
  assert isinstance(value, cupy.ndarray)
@@ -491,6 +491,8 @@ class KernelPyopencl(object):
491
491
 
492
492
  def to_function_arg(self, arg, value):
493
493
  if arg.pointer:
494
+ if value is None:
495
+ return None
494
496
  if hasattr(arg.atype, "_dtype"): # it is numerical scalar
495
497
  if isinstance(value, cl.Buffer):
496
498
  return value
@@ -127,8 +127,11 @@ class MetaHybridClass(type):
127
127
 
128
128
  # Take xofields from data['_xofields'] or from bases
129
129
  xofields = _build_xofields_dict(bases, data)
130
+ xostruct_data = xofields.copy()
131
+ if "_kernels" in data.keys():
132
+ xostruct_data["_kernels"] = data["_kernels"]
130
133
 
131
- _XoStruct = type(_XoStruct_name, (Struct,), xofields)
134
+ _XoStruct = type(_XoStruct_name, (Struct,), xostruct_data)
132
135
 
133
136
  if "_rename" in data.keys():
134
137
  rename = data["_rename"]
@@ -184,14 +187,7 @@ class MetaHybridClass(type):
184
187
  new_class._XoStruct._depends_on.extend(data["_depends_on"])
185
188
 
186
189
  if "_kernels" in data.keys():
187
- kernels = data["_kernels"].copy()
188
- for nn, kk in kernels.items():
189
- for aa in kk.args:
190
- if aa.atype is ThisClass:
191
- aa.atype = new_class._XoStruct
192
- if isclass(aa.atype) and issubclass(aa.atype, HybridClass):
193
- aa.atype = aa.atype._XoStruct
194
- new_class._XoStruct._kernels.update(kernels)
190
+ new_class._kernels = new_class._XoStruct._kernels
195
191
 
196
192
  for ii, tt in enumerate(new_class._XoStruct._depends_on):
197
193
  if isclass(tt) and issubclass(tt, HybridClass):
@@ -422,7 +418,3 @@ class HybridClass(metaclass=MetaHybridClass):
422
418
  vvrepr = repr(vv)
423
419
  args.append(f"{fname}={vvrepr}")
424
420
  return f'{type(self).__name__}({", ".join(args)})'
425
-
426
-
427
- class ThisClass: # Place holder
428
- pass
@@ -9,7 +9,6 @@ import numpy as np
9
9
 
10
10
  from .typeutils import Info, dispatch_arg, allocate_on_buffer, default_conf
11
11
  from .scalar import Int64
12
- from .array import Array
13
12
 
14
13
  log = logging.getLogger(__name__)
15
14
 
@@ -72,6 +71,8 @@ class Ref(metaclass=MetaRef):
72
71
  return Info(size=self._size)
73
72
 
74
73
  def __getitem__(self, shape):
74
+ from .array import Array
75
+
75
76
  return Array.mk_arrayclass(self, shape)
76
77
 
77
78
  def _gen_data_paths(self, base=None):
@@ -182,7 +183,9 @@ class MetaUnionRef(type):
182
183
  else:
183
184
  if value is None:
184
185
  xobj = None
185
- elif isinstance(value, tuple):
186
+ elif isinstance(
187
+ value, (tuple, list)
188
+ ): # accept list as it might be coming from JSON
186
189
  if len(value) == 0:
187
190
  xobj = None
188
191
  typeid = None
@@ -213,6 +216,8 @@ class MetaUnionRef(type):
213
216
  Int64._array_to_buffer(buffer, offset, ref)
214
217
 
215
218
  def __getitem__(cls, shape):
219
+ from .array import Array
220
+
216
221
  return Array.mk_arrayclass(cls, shape)
217
222
 
218
223
  def _pre_init(cls, *arg, **kwargs):
@@ -298,6 +303,8 @@ class UnionRef(metaclass=MetaUnionRef):
298
303
 
299
304
  def _to_dict(self):
300
305
  v = self.get()
306
+ if v is None:
307
+ return None
301
308
  classname = v.__class__.__name__
302
309
  if hasattr(v, "_to_dict"):
303
310
  v = v._to_dict()
@@ -46,6 +46,7 @@ Field instance:
46
46
 
47
47
  """
48
48
 
49
+ import copy
49
50
  import logging
50
51
  from typing import Callable, Optional
51
52
 
@@ -63,10 +64,23 @@ from .scalar import Int64
63
64
  from .array import Array
64
65
  from .context import Source, Arg, Kernel
65
66
  from .context_cpu import ContextCpu
67
+ from .ref import is_unionref
66
68
 
67
69
  log = logging.getLogger(__name__)
68
70
 
69
71
 
72
+ class ThisClass: # Place holder
73
+ pass
74
+
75
+
76
+ def _resolve_kernel_arg_type(owner, atype):
77
+ if atype is ThisClass:
78
+ return owner
79
+ if hasattr(atype, "_XoStruct"):
80
+ return atype._XoStruct
81
+ return atype
82
+
83
+
70
84
  class Field:
71
85
  def __init__(
72
86
  self, ftype, default=None, readonly=False, default_factory=None
@@ -265,7 +279,26 @@ class MetaStruct(type):
265
279
  if "_kernels" not in data.keys():
266
280
  data["_kernels"] = {}
267
281
 
268
- return type.__new__(cls, name, bases, data)
282
+ new_class = type.__new__(cls, name, bases, data)
283
+ resolved_kernels = {}
284
+ for kernel_name, kernel in (new_class._kernels or {}).items():
285
+ resolved_kernel = copy.copy(kernel)
286
+ resolved_kernel.args = []
287
+ for arg in kernel.args:
288
+ resolved_arg = copy.copy(arg)
289
+ resolved_arg.atype = _resolve_kernel_arg_type(
290
+ new_class, resolved_arg.atype
291
+ )
292
+ resolved_kernel.args.append(resolved_arg)
293
+ if isinstance(kernel.ret, Arg):
294
+ resolved_ret = copy.copy(kernel.ret)
295
+ resolved_ret.atype = _resolve_kernel_arg_type(
296
+ new_class, resolved_ret.atype
297
+ )
298
+ resolved_kernel.ret = resolved_ret
299
+ resolved_kernels[kernel_name] = resolved_kernel
300
+ new_class._kernels = resolved_kernels
301
+ return new_class
269
302
 
270
303
  def __getitem__(cls, shape):
271
304
  return Array.mk_arrayclass(cls, shape)
@@ -374,6 +407,15 @@ class Struct(metaclass=MetaStruct):
374
407
  out = {}
375
408
  for field in self._fields:
376
409
  v = field.__get__(self)
410
+ if is_unionref(field.ftype):
411
+ if v is None:
412
+ out[field.name] = None
413
+ else:
414
+ classname = v.__class__.__name__
415
+ if hasattr(v, "_to_dict"):
416
+ v = v._to_dict()
417
+ out[field.name] = (classname, v)
418
+ continue
377
419
  if hasattr(v, "_to_dict"):
378
420
  v = v._to_dict()
379
421
  out[field.name] = v
@@ -485,6 +527,7 @@ class Struct(metaclass=MetaStruct):
485
527
  get_suitable_kernel,
486
528
  XSK_PREBUILT_KERNELS_LOCATION,
487
529
  )
530
+
488
531
  kernel_info = get_suitable_kernel(
489
532
  config={},
490
533
  tracker_element_classes=[],
@@ -496,7 +539,7 @@ class Struct(metaclass=MetaStruct):
496
539
  Print.suppress = _print_state
497
540
  if kernel_info:
498
541
  kernels = context.kernels_from_file(
499
- module_name=kernel_info['module_name'],
542
+ module_name=kernel_info["module_name"],
500
543
  containing_dir=XSK_PREBUILT_KERNELS_LOCATION,
501
544
  kernel_descriptions=cls._kernels,
502
545
  )
@@ -107,8 +107,9 @@ def fix_random_seed(seed: int):
107
107
 
108
108
  return decorator
109
109
 
110
+
110
111
  def skip_if_forbid_compile():
111
- if os.environ.get('XOBJECTS_FORBID_COMPILE'):
112
+ if os.environ.get("XOBJECTS_FORBID_COMPILE"):
112
113
  pytest.skip(
113
114
  "Compilation is forbidden by the environment variable "
114
115
  "XOBJECTS_FORBID_COMPILE"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.5.15
3
+ Version: 0.6.1
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -1,41 +0,0 @@
1
- import xobjects as xo
2
-
3
-
4
- def test_to_dict():
5
- class A(xo.Struct):
6
- a = xo.Float64[:]
7
- b = xo.Int64
8
-
9
- class Uref(xo.UnionRef):
10
- _reftypes = (A,)
11
-
12
- x = A(a=[2, 3], b=1)
13
- u = Uref(x)
14
- v = Uref(*u._to_dict())
15
-
16
- assert v.get().a[0] == 2
17
- assert v.get().a[1] == 3
18
- assert v.get().b == 1
19
-
20
-
21
- def test_to_dict_array():
22
- class A(xo.Struct):
23
- a = xo.Float64[:]
24
-
25
- class B(xo.Struct):
26
- c = xo.Float64[:]
27
- d = xo.Int64
28
-
29
- class Uref(xo.UnionRef):
30
- _reftypes = A, B
31
-
32
- AUref = Uref[:]
33
-
34
- a = AUref(10)
35
- a[1] = A(a=[3])
36
- a[5] = B(c=2, d=1)
37
-
38
- b = AUref(a._to_dict())
39
-
40
- assert b[1].a[0] == 3
41
- assert b[5].d == 1
@@ -1 +0,0 @@
1
- __version__ = "0.5.15"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes