From 4aa0090a4436bfad419eafef07e76cc330575f42 Mon Sep 17 00:00:00 2001 From: sy3394 Date: Tue, 11 Apr 2023 20:29:20 +0300 Subject: [PATCH 1/3] pre-working version --- lyncs_quda/enum.py | 2 +- lyncs_quda/lib.py | 23 ++++++- lyncs_quda/struct.py | 142 +++++++++++++++++++++++++++++++++++++------ test/test_structs.py | 104 +++++++++++++++++++++++++++++-- 4 files changed, 245 insertions(+), 26 deletions(-) diff --git a/lyncs_quda/enum.py b/lyncs_quda/enum.py index 4b4d4e3..29b3fc1 100644 --- a/lyncs_quda/enum.py +++ b/lyncs_quda/enum.py @@ -118,7 +118,7 @@ class Enum(metaclass=EnumMeta): def __init__(self, fnc, lpath=None, default=None, callback=None): # fnc is supposed to return either a stripped key name or value of - # the corresponding QUDA enum type + # the corresponding QUDA enum type so as to decorate a property obj self.fnc = fnc self.lpath = lpath self.default = default diff --git a/lyncs_quda/lib.py b/lyncs_quda/lib.py index 58c2a66..d22ae72 100644 --- a/lyncs_quda/lib.py +++ b/lyncs_quda/lib.py @@ -223,6 +223,27 @@ def copy_struct(self): ) return self.lyncs_quda_copy_struct + @property + def copy_strings(self): + try: + return self.lyncs_quda_copy_strings + except AttributeError: + cppdef( + """ + void lyncs_quda_copy_strings(QudaMultigridParam &mp, std::vector in, bool is_infile){ + //void lyncs_quda_copy_strings(char (&out)[][256], std::vector in){ + int n = in.size(); + for (int i=0; i size: + raise ValueError( + f"Values size ({len(vals)}) larger than array size ({size})" + ) + else: + vals = (vals,) * size + for i, val in enumerate(vals): + if hasattr(arr[i], "__len__") and len(shape)>1: + setitems(arr[i], val, shape = shape[1:]) + else: + arr[i] = val + print("set item", i,"to",val) + +cppdef( + """ + template void set2Darray(T (&out)[nrow][ncol], T (&in)[nrow][ncol]){ + for (size_t i = 0; i< nrow; i++){ + for (size_t j = 0; j< ncol; j++){ + out[i][j] = in[i][j]; + } + } + } +""" +) +cppdef( + """ + template void print2Darray(T (&a)[nrow][ncol]){ + for (size_t i = 0; i< nrow; i++){ + for (size_t j = 0; j< ncol; j++){ + std::cout< lib.QUDA_MAX_MG_LEVEL else n + setattr(self._quda_params, "n_level", n) + for arg in args: self.update(arg) self.update(kwargs) @@ -67,30 +137,50 @@ def items(self): def update(self, params): "Updates values of the structure" - if not hasattr( - params, "items" - ): # ? in __init__, it takes *args, which is a tuple. expect a tuple of dict's? + if not hasattr(params, "items"): raise TypeError(f"Unsopported type for params: {type(params)}") for key, val in params.items(): setattr(self, key, val) def _assign(self, key, val): + print("assign") typ = self._types[key] - val = to_code(val, typ) - cur = getattr(self._params, key) - - if hasattr(cur, "shape"): # ? what is this? - setitems(cur, val) # ? what is this? + val = to_code(val, typ) if not("char" in typ and typ.count("[") == 2) else val + cur = getattr(self._quda_params, key) + + if "[" in self._types[key] and not "file" in key and not hasattr(cur, "shape"): + # safeguard against hectic behavior of cppyy + raise RuntimeError("cppyy is not happy for now. Try again!") + + if hasattr(cur, "shape") and "[" in typ and "file" in key: + if typ.count("[") >0: + print(val) + setattr(self._quda_params, key, val) + elif typ.count("[") == 2: + for i in range(cur.shape[0]): + print( val[i]) + #setattr(getattr(self._quda_params, key)[i], val[i]) + elif hasattr(cur, "shape") and "[" in typ: + shape = tuple([getattr(lib, macro) for macro in typ.split(" ") if "QUDA_" in macro or macro.isnumeric()]) + print("assign (shape)",key,typ,val,cur,cur.shape, isiterable(cur), shape) + cur.reshape(shape) + setitems(cur, val) else: - setattr(self._params, key, val) + # cannot set nullptr to void *, int *, etc; works for classes such as Enum classes with bind_object + if "*" in typ: + if val == nullptr: + raise ValueError("Cannot cast nullptr to a valid pointer") + val = to_pointer(val, ctype = typ) + setattr(self._quda_params, key, val) def __dir__(self): - return list(set(list(super().__dir__()) + list(self._params.keys()))) + return list(set(list(super().__dir__()) + list(self._quda_params.keys()))) def __getattr__(self, key): - return to_human(getattr(self._params, key), self._types[key]) + return to_human(getattr(self._quda_params, key), self._types[key]) def __setattr__(self, key, val): + print("set atr") if key in self.keys(): try: self._assign(key, val) @@ -98,8 +188,20 @@ def __setattr__(self, key, val): raise TypeError( f"Cannot assign '{val}' to '{key}' of type '{self._types[key]}'" ) - else: + else: #should we allow this? super().__setattr__(key, val) def __str__(self): return str(dict(self.items())) + + @property + def quda(self): + return self._quda_params + + @property + def ptr(self): + return to_pointer(addressof(self.quda), ctype = type(self).__name__ + " *") + + + def printf(self): + getattr(lib, "print"+type(self).__name__)(self._quda_params) diff --git a/test/test_structs.py b/test/test_structs.py index cf48951..e2922e5 100644 --- a/test/test_structs.py +++ b/test/test_structs.py @@ -1,13 +1,109 @@ from lyncs_quda import structs # This is also importing Enum +from lyncs_quda.enum import Enum +from lyncs_cppyy import nullptr, array_to_pointers from lyncs_quda.testing import fixlib as lib - +#from lyncs_cppyy.ll import addressof +import cppyy.ll +import numpy as np +from array import array def test_assign_zero(lib): for struct in dir(structs): - if struct.startswith("_") or struct == "Struct": + print(struct, type(struct),struct == "Struct", struct.startswith("_"),isinstance(struct, Enum),struct == "Enum") + + if struct.startswith("_") or struct == "Struct" or struct == "Enum" or issubclass(getattr(structs, struct), Enum): continue + params = getattr(structs, struct)() - + print("TEST PARAMS",params) + print("TET KYS", params.keys()) for key in params.keys(): - setattr(params, key, 0) + typ = getattr(structs, struct)._types[key] + obj = getattr(structs, typ) if typ in dir(structs) else int + val = 0 + if obj != Enum and issubclass(obj, Enum): + val = list(obj.values())[0] + elif "*" in typ: + continue + print("tst",struct,key,typ, obj,val) + + setattr(params, key, val) + +def test_assign_something(lib): + mp = structs.QudaMultigridParam() + mp.n_level = lib.QUDA_MAX_MG_LEVEL #needs to be supplied manually + ip = structs.QudaInvertParam() + ep = structs.QudaEigParam() + + # ptr to strct class works + print("pre inv set", mp.n_level, getattr(lib.newQudaMultigridParam(),"n_level"), lib.QUDA_MAX_MG_LEVEL) + mp.invert_param = cppyy.ll.addressof(ip.quda) + ip.split_grid = list(range(lib.QUDA_MAX_DIM)) + print("post inv set",mp.n_level, hasattr(ip.split_grid,"shape")) + mp.printf() + + + # array of complex conversion works + for i in range(lib.QUDA_MAX_DWF_LS): + print("pre b_5",ip.b_5[i]) + ip.b_5[i] = complex(1,i) + print("post b_5",ip.b_5[i]) + ip.b_5 = [complex(2,i) for i in range(lib.QUDA_MAX_DWF_LS)] + print(ip.b_5, [ip.b_5[i] for i in range(lib.QUDA_MAX_DWF_LS)]) + # array of basic types probably works + ip.split_grid = list(range(lib.QUDA_MAX_DIM)) + # hmm, so 'char []' has not shape info => not recognized as an array but as a str via cppyy? + for i, s in enumerate("hi I'm here!"): + ip.madwf_param_infile[i] = s #"hi I'm here!" + print(ip.madwf_param_infile[i]) + print(ip.madwf_param_infile, hasattr(ip.madwf_param_infile,"shape")) + ip.printf() + print(ip) + + + # the following works + print(mp.geo_block_size.shape) + for i in range(lib.QUDA_MAX_MG_LEVEL): + for j in range(lib.QUDA_MAX_DIM): + print("pre geo",i,j,":",mp.geo_block_size[i][j]) + mp.geo_block_size[i][j] = i+j + print("post geo",i,j,":", mp.geo_block_size[i][j]) + mp.printf() + # setitems does not work as it works wiht subviews, I suppose + l = [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] + #mp.geo_block_size = array('i', sum(l,[]))# [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] #np.ones((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM), dtype="i") + #mp.geo_block_size = l #np.asarray(l,dtype="i") + mp.printf() + l2 = [[i+j+5 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] + lib.set2Darray[int,lib.QUDA_MAX_DIM,lib.QUDA_MAX_MG_LEVEL](mp.geo_block_size,np.asarray(l2,dtype="i"))#has no effect... + + for i in range(lib.QUDA_MAX_MG_LEVEL): + for j in range(lib.QUDA_MAX_DIM): + print("geo",i,j,":",mp.geo_block_size[i][j], "vs", l[i][j] +5) + + lib.print2Darray[int,lib.QUDA_MAX_DIM,lib.QUDA_MAX_MG_LEVEL](mp.geo_block_size) + mp.printf() + + #print(list(mp.geo_block_size.reshape((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM))))#[QUDA_MAX_MG_LEVEL][QUDA_MAX_DIM] + print("infile",mp.vec_infile,mp.vec_infile.shape) + for i in range(lib.QUDA_MAX_MG_LEVEL): + pass + #mp.vec_infile[i] = "hi"+str(i) #error + #d=mp.vec_infile.reshape((lib.QUDA_MAX_MG_LEVEL, ))#256))# ValueError: cannot reshape array of size 261 into shape (5,) + #mp.vec_infile = ["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] + #f = array_to_pointers(mp.vec_infile) + #mp.vec_infile.reshape(mp.vec_infile.shape) + print(mp.vec_infile) + #v = np.frombuffer(mp.vec_infile, dtype=np.dtype('b') , count=-1)#lib.QUDA_MAX_MG_LEVEL) + #print(v) + #print(np.sum([1 for i in v]), len(v),len(mp.vec_infile[0])) + #v[0]=1 #bytes("h", 'ascii') + sa = ["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] + #lib.copy_strings(mp._quda_params, lib.std.vector["std::string"](sa)) + print(lib.std.vector["std::string"]([mp.vec_infile[i] for i in range(5)])) + lib.copy_strings(mp.vec_infile, lib.std.vector["std::string"](sa)) + #mp.vec_infile = np.empty(mp.vec_infile.shape, dtype=np.dtype("b")) #np.asarray(["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)], dtype=np.dtype(str)) #[lib.std.string("hi"+str(i)) for i in range(lib.QUDA_MAX_MG_LEVEL)] + #mp.vec_infile[0] = "hi" + mp.printf() + print(mp) From cc060833356beb30e0fe5baf9f945399d987de89 Mon Sep 17 00:00:00 2001 From: sy3394 Date: Mon, 24 Apr 2023 18:46:06 +0300 Subject: [PATCH 2/3] second commit --- lyncs_quda/struct.py | 88 ++++++++++++++++++++++++-------------------- test/test_structs.py | 72 +++++++++++++++++++++++------------- 2 files changed, 94 insertions(+), 66 deletions(-) diff --git a/lyncs_quda/struct.py b/lyncs_quda/struct.py index a8089f5..afcb689 100644 --- a/lyncs_quda/struct.py +++ b/lyncs_quda/struct.py @@ -50,6 +50,24 @@ def to_code(val, typ=None): return nullptr return val + +def get_dtype(typ): + if "*" in typ: + return np.dtype(object) + typ_dict = {"complex":"c", "unsigned":"u", "float":"single", "char":"byte", "comlex_double":"double"} + typ_list = typ.split() + dtype = "" + for w in typ_list: + if w in ("complex", "unsigned"): + dtype = typ_dict[w] + dtype + dtype += typ_dict.get(w, w) + if typ in ("bool", "long"): + dtype += "_" + if "int" in typ: + dtype += "c" + return np.dtype(dtype) + + def setitems(arr, vals, shape=None): "Sets items of an iterable object" shape = shape if shape is not None else arr.shape @@ -69,29 +87,6 @@ def setitems(arr, vals, shape=None): arr[i] = val print("set item", i,"to",val) -cppdef( - """ - template void set2Darray(T (&out)[nrow][ncol], T (&in)[nrow][ncol]){ - for (size_t i = 0; i< nrow; i++){ - for (size_t j = 0; j< ncol; j++){ - out[i][j] = in[i][j]; - } - } - } -""" -) -cppdef( - """ - template void print2Darray(T (&a)[nrow][ncol]){ - for (size_t i = 0; i< nrow; i++){ - for (size_t j = 0; j< ncol; j++){ - std::cout< lib.QUDA_MAX_MG_LEVEL else n @@ -145,32 +140,42 @@ def update(self, params): def _assign(self, key, val): print("assign") typ = self._types[key] - val = to_code(val, typ) if not("char" in typ and typ.count("[") == 2) else val + val = to_code(val, typ) cur = getattr(self._quda_params, key) - if "[" in self._types[key] and not "file" in key and not hasattr(cur, "shape"): + if "[" in self._types[key] and not hasattr(cur, "shape"):# not sure if this is needed for cppyy3.0.0 # safeguard against hectic behavior of cppyy raise RuntimeError("cppyy is not happy for now. Try again!") + - if hasattr(cur, "shape") and "[" in typ and "file" in key: - if typ.count("[") >0: - print(val) - setattr(self._quda_params, key, val) - elif typ.count("[") == 2: - for i in range(cur.shape[0]): - print( val[i]) - #setattr(getattr(self._quda_params, key)[i], val[i]) - elif hasattr(cur, "shape") and "[" in typ: - shape = tuple([getattr(lib, macro) for macro in typ.split(" ") if "QUDA_" in macro or macro.isnumeric()]) + if typ.count("[") > 1: + # cppyy<=3.0.0 cannot handle subviews properly + assert hasattr(cur, "shape") + if "file" in key: + array = np.chararray(cur.shape) + setitems(array, b"\0") + setitems(array, vals) + size = 1 + else: + dtype = get_dtyp(typ[:-typ.index("[")].strip()) + array = np.empty(cur.shape, dtype=dtype) + size = dtype.itemsize + lib.memcpy(to_pointer(addressof(cur)), to_pointer(array.__array_interface__["data"][0]), int(np.prod(cur.shape))*size) + elif typ.count("[") == 1: + assert hasattr(cur, "shape") + shape = tuple([getattr(lib, macro) for macro in typ.split(" ") if "QUDA_" in macro or macro.isnumeric()]) #not necessary for cppyy3.0.0? print("assign (shape)",key,typ,val,cur,cur.shape, isiterable(cur), shape) - cur.reshape(shape) + cur.reshape(shape) #not necessary for cppyy3.0.0? + if "*" in typ: + for i in range(shape[0]): + val = to_pointer(addressof(val), ctype = typ[:-typ.index("[")].strip()) setitems(cur, val) else: - # cannot set nullptr to void *, int *, etc; works for classes such as Enum classes with bind_object if "*" in typ: + # cannot set nullptr to void *, int *, etc; works for classes such as Enum classes with bind_object if val == nullptr: raise ValueError("Cannot cast nullptr to a valid pointer") - val = to_pointer(val, ctype = typ) + val = to_pointer(addressof(val), ctype = typ) setattr(self._quda_params, key, val) def __dir__(self): @@ -198,10 +203,13 @@ def __str__(self): def quda(self): return self._quda_params + @property + def address(self): + return addressof(self.quda) + @property def ptr(self): return to_pointer(addressof(self.quda), ctype = type(self).__name__ + " *") - def printf(self): getattr(lib, "print"+type(self).__name__)(self._quda_params) diff --git a/test/test_structs.py b/test/test_structs.py index e2922e5..1b6baa3 100644 --- a/test/test_structs.py +++ b/test/test_structs.py @@ -1,30 +1,30 @@ from lyncs_quda import structs # This is also importing Enum from lyncs_quda.enum import Enum -from lyncs_cppyy import nullptr, array_to_pointers +from lyncs_cppyy import nullptr, array_to_pointers, to_pointer from lyncs_quda.testing import fixlib as lib #from lyncs_cppyy.ll import addressof import cppyy.ll import numpy as np from array import array +import cppyy as cp + +import ctypes as C +from ctypes.util import find_library def test_assign_zero(lib): for struct in dir(structs): - print(struct, type(struct),struct == "Struct", struct.startswith("_"),isinstance(struct, Enum),struct == "Enum") if struct.startswith("_") or struct == "Struct" or struct == "Enum" or issubclass(getattr(structs, struct), Enum): continue - params = getattr(structs, struct)() - print("TEST PARAMS",params) - print("TET KYS", params.keys()) for key in params.keys(): typ = getattr(structs, struct)._types[key] - obj = getattr(structs, typ) if typ in dir(structs) else int + obj = getattr(structs, typ) if typ in dir(structs) else None val = 0 if obj != Enum and issubclass(obj, Enum): val = list(obj.values())[0] - elif "*" in typ: + elif "*" in typ: # cannot set a pointer field to nullptr via cppyy continue print("tst",struct,key,typ, obj,val) @@ -37,12 +37,10 @@ def test_assign_something(lib): ep = structs.QudaEigParam() # ptr to strct class works - print("pre inv set", mp.n_level, getattr(lib.newQudaMultigridParam(),"n_level"), lib.QUDA_MAX_MG_LEVEL) + print("pre inv set", mp.n_level, getattr(lib.newQudaMultigridParam(),"n_level"), lib.QUDA_MAX_MG_LEVEL,hasattr(mp.invert_param,"shape")) mp.invert_param = cppyy.ll.addressof(ip.quda) ip.split_grid = list(range(lib.QUDA_MAX_DIM)) - print("post inv set",mp.n_level, hasattr(ip.split_grid,"shape")) mp.printf() - # array of complex conversion works for i in range(lib.QUDA_MAX_DWF_LS): @@ -50,17 +48,15 @@ def test_assign_something(lib): ip.b_5[i] = complex(1,i) print("post b_5",ip.b_5[i]) ip.b_5 = [complex(2,i) for i in range(lib.QUDA_MAX_DWF_LS)] - print(ip.b_5, [ip.b_5[i] for i in range(lib.QUDA_MAX_DWF_LS)]) + print([(ip.b_5[i],ip.b_5[i]) for i in range(lib.QUDA_MAX_DWF_LS)]) # array of basic types probably works ip.split_grid = list(range(lib.QUDA_MAX_DIM)) - # hmm, so 'char []' has not shape info => not recognized as an array but as a str via cppyy? + # char[] needs to be set one by one for i, s in enumerate("hi I'm here!"): ip.madwf_param_infile[i] = s #"hi I'm here!" print(ip.madwf_param_infile[i]) print(ip.madwf_param_infile, hasattr(ip.madwf_param_infile,"shape")) ip.printf() - print(ip) - # the following works print(mp.geo_block_size.shape) @@ -70,19 +66,35 @@ def test_assign_something(lib): mp.geo_block_size[i][j] = i+j print("post geo",i,j,":", mp.geo_block_size[i][j]) mp.printf() - # setitems does not work as it works wiht subviews, I suppose + # setitems does not work properly as it works wiht subviews, I suppose: it sets entries to values different from the expected: l = [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] #mp.geo_block_size = array('i', sum(l,[]))# [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] #np.ones((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM), dtype="i") - #mp.geo_block_size = l #np.asarray(l,dtype="i") + mp.geo_block_size = l #np.asarray(l,dtype="i") mp.printf() - l2 = [[i+j+5 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] - lib.set2Darray[int,lib.QUDA_MAX_DIM,lib.QUDA_MAX_MG_LEVEL](mp.geo_block_size,np.asarray(l2,dtype="i"))#has no effect... - - for i in range(lib.QUDA_MAX_MG_LEVEL): - for j in range(lib.QUDA_MAX_DIM): - print("geo",i,j,":",mp.geo_block_size[i][j], "vs", l[i][j] +5) - - lib.print2Darray[int,lib.QUDA_MAX_DIM,lib.QUDA_MAX_MG_LEVEL](mp.geo_block_size) + print("FRom BUUGGREFFFER") + """ + Assume: arr is a lowlvelview obj of int carr[5][5] + Observation: obj.shape == (5,5) + in the following, possibly after reshaping, np.frombuffer(arr, dtype=DTYPE, count=-1) is used + if obj is reshaped to (25,), print(v, dtype=np.int32) prints only the first 10 elems of carr but correctly + print(v, dtype=np.int64) prints only the first 5 elems of carr incorectly + if obj is not reshaped or reshaped to (5,5) redundantly, v2=v[0] and v2[0] = 10 results to segfault + """ + arr = mp.geo_block_size + print(arr.shape) + #arr.reshape(arr.shape) # fails: segfault <- if you reshape it to + #v = np.frombuffer(arr, dtype=np.int32, count=-1) + data_pointer = C.cast(cppyy.ll.addressof(arr),C.POINTER(C.c_int)) + v = np.ctypeslib.as_array(data_pointer,shape=arr.shape) + print(v) + v2 = v[0] + for i in range(len(v2)): + v2[i] = 100+i + print(v) + mp.printf() + # the following attempt sets the entries only up to a certain point... + a1 = np.ones((np.prod(arr.shape),),dtype=np.int32) + cp.gbl.memcpy(to_pointer(cp.ll.addressof(mp.geo_block_size)), to_pointer(a1.__array_interface__["data"][0]), int(np.prod(arr.shape))*4) mp.printf() #print(list(mp.geo_block_size.reshape((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM))))#[QUDA_MAX_MG_LEVEL][QUDA_MAX_DIM] @@ -101,8 +113,16 @@ def test_assign_something(lib): #v[0]=1 #bytes("h", 'ascii') sa = ["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] #lib.copy_strings(mp._quda_params, lib.std.vector["std::string"](sa)) - print(lib.std.vector["std::string"]([mp.vec_infile[i] for i in range(5)])) - lib.copy_strings(mp.vec_infile, lib.std.vector["std::string"](sa)) + + #print(lib.std.vector["std::string"]([mp.vec_infile[i] for i in range(5)])) + #lib.copy_strings(mp.vec_infile, lib.std.vector["std::string"](sa)) + sa2 = np.chararray((lib.QUDA_MAX_MG_LEVEL,256)) + for i,s in enumerate(sa): + sa2[i] = [s[j] if j Date: Wed, 26 Apr 2023 19:01:34 +0300 Subject: [PATCH 3/3] working version --- lyncs_quda/lib.py | 21 --------- lyncs_quda/struct.py | 62 +++++++++++++------------- test/test_structs.py | 104 +++---------------------------------------- 3 files changed, 38 insertions(+), 149 deletions(-) diff --git a/lyncs_quda/lib.py b/lyncs_quda/lib.py index d22ae72..c9d550d 100644 --- a/lyncs_quda/lib.py +++ b/lyncs_quda/lib.py @@ -222,27 +222,6 @@ def copy_struct(self): """ ) return self.lyncs_quda_copy_struct - - @property - def copy_strings(self): - try: - return self.lyncs_quda_copy_strings - except AttributeError: - cppdef( - """ - void lyncs_quda_copy_strings(QudaMultigridParam &mp, std::vector in, bool is_infile){ - //void lyncs_quda_copy_strings(char (&out)[][256], std::vector in){ - int n = in.size(); - for (int i=0; i size: raise ValueError( @@ -81,12 +80,13 @@ def setitems(arr, vals, shape=None): else: vals = (vals,) * size for i, val in enumerate(vals): - if hasattr(arr[i], "__len__") and len(shape)>1: - setitems(arr[i], val, shape = shape[1:]) + if len(shape)>1 and hasattr(arr[i], "__len__"): + is_string = len(shape[1:]) == 1 and type(vals[0]) == str + setitems(arr[i], val, shape = shape[1:], is_string=is_string) else: arr[i] = val - print("set item", i,"to",val) + class Struct: "Struct base class" _types = {} @@ -104,18 +104,11 @@ def __init__(self, *args, **kwargs): if not getattr(self._quda_params, key) in enm.values(): val = list(enm.values())[-1] self._assign(key, val) - # to avoid codec error - elif 'file' in key and self._types[key].count("[")<0: - # TODO: think about a better thing to do here - # The Problem: attribute access, getattr(self._quda_params, key), raises 'UnicodeDecodeError: 'utf-8' codec can't decode byte...' - # if not initialized to 0's if one uses newQuda* functions - #print(key, self._types[key]) - setattr(self._quda_params, key, getattr(default_params, key)) # temporal fix: newQudaMultigridParam does not assign a default value to n_level if "Multigrid" in type(self).__name__: n = getattr(self._quda_params, "n_level") - n = lib.QUDA_MAX_MG_LEVEL if n < 0 and n > lib.QUDA_MAX_MG_LEVEL else n + n = lib.QUDA_MAX_MG_LEVEL if n < 0 or n > lib.QUDA_MAX_MG_LEVEL else n setattr(self._quda_params, "n_level", n) for arg in args: @@ -138,7 +131,6 @@ def update(self, params): setattr(self, key, val) def _assign(self, key, val): - print("assign") typ = self._types[key] val = to_code(val, typ) cur = getattr(self._quda_params, key) @@ -150,26 +142,37 @@ def _assign(self, key, val): if typ.count("[") > 1: # cppyy<=3.0.0 cannot handle subviews properly + # Trying to manipulate the sub-array either results in error or segfault + # => array of arrays is set using glb.memcpy + # Alternative: + # use ctypes (C = ctypes, arr = LowlevelView of array of arrays) + # ptr = C.cast(cppyy.ll.addressof(arr), C.POINTER(C.c_int)) + # narr = np.ctypeslib.as_array(ptr, shape=arr.shape) + # This allows to access sub-indicies properly, i.e., narr[2][3] = 9 works assert hasattr(cur, "shape") if "file" in key: + #? array = np.zeros(cur.shape, dtype="S1") and remove setitems(array, b"\0"); is this ok? + #? not sure of this as "" is not b"\0" array = np.chararray(cur.shape) - setitems(array, b"\0") - setitems(array, vals) + setitems(array, b"\0") + setitems(array, val) size = 1 else: - dtype = get_dtyp(typ[:-typ.index("[")].strip()) - array = np.empty(cur.shape, dtype=dtype) + dtype = get_dtype(typ[:typ.index("[")].strip()) + array = np.asarray(val, dtype=dtype) size = dtype.itemsize lib.memcpy(to_pointer(addressof(cur)), to_pointer(array.__array_interface__["data"][0]), int(np.prod(cur.shape))*size) elif typ.count("[") == 1: assert hasattr(cur, "shape") shape = tuple([getattr(lib, macro) for macro in typ.split(" ") if "QUDA_" in macro or macro.isnumeric()]) #not necessary for cppyy3.0.0? - print("assign (shape)",key,typ,val,cur,cur.shape, isiterable(cur), shape) - cur.reshape(shape) #not necessary for cppyy3.0.0? + cur.reshape(shape) #? not necessary for cppyy3.0.0? if "*" in typ: for i in range(shape[0]): val = to_pointer(addressof(val), ctype = typ[:-typ.index("[")].strip()) - setitems(cur, val) + is_string = True if "char" in typ else False + if is_string: + setitems(cur, b"\0") # for printing + setitems(cur, val, is_string=is_string) else: if "*" in typ: # cannot set nullptr to void *, int *, etc; works for classes such as Enum classes with bind_object @@ -185,7 +188,6 @@ def __getattr__(self, key): return to_human(getattr(self._quda_params, key), self._types[key]) def __setattr__(self, key, val): - print("set atr") if key in self.keys(): try: self._assign(key, val) diff --git a/test/test_structs.py b/test/test_structs.py index 1b6baa3..38fda97 100644 --- a/test/test_structs.py +++ b/test/test_structs.py @@ -1,15 +1,6 @@ from lyncs_quda import structs # This is also importing Enum from lyncs_quda.enum import Enum -from lyncs_cppyy import nullptr, array_to_pointers, to_pointer from lyncs_quda.testing import fixlib as lib -#from lyncs_cppyy.ll import addressof -import cppyy.ll -import numpy as np -from array import array -import cppyy as cp - -import ctypes as C -from ctypes.util import find_library def test_assign_zero(lib): for struct in dir(structs): @@ -32,98 +23,15 @@ def test_assign_zero(lib): def test_assign_something(lib): mp = structs.QudaMultigridParam() - mp.n_level = lib.QUDA_MAX_MG_LEVEL #needs to be supplied manually ip = structs.QudaInvertParam() ep = structs.QudaEigParam() # ptr to strct class works - print("pre inv set", mp.n_level, getattr(lib.newQudaMultigridParam(),"n_level"), lib.QUDA_MAX_MG_LEVEL,hasattr(mp.invert_param,"shape")) - mp.invert_param = cppyy.ll.addressof(ip.quda) + mp.n_level = 3 # This is supposed to be set explicitly + mp.invert_param = ip.quda ip.split_grid = list(range(lib.QUDA_MAX_DIM)) + ip.madwf_param_infile = "hi I'm here!" + mp.geo_block_size = [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] + mp.vec_infile = ["infile" + str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] mp.printf() - - # array of complex conversion works - for i in range(lib.QUDA_MAX_DWF_LS): - print("pre b_5",ip.b_5[i]) - ip.b_5[i] = complex(1,i) - print("post b_5",ip.b_5[i]) - ip.b_5 = [complex(2,i) for i in range(lib.QUDA_MAX_DWF_LS)] - print([(ip.b_5[i],ip.b_5[i]) for i in range(lib.QUDA_MAX_DWF_LS)]) - # array of basic types probably works - ip.split_grid = list(range(lib.QUDA_MAX_DIM)) - # char[] needs to be set one by one - for i, s in enumerate("hi I'm here!"): - ip.madwf_param_infile[i] = s #"hi I'm here!" - print(ip.madwf_param_infile[i]) - print(ip.madwf_param_infile, hasattr(ip.madwf_param_infile,"shape")) - ip.printf() - - # the following works - print(mp.geo_block_size.shape) - for i in range(lib.QUDA_MAX_MG_LEVEL): - for j in range(lib.QUDA_MAX_DIM): - print("pre geo",i,j,":",mp.geo_block_size[i][j]) - mp.geo_block_size[i][j] = i+j - print("post geo",i,j,":", mp.geo_block_size[i][j]) - mp.printf() - # setitems does not work properly as it works wiht subviews, I suppose: it sets entries to values different from the expected: - l = [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] - #mp.geo_block_size = array('i', sum(l,[]))# [[i+j+1 for j in range(lib.QUDA_MAX_DIM)] for i in range(lib.QUDA_MAX_MG_LEVEL)] #np.ones((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM), dtype="i") - mp.geo_block_size = l #np.asarray(l,dtype="i") - mp.printf() - print("FRom BUUGGREFFFER") - """ - Assume: arr is a lowlvelview obj of int carr[5][5] - Observation: obj.shape == (5,5) - in the following, possibly after reshaping, np.frombuffer(arr, dtype=DTYPE, count=-1) is used - if obj is reshaped to (25,), print(v, dtype=np.int32) prints only the first 10 elems of carr but correctly - print(v, dtype=np.int64) prints only the first 5 elems of carr incorectly - if obj is not reshaped or reshaped to (5,5) redundantly, v2=v[0] and v2[0] = 10 results to segfault - """ - arr = mp.geo_block_size - print(arr.shape) - #arr.reshape(arr.shape) # fails: segfault <- if you reshape it to - #v = np.frombuffer(arr, dtype=np.int32, count=-1) - data_pointer = C.cast(cppyy.ll.addressof(arr),C.POINTER(C.c_int)) - v = np.ctypeslib.as_array(data_pointer,shape=arr.shape) - print(v) - v2 = v[0] - for i in range(len(v2)): - v2[i] = 100+i - print(v) - mp.printf() - # the following attempt sets the entries only up to a certain point... - a1 = np.ones((np.prod(arr.shape),),dtype=np.int32) - cp.gbl.memcpy(to_pointer(cp.ll.addressof(mp.geo_block_size)), to_pointer(a1.__array_interface__["data"][0]), int(np.prod(arr.shape))*4) - mp.printf() - - #print(list(mp.geo_block_size.reshape((lib.QUDA_MAX_MG_LEVEL,lib.QUDA_MAX_DIM))))#[QUDA_MAX_MG_LEVEL][QUDA_MAX_DIM] - print("infile",mp.vec_infile,mp.vec_infile.shape) - for i in range(lib.QUDA_MAX_MG_LEVEL): - pass - #mp.vec_infile[i] = "hi"+str(i) #error - #d=mp.vec_infile.reshape((lib.QUDA_MAX_MG_LEVEL, ))#256))# ValueError: cannot reshape array of size 261 into shape (5,) - #mp.vec_infile = ["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] - #f = array_to_pointers(mp.vec_infile) - #mp.vec_infile.reshape(mp.vec_infile.shape) - print(mp.vec_infile) - #v = np.frombuffer(mp.vec_infile, dtype=np.dtype('b') , count=-1)#lib.QUDA_MAX_MG_LEVEL) - #print(v) - #print(np.sum([1 for i in v]), len(v),len(mp.vec_infile[0])) - #v[0]=1 #bytes("h", 'ascii') - sa = ["hi"+str(i) for i in range(lib.QUDA_MAX_MG_LEVEL)] - #lib.copy_strings(mp._quda_params, lib.std.vector["std::string"](sa)) - - #print(lib.std.vector["std::string"]([mp.vec_infile[i] for i in range(5)])) - #lib.copy_strings(mp.vec_infile, lib.std.vector["std::string"](sa)) - sa2 = np.chararray((lib.QUDA_MAX_MG_LEVEL,256)) - for i,s in enumerate(sa): - sa2[i] = [s[j] if j