-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathceed_vector.py
487 lines (377 loc) · 15.3 KB
/
ceed_vector.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors
# All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
#
# SPDX-License-Identifier: BSD-2-Clause
#
# This file is part of CEED: http://github.com/ceed
from _ceed_cffi import ffi, lib
import tempfile
import numpy as np
import contextlib
from .ceed_constants import MEM_HOST, USE_POINTER, COPY_VALUES, NORM_2, scalar_types
# ------------------------------------------------------------------------------
class Vector():
"""Ceed Vector: storing and manipulating vectors."""
# Constructor
def __init__(self, ceed, size):
# CeedVector object
self._pointer = ffi.new("CeedVector *")
# Reference to Ceed
self._ceed = ceed
# libCEED call
err_code = lib.CeedVectorCreate(
self._ceed._pointer[0], size, self._pointer)
self._ceed._check_error(err_code)
# Destructor
def __del__(self):
# libCEED call
err_code = lib.CeedVectorDestroy(self._pointer)
self._ceed._check_error(err_code)
# Representation
def __repr__(self):
return "<CeedVector instance at " + hex(id(self)) + ">"
# String conversion for print() to stdout
def __str__(self):
"""View a Vector via print()."""
# libCEED call
fmt = ffi.new("char[]", "%f".encode('ascii'))
with tempfile.NamedTemporaryFile() as key_file:
with open(key_file.name, 'r+') as stream_file:
stream = ffi.cast("FILE *", stream_file)
err_code = lib.CeedVectorView(self._pointer[0], fmt, stream)
self._ceed._check_error(err_code)
stream_file.seek(0)
out_string = stream_file.read()
return out_string
# Copy the array from a vector into self
def copy_from(self, vec_source):
"""Copies the array of vec_source into the array of self.
Args:
*vector: the Vector to copy from"""
# libCEED call
err_code = lib.CeedVectorCopy(vec_source._pointer[0], self._pointer[0])
self._ceed._check_error(err_code)
# Set Vector's data array
def set_array(self, array, memtype=MEM_HOST, cmode=COPY_VALUES):
"""Set the array used by a Vector, freeing any previously allocated
array if applicable.
Args:
*array: Numpy or Numba array to be used
**memtype: memory type of the array being passed, default CEED_MEM_HOST
**cmode: copy mode for the array, default CEED_COPY_VALUES"""
# Store array reference if needed
if cmode == USE_POINTER:
self._array_reference = array
else:
self._array_reference = None
# Setup the numpy array for the libCEED call
array_pointer = ffi.new("CeedScalar *")
if memtype == MEM_HOST:
array_pointer = ffi.cast(
"CeedScalar *",
array.__array_interface__['data'][0])
else:
array_pointer = ffi.cast(
"CeedScalar *",
array.__cuda_array_interface__['data'][0])
# libCEED call
err_code = lib.CeedVectorSetArray(
self._pointer[0], memtype, cmode, array_pointer)
self._ceed._check_error(err_code)
# Get Vector's data array
def get_array(self, memtype=MEM_HOST):
"""Get read/write access to a Vector via the specified memory type.
Args:
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
*array: Numpy or Numba array"""
# Retrieve the length of the array
length_pointer = ffi.new("CeedSize *")
err_code = lib.CeedVectorGetLength(self._pointer[0], length_pointer)
self._ceed._check_error(err_code)
# Setup the pointer's pointer
array_pointer = ffi.new("CeedScalar **")
# libCEED call
err_code = lib.CeedVectorGetArray(
self._pointer[0], memtype, array_pointer)
self._ceed._check_error(err_code)
# Return array created from buffer
if memtype == MEM_HOST:
# Create buffer object from returned pointer
buff = ffi.buffer(
array_pointer[0],
ffi.sizeof("CeedScalar") *
length_pointer[0])
# return Numpy array
return np.frombuffer(buff, dtype=scalar_types[lib.CEED_SCALAR_TYPE])
else:
# CUDA array interface
# https://numba.pydata.org/numba-doc/latest/cuda/cuda_array_interface.html
import numba.cuda as nbcuda
desc = {
'shape': (length_pointer[0]),
'typestr': '>f8',
'data': (int(ffi.cast("intptr_t", array_pointer[0])), False),
'version': 2
}
# return Numba array
return nbcuda.from_cuda_array_interface(desc)
# Get Vector's data array in read-only mode
def get_array_read(self, memtype=MEM_HOST):
"""Get read-only access to a Vector via the specified memory type.
Args:
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
*array: Numpy or Numba array"""
# Retrieve the length of the array
length_pointer = ffi.new("CeedSize *")
err_code = lib.CeedVectorGetLength(self._pointer[0], length_pointer)
self._ceed._check_error(err_code)
# Setup the pointer's pointer
array_pointer = ffi.new("CeedScalar **")
# libCEED call
err_code = lib.CeedVectorGetArrayRead(
self._pointer[0], memtype, array_pointer)
self._ceed._check_error(err_code)
# Return array created from buffer
if memtype == MEM_HOST:
# Create buffer object from returned pointer
buff = ffi.buffer(
array_pointer[0],
ffi.sizeof("CeedScalar") *
length_pointer[0])
# return read only Numpy array
ret = np.frombuffer(buff, dtype=scalar_types[lib.CEED_SCALAR_TYPE])
ret.flags['WRITEABLE'] = False
return ret
else:
# CUDA array interface
# https://numba.pydata.org/numba-doc/latest/cuda/cuda_array_interface.html
import numba.cuda as nbcuda
desc = {
'shape': (length_pointer[0]),
'typestr': '>f8',
'data': (int(ffi.cast("intptr_t", array_pointer[0])), False),
'version': 2
}
# return read only Numba array
return nbcuda.from_cuda_array_interface(desc)
# Get Vector's data array in write-only mode
def get_array_write(self, memtype=MEM_HOST):
"""Get write-only access to a Vector via the specified memory type.
All old values should be considered invalid.
Args:
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
*array: Numpy or Numba array"""
# Retrieve the length of the array
length_pointer = ffi.new("CeedSize *")
err_code = lib.CeedVectorGetLength(self._pointer[0], length_pointer)
self._ceed._check_error(err_code)
# Setup the pointer's pointer
array_pointer = ffi.new("CeedScalar **")
# libCEED call
err_code = lib.CeedVectorGetArrayWrite(
self._pointer[0], memtype, array_pointer)
self._ceed._check_error(err_code)
# Return array created from buffer
if memtype == MEM_HOST:
# Create buffer object from returned pointer
buff = ffi.buffer(
array_pointer[0],
ffi.sizeof("CeedScalar") *
length_pointer[0])
# return Numpy array
return np.frombuffer(buff, dtype=scalar_types[lib.CEED_SCALAR_TYPE])
else:
# CUDA array interface
# https://numba.pydata.org/numba-doc/latest/cuda/cuda_array_interface.html
import numba.cuda as nbcuda
desc = {
'shape': (length_pointer[0]),
'typestr': '>f8',
'data': (int(ffi.cast("intptr_t", array_pointer[0])), False),
'version': 2
}
# return Numba array
return nbcuda.from_cuda_array_interface(desc)
# Restore the Vector's data array
def restore_array(self):
"""Restore an array obtained using get_array()."""
# Setup the pointer's pointer
array_pointer = ffi.new("CeedScalar **")
# libCEED call
err_code = lib.CeedVectorRestoreArray(self._pointer[0], array_pointer)
self._ceed._check_error(err_code)
# Restore an array obtained using getArrayRead
def restore_array_read(self):
"""Restore an array obtained using get_array_read()."""
# Setup the pointer's pointer
array_pointer = ffi.new("CeedScalar **")
# libCEED call
err_code = lib.CeedVectorRestoreArrayRead(
self._pointer[0], array_pointer)
self._ceed._check_error(err_code)
@contextlib.contextmanager
def array(self, *shape, memtype=MEM_HOST):
"""Context manager for array access.
Args:
shape (tuple): shape of returned numpy.array
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
np.array: writable view of vector
Examples:
Constructing the identity inside a libceed.Vector:
>>> vec = ceed.Vector(16)
>>> with vec.array(4, 4) as x:
>>> x[...] = np.eye(4)
"""
x = self.get_array(memtype=memtype)
if shape:
x = x.reshape(shape)
yield x
self.restore_array()
@contextlib.contextmanager
def array_read(self, *shape, memtype=MEM_HOST):
"""Context manager for read-only array access.
Args:
shape (tuple): shape of returned numpy.array
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
np.array: read-only view of vector
Examples:
Viewing contents of a reshaped libceed.Vector view:
>>> vec = ceed.Vector(6)
>>> vec.set_value(1.3)
>>> with vec.array_read(2, 3) as x:
>>> print(x)
"""
x = self.get_array_read(memtype=memtype)
if shape:
x = x.reshape(shape)
yield x
self.restore_array_read()
@contextlib.contextmanager
def array_write(self, *shape, memtype=MEM_HOST):
"""Context manager for write-only array access.
All old values should be considered invalid.
Args:
shape (tuple): shape of returned numpy.array
**memtype: memory type of the array being passed, default CEED_MEM_HOST
Returns:
np.array: write-only view of vector
Examples:
Viewing contents of a reshaped libceed.Vector view:
>>> vec = ceed.Vector(6)
>>> vec.set_value(1.3)
>>> with vec.array_read(2, 3) as x:
>>> print(x)
"""
x = self.get_array_write(memtype=memtype)
if shape:
x = x.reshape(shape)
yield x
self.restore_array()
# Get the length of a Vector
def get_length(self):
"""Get the length of a Vector.
Returns:
length: length of the Vector"""
length_pointer = ffi.new("CeedSize *")
# libCEED call
err_code = lib.CeedVectorGetLength(self._pointer[0], length_pointer)
self._ceed._check_error(err_code)
return length_pointer[0]
# Get the length of a Vector
def __len__(self):
"""Get the length of a Vector.
Returns:
length: length of the Vector"""
length_pointer = ffi.new("CeedSize *")
# libCEED call
err_code = lib.CeedVectorGetLength(self._pointer[0], length_pointer)
self._ceed._check_error(err_code)
return length_pointer[0]
# Set the Vector to a given constant value
def set_value(self, value):
"""Set the Vector to a constant value.
Args:
value: value to be used"""
# libCEED call
err_code = lib.CeedVectorSetValue(self._pointer[0], value)
self._ceed._check_error(err_code)
# Sync the Vector to a specified memtype
def sync_array(self, memtype=MEM_HOST):
"""Sync the Vector to a specified memtype.
Args:
**memtype: memtype to be synced"""
# libCEED call
err_code = lib.CeedVectorSyncArray(self._pointer[0], memtype)
self._ceed._check_error(err_code)
# Compute the norm of a vector
def norm(self, normtype=NORM_2):
"""Get the norm of a Vector.
Args:
**normtype: type of norm to be computed"""
norm_pointer = ffi.new("CeedScalar *")
# libCEED call
err_code = lib.CeedVectorNorm(self._pointer[0], normtype, norm_pointer)
self._ceed._check_error(err_code)
return norm_pointer[0]
# Take the reciprocal of a vector
def reciprocal(self):
"""Take the reciprocal of a Vector."""
# libCEED call
err_code = lib.CeedVectorReciprocal(self._pointer[0])
self._ceed._check_error(err_code)
return self
# Compute self = alpha self
def scale(self, alpha):
"""Compute self = alpha self."""
# libCEED call
err_code = lib.CeedVectorScale(self._pointer[0], alpha)
self._ceed._check_error(err_code)
return self
# Compute self = alpha x + self
def axpy(self, alpha, x):
"""Compute self = alpha x + self."""
# libCEED call
err_code = lib.CeedVectorAXPY(self._pointer[0], alpha, x._pointer[0])
self._ceed._check_error(err_code)
return self
# Compute self = alpha x + beta self
def axpby(self, alpha, beta, x):
"""Compute self = alpha x + beta self."""
# libCEED call
err_code = lib.CeedVectorAXPBY(
self._pointer[0], alpha, beta, x._pointer[0])
self._ceed._check_error(err_code)
return self
# Compute the pointwise multiplication self = x .* y
def pointwise_mult(self, x, y):
"""Compute the pointwise multiplication self = x .* y."""
# libCEED call
err_code = lib.CeedVectorPointwiseMult(
self._pointer[0], x._pointer[0], y._pointer[0]
)
self._ceed._check_error(err_code)
return self
def _state(self):
"""Return the modification state of the Vector.
State is incremented each time the Vector is mutated, and is odd whenever a
mutable reference has not been returned.
"""
state_pointer = ffi.new("uint64_t *")
err_code = lib.CeedVectorGetState(self._pointer[0], state_pointer)
self._ceed._check_error(err_code)
return state_pointer[0]
# ------------------------------------------------------------------------------
class _VectorWrap(Vector):
"""Wrap a CeedVector pointer in a Vector object."""
# Constructor
def __init__(self, ceed, pointer):
# CeedVector object
self._pointer = pointer
# Reference to Ceed
self._ceed = ceed
# ------------------------------------------------------------------------------