File: utils.pyx

package info (click to toggle)
python-suitesparse-graphblas 7.4.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 956 kB
  • sloc: ansic: 9,939; python: 3,575; sh: 52; makefile: 13
file content (75 lines) | stat: -rw-r--r-- 2,898 bytes parent folder | download
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
import numpy as np
from cpython.ref cimport Py_INCREF
from libc.stdint cimport uintptr_t
from numpy cimport NPY_ARRAY_F_CONTIGUOUS, NPY_ARRAY_OWNDATA, NPY_ARRAY_WRITEABLE
from numpy cimport dtype as dtype_t
from numpy cimport import_array, ndarray, npy_intp

import_array()

cpdef int call_gxb_init(object ffi, object lib, int mode):
    # We need to call `GxB_init`, but we didn't compile Cython against GraphBLAS.  So, we get it from cffi.
    # Step 1: ffi.addressof(lib, "GxB_init")
    #    Return type: cffi.cdata object of a function pointer.  Can't cast to int.
    # Step 2: ffi.cast("uintptr_t", ...)
    #    Return type: cffi.cdata object of a uintptr_t type, an unsigned pointer.  Can cast to int.
    # Step 3: int(...)
    #    Return type: int.  The physical address of the function.
    # Step 4: <uintptr_t>(...)
    #    Return type: uintptr_t in Cython.  Cast Python int to Cython integer for pointers.
    # Step 5: <GsB_init>(...)
    #    Return: function pointer in Cython!

    cdef GxB_init func = <GxB_init><uintptr_t>int(ffi.cast("uintptr_t", ffi.addressof(lib, "GxB_init")))
    return func(<GrB_Mode>mode, PyDataMem_NEW, PyDataMem_NEW_ZEROED, PyDataMem_RENEW, PyDataMem_FREE)


cpdef ndarray claim_buffer(object ffi, object cdata, size_t size, dtype_t dtype):
    cdef:
        npy_intp dims = size
        uintptr_t ptr = int(ffi.cast("uintptr_t", cdata))
        ndarray array
    Py_INCREF(dtype)
    array = PyArray_NewFromDescr(
        ndarray, dtype, 1, &dims, NULL, <void*>ptr, NPY_ARRAY_WRITEABLE, <object>NULL
    )
    PyArray_ENABLEFLAGS(array, NPY_ARRAY_OWNDATA)
    return array


cpdef ndarray claim_buffer_2d(
    object ffi, object cdata, size_t cdata_size, size_t nrows, size_t ncols, dtype_t dtype, bint is_c_order
):
    cdef:
        size_t size = nrows * ncols
        ndarray array
        uintptr_t ptr
        npy_intp dims[2]
        int flags = NPY_ARRAY_WRITEABLE
    if cdata_size == size:
        ptr = int(ffi.cast("uintptr_t", cdata))
        dims[0] = nrows
        dims[1] = ncols
        if not is_c_order:
            flags |= NPY_ARRAY_F_CONTIGUOUS
        Py_INCREF(dtype)
        array = PyArray_NewFromDescr(
            ndarray, dtype, 2, dims, NULL, <void*>ptr, flags, <object>NULL
        )
        PyArray_ENABLEFLAGS(array, NPY_ARRAY_OWNDATA)
    elif cdata_size > size:  # pragma: no cover
        array = claim_buffer(ffi, cdata, cdata_size, dtype)
        if is_c_order:
            array = array[:size].reshape((nrows, ncols))
        else:
            array = array[:size].reshape((ncols, nrows)).T
    else:  # pragma: no cover
        raise ValueError(
            f"Buffer size too small: {cdata_size}.  "
            f"Unable to create matrix of size {nrows}x{ncols} = {size}"
        )
    return array


cpdef unclaim_buffer(ndarray array):
    PyArray_CLEARFLAGS(array, NPY_ARRAY_OWNDATA | NPY_ARRAY_WRITEABLE)