/* data.c Numerical Array Extension for Ruby (C) Copyright 1999-2017 by Masahiro TANAKA */ #include #include "numo/narray.h" #include "numo/template.h" static VALUE sym_mulsum; static ID id_mulsum; static ID id_respond_to_p; static ID id_store; static ID id_swap_byte; // --------------------------------------------------------------------- #define LOOP_UNARY_PTR(lp,proc) \ { \ size_t i; \ ssize_t s1, s2; \ char *p1, *p2; \ size_t *idx1, *idx2; \ INIT_COUNTER(lp, i); \ INIT_PTR_IDX(lp, 0, p1, s1, idx1); \ INIT_PTR_IDX(lp, 1, p2, s2, idx2); \ if (idx1) { \ if (idx2) { \ for (; i--;) { \ proc((p1+*idx1), (p2+*idx2)); \ idx1++; \ idx2++; \ } \ } else { \ for (; i--;) { \ proc((p1+*idx1), p2); \ idx1++; \ p2 += s2; \ } \ } \ } else { \ if (idx2) { \ for (; i--;) { \ proc(p1, (p1+*idx2)); \ p1 += s1; \ idx2++; \ } \ } else { \ for (; i--;) { \ proc(p1, p2); \ p1 += s1; \ p2 += s2; \ } \ } \ } \ } #define m_memcpy(src,dst) memcpy(dst,src,e) void iter_copy_bytes(na_loop_t *const lp) { size_t e; e = lp->args[0].elmsz; LOOP_UNARY_PTR(lp,m_memcpy); } VALUE na_copy(VALUE self) { VALUE v; ndfunc_arg_in_t ain[1] = {{Qnil,0}}; ndfunc_arg_out_t aout[1] = {{INT2FIX(0),0}}; ndfunc_t ndf = { iter_copy_bytes, FULL_LOOP, 1, 1, ain, aout }; v = na_ndloop(&ndf, 1, self); return v; } VALUE na_store(VALUE self, VALUE src) { return rb_funcall(self,id_store,1,src); } // --------------------------------------------------------------------- #define m_swap_byte(q1,q2) \ { \ size_t j; \ memcpy(b1,q1,e); \ for (j=0; jargs[0].elmsz; b1 = ALLOCA_N(char, e); b2 = ALLOCA_N(char, e); LOOP_UNARY_PTR(lp,m_swap_byte); } static VALUE nary_swap_byte(VALUE self) { VALUE v; ndfunc_arg_in_t ain[1] = {{Qnil,0}}; ndfunc_arg_out_t aout[1] = {{INT2FIX(0),0}}; ndfunc_t ndf = { iter_swap_byte, FULL_LOOP|NDF_ACCEPT_BYTESWAP, 1, 1, ain, aout }; v = na_ndloop(&ndf, 1, self); if (self!=v) { na_copy_flags(self, v); } REVERSE_ENDIAN(v); return v; } static VALUE nary_to_network(VALUE self) { if (TEST_BIG_ENDIAN(self)) { return self; } return rb_funcall(self, id_swap_byte, 0); } static VALUE nary_to_vacs(VALUE self) { if (TEST_LITTLE_ENDIAN(self)) { return self; } return rb_funcall(self, id_swap_byte, 0); } static VALUE nary_to_host(VALUE self) { if (TEST_HOST_ORDER(self)) { return self; } return rb_funcall(self, id_swap_byte, 0); } static VALUE nary_to_swapped(VALUE self) { if (TEST_BYTE_SWAPPED(self)) { return self; } return rb_funcall(self, id_swap_byte, 0); } //---------------------------------------------------------------------- static inline int check_axis(int axis, int ndim) { if (axis < -ndim || axis >= ndim) { rb_raise(nary_eDimensionError,"invalid axis (%d for %d-dimension)", axis, ndim); } if (axis < 0) { axis += ndim; } return axis; } /* Interchange two axes. @overload swapaxes(axis1,axis2) @param [Integer] axis1 @param [Integer] axis2 @return [Numo::NArray] view of NArray. @example x = Numo::Int32[[1,2,3]] p x.swapaxes(0,1) # Numo::Int32(view)#shape=[3,1] # [[1], # [2], # [3]] p x = Numo::Int32[[[0,1],[2,3]],[[4,5],[6,7]]] # Numo::Int32#shape=[2,2,2] # [[[0, 1], # [2, 3]], # [[4, 5], # [6, 7]]] p x.swapaxes(0,2) # Numo::Int32(view)#shape=[2,2,2] # [[[0, 4], # [2, 6]], # [[1, 5], # [3, 7]]] */ VALUE na_swapaxes(VALUE self, VALUE a1, VALUE a2) { int i, j, ndim; size_t tmp_shape; stridx_t tmp_stridx; narray_view_t *na; volatile VALUE view; view = na_make_view(self); GetNArrayView(view,na); ndim = na->base.ndim; i = check_axis(NUM2INT(a1), ndim); j = check_axis(NUM2INT(a2), ndim); tmp_shape = na->base.shape[i]; tmp_stridx = na->stridx[i]; na->base.shape[i] = na->base.shape[j]; na->stridx[i] = na->stridx[j]; na->base.shape[j] = tmp_shape; na->stridx[j] = tmp_stridx; return view; } VALUE na_transpose_map(VALUE self, int *map) { int i, ndim; size_t *shape; stridx_t *stridx; narray_view_t *na; volatile VALUE view; view = na_make_view(self); GetNArrayView(view,na); ndim = na->base.ndim; shape = ALLOCA_N(size_t,ndim); stridx = ALLOCA_N(stridx_t,ndim); for (i=0; ibase.shape[i]; stridx[i] = na->stridx[i]; } for (i=0; ibase.shape[i] = shape[map[i]]; na->stridx[i] = stridx[map[i]]; } return view; } #define SWAP(a,b,tmp) {tmp=a;a=b;b=tmp;} VALUE na_transpose(int argc, VALUE *argv, VALUE self) { int ndim, *map, *permute; int i, d; bool is_positive, is_negative; narray_t *na1; GetNArray(self,na1); ndim = na1->ndim; if (ndim < 2) { if (argc > 0) { rb_raise(rb_eArgError, "unnecessary argument for 1-d array"); } return na_make_view(self); } map = ALLOCA_N(int,ndim); if (argc == 0) { for (i=0; i < ndim; i++) { map[i] = ndim-1-i; } return na_transpose_map(self,map); } // with argument if (argc > ndim) { rb_raise(rb_eArgError, "more arguments than ndim"); } for (i=0; i < ndim; i++) { map[i] = i; } permute = ALLOCA_N(int,argc); for (i=0; i < argc; i++) { permute[i] = 0; } is_positive = is_negative = 0; for (i=0; i < argc; i++) { if (TYPE(argv[i]) != T_FIXNUM) { rb_raise(rb_eArgError, "invalid argument"); } d = FIX2INT(argv[i]); if (d >= 0) { if (d >= argc) { rb_raise(rb_eArgError, "out of dimension range"); } if (is_negative) { rb_raise(rb_eArgError, "dimension must be non-negative only or negative only"); } if (permute[d]) { rb_raise(rb_eArgError, "not permutation"); } map[i] = d; permute[d] = 1; is_positive = 1; } else { if (d < -argc) { rb_raise(rb_eArgError, "out of dimension range"); } if (is_positive) { rb_raise(rb_eArgError, "dimension must be non-negative only or negative only"); } if (permute[argc+d]) { rb_raise(rb_eArgError, "not permutation"); } map[ndim-argc+i] = ndim+d; permute[argc+d] = 1; is_negative = 1; } } return na_transpose_map(self,map); } //---------------------------------------------------------------------- static void na_check_reshape(int argc, VALUE *argv, VALUE self, size_t *shape) { int i, unfixed=-1; size_t total=1; narray_t *na; if (argc == 0) { rb_raise(rb_eArgError, "No argrument"); } GetNArray(self,na); if (NA_SIZE(na) == 0) { rb_raise(rb_eRuntimeError, "cannot reshape empty array"); } /* get shape from argument */ for (i=0; i= 0) { rb_raise(rb_eArgError,"multiple unfixed dimension"); } unfixed = i; break; default: rb_raise(rb_eArgError,"illegal type"); } } if (unfixed>=0) { if (NA_SIZE(na) % total != 0) { rb_raise(rb_eArgError, "Total size size must be divisor"); } shape[unfixed] = NA_SIZE(na) / total; } else if (total != NA_SIZE(na)) { rb_raise(rb_eArgError, "Total size must be same"); } } /* Change the shape of self NArray without coping. Raise exception if self is non-contiguous. @overload reshape!(size0,size1,...) @param sizeN [Integer] new shape @return [Numo::NArray] return self. @example */ static VALUE na_reshape_bang(int argc, VALUE *argv, VALUE self) { size_t *shape; narray_t *na; if (na_check_contiguous(self)==Qfalse) { rb_raise(rb_eStandardError, "cannot change shape of non-contiguous NArray"); } shape = ALLOCA_N(size_t, argc); na_check_reshape(argc, argv, self, shape); GetNArray(self, na); na_setup_shape(na, argc, shape); return self; } /* Copy and change the shape of NArray. Returns a copied NArray. @overload reshape(size0,size1,...) @param sizeN [Integer] new shape @return [Numo::NArray] return self. @example */ static VALUE na_reshape(int argc, VALUE *argv, VALUE self) { size_t *shape; narray_t *na; VALUE copy; shape = ALLOCA_N(size_t, argc); na_check_reshape(argc, argv, self, shape); copy = rb_funcall(self, rb_intern("dup"), 0); GetNArray(copy, na); na_setup_shape(na, argc, shape); return copy; } //---------------------------------------------------------------------- VALUE na_flatten_dim(VALUE self, int sd) { int i, nd, fd; size_t j; size_t *c, *pos, *idx1, *idx2; size_t stride; size_t *shape, size; stridx_t sdx; narray_t *na; narray_view_t *na1, *na2; volatile VALUE view; GetNArray(self,na); nd = na->ndim; if (nd==0) { return na_make_view(self); } if (sd<0 || sd>=nd) { rb_bug("na_flaten_dim: start_dim (%d) out of range",sd); } // new shape shape = ALLOCA_N(size_t,sd+1); for (i=0; ishape[i]; } size = 1; for (i=sd; ishape[i]; } shape[sd] = size; // new object view = na_s_allocate_view(CLASS_OF(self)); na_copy_flags(self, view); GetNArrayView(view, na2); // new stride na_setup_shape((narray_t*)na2, sd+1, shape); na2->stridx = ALLOC_N(stridx_t,sd+1); switch(na->type) { case NARRAY_DATA_T: case NARRAY_FILEMAP_T: stride = nary_element_stride(self); for (i=sd+1; i--; ) { //printf("data: i=%d shpae[i]=%ld stride=%ld\n",i,shape[i],stride); SDX_SET_STRIDE(na2->stridx[i],stride); stride *= shape[i]; } na2->offset = 0; na2->data = self; break; case NARRAY_VIEW_T: GetNArrayView(self, na1); na2->data = na1->data; na2->offset = na1->offset; for (i=0; istridx[i])) { idx1 = SDX_GET_INDEX(na1->stridx[i]); idx2 = ALLOC_N(size_t, shape[i]); for (j=0; jstridx[i],idx2); } else { na2->stridx[i] = na1->stridx[i]; //printf("view: i=%d stridx=%d\n",i,SDX_GET_STRIDE(sdx)); } } // flat dimenion == last dimension if (RTEST(na_check_ladder(self,sd))) { //if (0) { na2->stridx[sd] = na1->stridx[nd-1]; } else { // set index idx2 = ALLOC_N(size_t, shape[sd]); SDX_SET_INDEX(na2->stridx[sd],idx2); // init for md-loop fd = nd-sd; c = ALLOC_N(size_t, fd); for (i=0; istridx[i+sd]; if (SDX_IS_INDEX(sdx)) { pos[i+1] = pos[i] + SDX_GET_INDEX(sdx)[c[i]]; } else { pos[i+1] = pos[i] + SDX_GET_STRIDE(sdx)*c[i]; } } idx2[j++] = pos[i]; for (;;) { if (i==0) goto loop_end; i--; c[i]++; if (c[i] < na1->base.shape[i+sd]) break; c[i] = 0; } } loop_end: xfree(pos); xfree(c); } break; } return view; } VALUE na_flatten(VALUE self) { return na_flatten_dim(self,0); } //---------------------------------------------------------------------- #define MIN(a,b) (((a)<(b))?(a):(b)) /* Returns a diagonal view of NArray @overload diagonal([offset,axes]) @param [Integer] offset Diagonal offset from the main diagonal. The default is 0. k>0 for diagonals above the main diagonal, and k<0 for diagonals below the main diagonal. @param [Array] axes Array of axes to be used as the 2-d sub-arrays from which the diagonals should be taken. Defaults to last-two axes ([-2,-1]). @return [Numo::NArray] diagonal view of NArray. @example a = Numo::DFloat.new(4,5).seq => Numo::DFloat#shape=[4,5] [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]] b = a.diagonal(1) => Numo::DFloat(view)#shape=[4] [1, 7, 13, 19] b.store(0) a => Numo::DFloat#shape=[4,5] [[0, 0, 2, 3, 4], [5, 6, 0, 8, 9], [10, 11, 12, 0, 14], [15, 16, 17, 18, 0]] b.store([1,2,3,4]) a => Numo::DFloat#shape=[4,5] [[0, 1, 2, 3, 4], [5, 6, 2, 8, 9], [10, 11, 12, 3, 14], [15, 16, 17, 18, 4]] */ VALUE na_diagonal(int argc, VALUE *argv, VALUE self) { int i, k, nd; size_t j; size_t *idx0, *idx1, *diag_idx; size_t *shape; size_t diag_size; ssize_t stride, stride0, stride1; narray_t *na; narray_view_t *na1, *na2; VALUE view; VALUE vofs=0, vaxes=0; ssize_t kofs; size_t k0, k1; int ax[2]; // check arguments if (argc>2) { rb_raise(rb_eArgError,"too many arguments (%d for 0..2)",argc); } for (i=0; indim; if (nd < 2) { rb_raise(nary_eDimensionError,"less than 2-d array"); } if (vaxes) { if (RARRAY_LEN(vaxes) != 2) { rb_raise(rb_eArgError,"axes must be 2-element array"); } ax[0] = NUM2INT(RARRAY_AREF(vaxes,0)); ax[1] = NUM2INT(RARRAY_AREF(vaxes,1)); if (ax[0]<-nd || ax[0]>=nd || ax[1]<-nd || ax[1]>=nd) { rb_raise(rb_eArgError,"axis out of range:[%d,%d]",ax[0],ax[1]); } if (ax[0]<0) {ax[0] += nd;} if (ax[1]<0) {ax[1] += nd;} if (ax[0]==ax[1]) { rb_raise(rb_eArgError,"same axes:[%d,%d]",ax[0],ax[1]); } } else { ax[0] = nd-2; ax[1] = nd-1; } // Diagonal offset from the main diagonal. if (kofs >= 0) { k0 = 0; k1 = kofs; if (k1 >= na->shape[ax[1]]) { rb_raise(rb_eArgError,"invalid diagonal offset(%"SZF"d) for " "last dimension size(%"SZF"d)",kofs,na->shape[ax[1]]); } } else { k0 = -kofs; k1 = 0; if (k0 >= na->shape[ax[0]]) { rb_raise(rb_eArgError,"invalid diagonal offset(=%"SZF"d) for " "last-1 dimension size(%"SZF"d)",kofs,na->shape[ax[0]]); } } diag_size = MIN(na->shape[ax[0]]-k0,na->shape[ax[1]]-k1); // new shape shape = ALLOCA_N(size_t,nd-1); for (i=k=0; ishape[i]; } } shape[k] = diag_size; // new object view = na_s_allocate_view(CLASS_OF(self)); na_copy_flags(self, view); GetNArrayView(view, na2); // new stride na_setup_shape((narray_t*)na2, nd-1, shape); na2->stridx = ALLOC_N(stridx_t, nd-1); switch(na->type) { case NARRAY_DATA_T: case NARRAY_FILEMAP_T: na2->offset = 0; na2->data = self; stride = stride0 = stride1 = nary_element_stride(self); for (i=nd,k=nd-2; i--; ) { if (i==ax[1]) { stride1 = stride; if (kofs > 0) { na2->offset = kofs*stride; } } else if (i==ax[0]) { stride0 = stride; if (kofs < 0) { na2->offset = (-kofs)*stride; } } else { SDX_SET_STRIDE(na2->stridx[--k],stride); } stride *= na->shape[i]; } SDX_SET_STRIDE(na2->stridx[nd-2],stride0+stride1); break; case NARRAY_VIEW_T: GetNArrayView(self, na1); na2->data = na1->data; na2->offset = na1->offset; for (i=k=0; istridx[i])) { idx0 = SDX_GET_INDEX(na1->stridx[i]); idx1 = ALLOC_N(size_t, na->shape[i]); for (j=0; jshape[i]; j++) { idx1[j] = idx0[j]; } SDX_SET_INDEX(na2->stridx[k],idx1); } else { na2->stridx[k] = na1->stridx[i]; } k++; } } if (SDX_IS_INDEX(na1->stridx[ax[0]])) { idx0 = SDX_GET_INDEX(na1->stridx[ax[0]]); diag_idx = ALLOC_N(size_t, diag_size); if (SDX_IS_INDEX(na1->stridx[ax[1]])) { idx1 = SDX_GET_INDEX(na1->stridx[ax[1]]); for (j=0; jstridx[ax[1]]); for (j=0; jstridx[nd-2],diag_idx); } else { stride0 = SDX_GET_STRIDE(na1->stridx[ax[0]]); if (SDX_IS_INDEX(na1->stridx[ax[1]])) { idx1 = SDX_GET_INDEX(na1->stridx[ax[1]]); diag_idx = ALLOC_N(size_t, diag_size); for (j=0; jstridx[nd-2],diag_idx); } else { stride1 = SDX_GET_STRIDE(na1->stridx[ax[1]]); na2->offset += stride0*k0 + stride1*k1; SDX_SET_STRIDE(na2->stridx[nd-2],stride0+stride1); } } break; } return view; } //---------------------------------------------------------------------- #if 0 #ifdef SWAP #undef SWAP #endif #define SWAP(a,b,t) {t=a;a=b;b=t;} static VALUE na_new_dimension_for_dot(VALUE self, int pos, int len, bool transpose) { int i, k, l, nd; size_t j; size_t *idx1, *idx2; size_t *shape; ssize_t stride; narray_t *na; narray_view_t *na1, *na2; size_t shape_n; stridx_t stridx_n; volatile VALUE view; GetNArray(self,na); nd = na->ndim; view = na_s_allocate_view(CLASS_OF(self)); na_copy_flags(self, view); GetNArrayView(view, na2); // new dimension if (pos < 0) pos += nd; if (pos > nd || pos < 0) { rb_raise(rb_eRangeError,"new dimension is out of range"); } nd += len; shape = ALLOCA_N(size_t,nd); na2->stridx = ALLOC_N(stridx_t,nd); switch(na->type) { case NARRAY_DATA_T: case NARRAY_FILEMAP_T: i = k = 0; while (i < nd) { if (i == pos && len > 0) { for (l=0; lshape[k++]; } } na_setup_shape((narray_t*)na2, nd, shape); stride = nary_element_stride(self); for (i=nd; i--;) { SDX_SET_STRIDE(na2->stridx[i], stride); stride *= shape[i]; } na2->offset = 0; na2->data = self; break; case NARRAY_VIEW_T: GetNArrayView(self, na1); i = k = 0; while (i < nd) { if (i == pos && len > 0) { if (SDX_IS_INDEX(na1->stridx[k])) { stride = SDX_GET_INDEX(na1->stridx[k])[0]; } else { stride = SDX_GET_STRIDE(na1->stridx[k]); } for (l=0; lstridx[i], stride); i++; } } else { shape[i] = na1->base.shape[k]; if (SDX_IS_INDEX(na1->stridx[k])) { idx1 = SDX_GET_INDEX(na1->stridx[k]); idx2 = ALLOC_N(size_t,na1->base.shape[k]); for (j=0; jbase.shape[k]; j++) { idx2[j] = idx1[j]; } SDX_SET_INDEX(na2->stridx[i], idx2); } else { na2->stridx[i] = na1->stridx[k]; } i++; k++; } } na_setup_shape((narray_t*)na2, nd, shape); na2->offset = na1->offset; na2->data = na1->data; break; } if (transpose) { SWAP(na2->base.shape[nd-1], na2->base.shape[nd-2], shape_n); SWAP(na2->stridx[nd-1], na2->stridx[nd-2], stridx_n); } return view; } //---------------------------------------------------------------------- /* * call-seq: * narray.dot(other) => narray * * Returns dot product. * */ static VALUE numo_na_dot(VALUE self, VALUE other) { VALUE test; volatile VALUE a1=self, a2=other; narray_t *na1, *na2; test = rb_funcall(a1, id_respond_to_p, 1, sym_mulsum); if (!RTEST(test)) { rb_raise(rb_eNoMethodError,"requires mulsum method for dot method"); } GetNArray(a1,na1); GetNArray(a2,na2); if (na1->ndim==0 || na2->ndim==0) { rb_raise(nary_eDimensionError,"zero dimensional narray"); } if (na2->ndim > 1) { if (na1->shape[na1->ndim-1] != na2->shape[na2->ndim-2]) { rb_raise(nary_eShapeError,"shape mismatch: self.shape[-1](=%"SZF"d) != other.shape[-2](=%"SZF"d)", na1->shape[na1->ndim-1], na2->shape[na2->ndim-2]); } // insert new axis [ ..., last-1-dim, newaxis*other.ndim, last-dim ] a1 = na_new_dimension_for_dot(a1, na1->ndim-1, na2->ndim-1, 0); // insert & transpose [ newaxis*self.ndim, ..., last-dim, last-1-dim ] a2 = na_new_dimension_for_dot(a2, 0, na1->ndim-1, 1); } return rb_funcall(a1,id_mulsum,2,a2,INT2FIX(-1)); } #endif void Init_nary_data() { rb_define_method(cNArray, "copy", na_copy, 0); // deprecated rb_define_method(cNArray, "flatten", na_flatten, 0); rb_define_method(cNArray, "swapaxes", na_swapaxes, 2); rb_define_method(cNArray, "transpose", na_transpose, -1); rb_define_method(cNArray, "reshape", na_reshape,-1); rb_define_method(cNArray, "reshape!", na_reshape_bang,-1); /* rb_define_alias(cNArray, "shape=","reshape!"); */ rb_define_method(cNArray, "diagonal", na_diagonal,-1); rb_define_method(cNArray, "swap_byte", nary_swap_byte, 0); #ifdef DYNAMIC_ENDIAN #else #ifdef WORDS_BIGENDIAN #else // LITTLE_ENDIAN rb_define_alias(cNArray, "hton", "swap_byte"); rb_define_alias(cNArray, "network_order?", "byte_swapped?"); rb_define_alias(cNArray, "little_endian?", "host_order?"); rb_define_alias(cNArray, "vacs_order?", "host_order?"); #endif #endif rb_define_method(cNArray, "to_network", nary_to_network, 0); rb_define_method(cNArray, "to_vacs", nary_to_vacs, 0); rb_define_method(cNArray, "to_host", nary_to_host, 0); rb_define_method(cNArray, "to_swapped", nary_to_swapped, 0); //rb_define_method(cNArray, "dot", numo_na_dot, 1); id_mulsum = rb_intern("mulsum"); sym_mulsum = ID2SYM(id_mulsum); id_respond_to_p = rb_intern("respond_to?"); id_store = rb_intern("store"); id_swap_byte = rb_intern("swap_byte"); }