Skip to content

Commit

Permalink
gh-218: add vector assign and eadd ops
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorOrachyov committed Aug 29, 2023
1 parent 1b7161d commit 6e18be6
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 42 deletions.
15 changes: 13 additions & 2 deletions python/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,16 @@
s = pyspla.Scalar(value=10, dtype=pyspla.INT)
print(s)

v = pyspla.Vector.from_lists([1, 2, 3], [-10, 100, 20], shape=10, dtype=pyspla.INT)
print(v.reduce(pyspla.INT.PLUS))
v = pyspla.Vector.from_lists([10, 20, 30], [-10, 100, 20], shape=100, dtype=pyspla.FLOAT)
print(v.to_list())

u = pyspla.Vector.generate(shape=100, dtype=pyspla.FLOAT, density=0.3)
print(u.to_list())

r = v.eadd(pyspla.FLOAT.MULT, u)
print(r.to_list())

m = pyspla.Vector.from_lists([0, 2, 5], [-1, 1, 1], shape=10, dtype=pyspla.INT)
t = pyspla.Vector(shape=10, dtype=pyspla.INT)
t.assign(m, pyspla.Scalar(pyspla.INT, 10), pyspla.INT.SECOND, pyspla.INT.GEZERO)
print(t.to_list())
4 changes: 2 additions & 2 deletions python/pyspla/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@
- `UINT`. 4-byte-sized unsigned integral value.
- `FLOAT`. 4-byte-sized single-precision floating point value.
Operations
----------
Ops
---
Library provides a set of unary, binary and select ops for values data manipulation inside
matrix and vector containers. Unary operations commonly used for apply and transformation
Expand Down
4 changes: 3 additions & 1 deletion python/pyspla/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def generate(cls, dtype=INT, shape=0, seed=None, dist=(0, 1)):
Creates new array of desired type and shape and fills its content
with random values, generated using specified distribution.
:param dtype: Type.
:param dtype: optional: Type. default: INT.
Type of values array will have.
:param shape: optional: int. default: 0.
Expand Down Expand Up @@ -260,6 +260,8 @@ def generate(cls, dtype=INT, shape=0, seed=None, dist=(0, 1)):
elif dtype is FLOAT:
for i in range(shape):
array.set(i, rnd.uniform(dist[0], dist[1]))
else:
raise Exception("unknown type")

return array

Expand Down
2 changes: 1 addition & 1 deletion python/pyspla/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ def __init__(self, label=None, hnd=None):
MemView native void* handle to a C counterpart.
"""

super().__init__(label, hnd)
super().__init__(label, hnd)
184 changes: 153 additions & 31 deletions python/pyspla/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
import ctypes

from .bridge import backend, check
from .type import Type, INT, UINT
from .type import Type, INT, UINT, FLOAT
from .object import Object
from .memview import MemView
from .scalar import Scalar
from .matrix import Matrix
from .descriptor import Descriptor
from .op import OpUnary, OpBinary, OpSelect
import random as rnd


class Vector(Object):
Expand Down Expand Up @@ -70,6 +71,8 @@ class Vector(Object):
using one of built-in OpenCL or CUDA accelerators.
"""

__slots__ = ["_dtype", "_shape"]

def __init__(self, shape, dtype=INT, hnd=None, label=None):
"""
Creates new vector of specified type and shape.
Expand All @@ -95,6 +98,7 @@ def __init__(self, shape, dtype=INT, hnd=None, label=None):
assert issubclass(dtype, Type)

self._dtype = dtype
self._shape = (shape, 1)

if not hnd:
hnd = ctypes.c_void_p(0)
Expand All @@ -114,33 +118,31 @@ def n_rows(self):
"""
Number of rows in the vector.
"""
count = ctypes.c_uint(0)
check(backend().spla_Vector_get_n_rows(self._hnd, ctypes.byref(count)))
return int(count.value)
return self._shape[0]

@property
def shape(self):
"""
2-Tuple with shape of vector where second value is always 1.
"""

return self.n_rows, 1
return self._shape

def build(self, ii_view: MemView, vv_view: MemView):
def build(self, keys_view: MemView, values_view: MemView):
"""
Builds vector content from a raw memory view resources.
:param ii_view: MemView.
:param keys_view: MemView.
View to keys of vector to assign.
:param vv_view: MemView.
:param values_view: MemView.
View to actual values to store.
"""

assert ii_view
assert vv_view
assert keys_view
assert values_view

check(backend().spla_Vector_build(self._hnd, ii_view._hnd, vv_view._hnd))
check(backend().spla_Vector_build(self._hnd, keys_view._hnd, values_view._hnd))

def read(self):
"""
Expand All @@ -149,10 +151,10 @@ def read(self):
:return: tuple (MemView, MemView) objects with view to the vector keys and vector values.
"""

ii_view_hnd = ctypes.c_void_p(0)
vv_view_hnd = ctypes.c_void_p(0)
check(backend().spla_Vector_read(self._hnd, ctypes.byref(ii_view_hnd), ctypes.byref(vv_view_hnd)))
return MemView(hnd=ii_view_hnd, owner=self), MemView(hnd=vv_view_hnd, owner=self)
keys_view_hnd = ctypes.c_void_p(0)
values_view_hnd = ctypes.c_void_p(0)
check(backend().spla_Vector_read(self._hnd, ctypes.byref(keys_view_hnd), ctypes.byref(values_view_hnd)))
return MemView(hnd=keys_view_hnd, owner=self), MemView(hnd=values_view_hnd, owner=self)

def to_lists(self):
"""
Expand All @@ -172,16 +174,26 @@ def to_lists(self):

return list(buffer_keys), list(buffer_values)

def to_list(self):
"""
Read vector data as a python lists of tuples where key and value stored together.
:return: List of vector entries.
"""

keys, values = self.to_lists()
return list(zip(keys, values))

@classmethod
def from_lists(cls, ii: list, vv: list, shape, dtype=INT):
def from_lists(cls, keys: list, values: list, shape, dtype=INT):
"""
Build vector from a list of sorted keys and associated values to store in vector.
List with keys `ii` must index entries from range [0, shape-1] and all keys must be sorted.
List with keys `keys` must index entries from range [0, shape-1] and all keys must be sorted.
:param ii: list[UINT].
:param keys: list[UINT].
List with integral keys of entries.
:param vv: list[Type].
:param values: list[Type].
List with values to store in the vector.
:param shape: int.
Expand All @@ -193,29 +205,136 @@ def from_lists(cls, ii: list, vv: list, shape, dtype=INT):
:return: Created vector filled with values.
"""

assert ii
assert vv
assert len(ii) == len(vv)
assert len(keys) == len(values)
assert shape > 0

count = len(ii)
if not keys:
return Vector(shape, dtype)

c_ii = (UINT._c_type * count)(*ii)
c_vv = (dtype._c_type * count)(*vv)
count = len(keys)

view_ii = MemView(buffer=c_ii, size=ctypes.sizeof(c_ii), mutable=False)
view_vv = MemView(buffer=c_vv, size=ctypes.sizeof(c_vv), mutable=False)
c_keys = (UINT._c_type * count)(*keys)
c_values = (dtype._c_type * count)(*values)

view_keys = MemView(buffer=c_keys, size=ctypes.sizeof(c_keys), mutable=False)
view_values = MemView(buffer=c_values, size=ctypes.sizeof(c_values), mutable=False)

v = Vector(shape=shape, dtype=dtype)
v.build(view_ii, view_vv)
v.build(view_keys, view_values)

return v

def reduce(self, op_reduce, r=None, init=None, desc=None):
@classmethod
def generate(cls, shape, dtype=INT, density=0.1, seed=None, dist=(0, 1)):
"""
Creates new vector of desired type and shape and fills its content
with random values, generated using specified distribution.
:param shape: int.
Size of the array (number of values).
:param dtype: optional: Type. default: INT.
Type of values vector will have.
:param density: optional: float. default: 0.1.
Density of vector or how many entries to generate.
:param seed: optional: int. default: None.
Optional seed to randomize generator.
:param dist: optional: tuple. default: [0,1].
Optional distribution for uniform generation of values.
:return: Created array filled with values.
"""

if seed is not None:
rnd.seed(seed)

keys = [i for i in range(0, shape) if rnd.uniform(0, 1) < density]
count = len(keys)

if dtype is INT:
values = [rnd.randint(dist[0], dist[1]) for i in range(count)]
elif dtype is UINT:
values = [rnd.randint(dist[0], dist[1]) for i in range(count)]
elif dtype is FLOAT:
values = [rnd.uniform(dist[0], dist[1]) for i in range(count)]
else:
raise Exception("unknown type")

return cls.from_lists(keys, values, shape=shape, dtype=dtype)

def eadd(self, op, v, r=None, desc=None):
"""
Element-wise add one vector to another and return result.
:param op: OpBinary.
Binary operation to sum values.
:param v: Vector.
Other right vector to sum with this.
:param r: optional: Vector. default: None.
Optional vector to store result.
:param desc: optional: Descriptor. default: None.
Optional descriptor object to configure the execution.
:return: Vector with result.
"""

assert v
assert v.n_rows == self.n_rows
assert v.dtype == self.dtype
assert op

if r is None:
r = Vector(shape=self.n_rows, dtype=self.dtype)

check(backend().spla_Exec_v_eadd(r.hnd, self.hnd, v.hnd, op.hnd,
self._get_desc(desc), self._get_task(None)))

return r

def assign(self, mask, value, op_assign, op_select, desc=None):
"""
Assign scalar value to a vector by mask.
:param mask: Vector.
Mask vector which structure will be used to select entries for assignment.
:param value: Scalar.
Value to assign.
:param op_assign: OpBinary.
Binary operation used to combine existing value in vector and scalar.
:param op_select: OpSelect.
Predicate to select entries in the mask to assign.
:param desc: optional: Descriptor. default: None.
Optional descriptor object to configure the execution.
:return: This vector.
"""

assert mask
assert value
assert mask.n_rows == self.n_rows
assert op_assign
assert op_select

check(backend().spla_Exec_v_assign_masked(self.hnd, mask.hnd, value.hnd, op_assign.hnd, op_select.hnd,
self._get_desc(desc), self._get_task(None)))

return self

def reduce(self, op, r=None, init=None, desc=None):
"""
Reduce vector elements.
:param op_reduce: OpBinary.
:param op: OpBinary.
Binary operation to apply for reduction of vector elements.
:param r: optional: Scalar: default: 0.
Expand All @@ -235,7 +354,10 @@ def reduce(self, op_reduce, r=None, init=None, desc=None):
if init is None:
init = Scalar(dtype=self.dtype, value=0)

check(backend().spla_Exec_v_reduce(r.hnd, init.hnd, self.hnd, op_reduce.hnd,
assert r.dtype == self.dtype
assert init.dtype == self.dtype

check(backend().spla_Exec_v_reduce(r.hnd, init.hnd, self.hnd, op.hnd,
self._get_desc(desc), self._get_task(None)))

return r
Expand Down
23 changes: 21 additions & 2 deletions src/opencl/cl_format_coo_vec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ namespace spla {
const uint* Ai,
const T* Ax,
CLCooVec<T>& storage) {
assert(n_values > 0);
if (n_values == 0) {
LOG_MSG(Status::Ok, "nothing to do");

storage.values = 0;
storage.Ai = cl::Buffer();
storage.Ax = cl::Buffer();
return;
}

const std::size_t buffer_size_Ai = n_values * sizeof(uint);
const std::size_t buffer_size_Ax = n_values * sizeof(T);
Expand All @@ -64,7 +71,14 @@ namespace spla {
template<typename T>
void cl_coo_vec_resize(const std::size_t n_values,
CLCooVec<T>& storage) {
assert(n_values > 0);
if (n_values == 0) {
LOG_MSG(Status::Ok, "nothing to do");

storage.values = 0;
storage.Ai = cl::Buffer();
storage.Ax = cl::Buffer();
return;
}

const std::size_t buffer_size_Ai = n_values * sizeof(uint);
const std::size_t buffer_size_Ax = n_values * sizeof(T);
Expand Down Expand Up @@ -124,6 +138,11 @@ namespace spla {

cl_fill_value<T>(queue, out.Ax, n_rows, fill_value);

if (in.values == 0) {
LOG_MSG(Status::Ok, "nothing to do");
return;
}

auto* acc = get_acc_cl();

uint block_size = acc->get_default_wgs();
Expand Down
9 changes: 9 additions & 0 deletions src/opencl/cl_format_dense_vec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ namespace spla {
queue.enqueueNDRangeKernel(kernel, cl::NDRange(), global, local);
uint count = cl_count.get(queue);

if (count == 0) {
LOG_MSG(Status::Ok, "nothing to do");

out.values = 0;
out.Ai = cl::Buffer();
out.Ax = cl::Buffer();
return;
}

out.values = count;
out.Ai = cl::Buffer(acc->get_context(), CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS, count * sizeof(uint));
out.Ax = cl::Buffer(acc->get_context(), CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS, count * sizeof(T));
Expand Down
Loading

0 comments on commit 6e18be6

Please sign in to comment.