/* Call any of the clpack_xgetrf functions as directly as possible. * * The clapack_getrf functions (dgetrf, sgetrf, cgetrf, and zgetrf) compute an LU factorization of a general M-by-N * matrix A using partial pivoting with row interchanges. * * The factorization has the form: * A = P * L * U * where P is a permutation matrix, L is lower triangular with unit diagonal elements (lower trapezoidal if m > n), * and U is upper triangular (upper trapezoidal if m < n). * * This is the right-looking level 3 BLAS version of the algorithm. * * == Arguments * See: http://www.netlib.org/lapack/double/dgetrf.f * (You don't need argument 5; this is the value returned by this function.) * * You probably don't want to call this function. Instead, why don't you try clapack_getrf, which is more flexible * with its arguments? * * This function does almost no type checking. Seriously, be really careful when you call it! There's no exception * handling, so you can easily crash Ruby! * * Returns an array giving the pivot indices (normally these are argument #5). */ static VALUE nm_clapack_getrf(VALUE self, VALUE order, VALUE m, VALUE n, VALUE a, VALUE lda) { static int (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const int m, const int n, void* a, const int lda, int* ipiv) = { NULL, NULL, NULL, NULL, NULL, // integers not allowed due to division nm::math::clapack_getrf<float>, nm::math::clapack_getrf<double>, clapack_cgetrf, clapack_zgetrf, // call directly, same function signature! nm::math::clapack_getrf<nm::Rational32>, nm::math::clapack_getrf<nm::Rational64>, nm::math::clapack_getrf<nm::Rational128>, nm::math::clapack_getrf<nm::RubyObject> }; int M = FIX2INT(m), N = FIX2INT(n); // Allocate the pivot index array, which is of size MIN(M, N). size_t ipiv_size = std::min(M,N); int* ipiv = ALLOCA_N(int, ipiv_size); // Call either our version of getrf or the LAPACK version. ttable[NM_DTYPE(a)](blas_order_sym(order), M, N, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), ipiv); // Result will be stored in a. We return ipiv as an array. VALUE ipiv_array = rb_ary_new2(ipiv_size); for (size_t i = 0; i < ipiv_size; ++i) { rb_ary_store(ipiv_array, i, INT2FIX(ipiv[i])); } return ipiv_array; }
static VALUE recsph(VALUE self, VALUE rectangular) { double radius, colatitude, longitude; recsph_c(NM_STORAGE_DENSE(rectangular)->elements, &radius, &colatitude, &longitude); return rb_ary_new3(3, DBL2NUM(radius), DBL2NUM(colatitude), DBL2NUM(longitude)); }
static VALUE reclat(VALUE self, VALUE rectangular_point) { double radius, longitude, latitude; reclat_c(NM_STORAGE_DENSE(rectangular_point)->elements, &radius, &longitude, &latitude); return rb_ary_new3(3, DBL2NUM(radius), DBL2NUM(longitude), DBL2NUM(latitude)); }
static VALUE recrad(VALUE self, VALUE rectangular) { double range, right_ascension, declination; recrad_c(NM_STORAGE_DENSE(rectangular)->elements, &range, &right_ascension, &declination); return rb_ary_new3(3, DBL2NUM(range), DBL2NUM(right_ascension), DBL2NUM(declination)); }
/* * Call any of the cblas_xrotg functions as directly as possible. * * xROTG computes the elements of a Givens plane rotation matrix such that: * * | c s | | a | | r | * | -s c | * | b | = | 0 | * * where r = +- sqrt( a**2 + b**2 ) and c**2 + s**2 = 1. * * The Givens plane rotation can be used to introduce zero elements into a matrix selectively. * * This function differs from most of the other raw BLAS accessors. Instead of * providing a, b, c, s as arguments, you should only provide a and b (the * inputs), and you should provide them as the first two elements of any dense * NMatrix type. * * The outputs [c,s] will be returned in a Ruby Array at the end; the input * NMatrix will also be modified in-place. * * This function, like the other cblas_ functions, does minimal type-checking. */ static VALUE nm_lapacke_cblas_rotg(VALUE self, VALUE ab) { static void (*ttable[nm::NUM_DTYPES])(void* a, void* b, void* c, void* s) = { NULL, NULL, NULL, NULL, NULL, // can't represent c and s as integers, so no point in having integer operations. nm::math::lapacke::cblas_rotg<float>, nm::math::lapacke::cblas_rotg<double>, nm::math::lapacke::cblas_rotg<nm::Complex64>, nm::math::lapacke::cblas_rotg<nm::Complex128>, NULL //nm::math::lapacke::cblas_rotg<nm::RubyObject> }; nm::dtype_t dtype = NM_DTYPE(ab); if (!ttable[dtype]) { rb_raise(nm_eDataTypeError, "this operation undefined for integer vectors"); return Qnil; } else { NM_CONSERVATIVE(nm_register_value(&self)); NM_CONSERVATIVE(nm_register_value(&ab)); void *pC = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]), *pS = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]); // extract A and B from the NVector (first two elements) void* pA = NM_STORAGE_DENSE(ab)->elements; void* pB = (char*)(NM_STORAGE_DENSE(ab)->elements) + DTYPE_SIZES[dtype]; // c and s are output ttable[dtype](pA, pB, pC, pS); VALUE result = rb_ary_new2(2); if (dtype == nm::RUBYOBJ) { rb_ary_store(result, 0, *reinterpret_cast<VALUE*>(pC)); rb_ary_store(result, 1, *reinterpret_cast<VALUE*>(pS)); } else { rb_ary_store(result, 0, nm::rubyobj_from_cval(pC, dtype).rval); rb_ary_store(result, 1, nm::rubyobj_from_cval(pS, dtype).rval); } NM_CONSERVATIVE(nm_unregister_value(&ab)); NM_CONSERVATIVE(nm_unregister_value(&self)); return result; } }
static VALUE recpgr(VALUE self, VALUE body, VALUE rectangular, VALUE radius_equatorial, VALUE flattening) { double longitude, latitude, altitude; recpgr_c(RB_SYM2STR(body), NM_STORAGE_DENSE(rectangular)->elements, NUM2DBL(radius_equatorial), NUM2DBL(flattening), &longitude, &latitude, &altitude); if(spice_error(SPICE_ERROR_SHORT)) return Qnil; return rb_ary_new3(3, DBL2NUM(longitude), DBL2NUM(latitude), DBL2NUM(altitude)); }
/* * Based on LAPACK's dscal function, but for any dtype. * * In-place modification; returns the modified vector as well. */ static VALUE nm_clapack_scal(VALUE self, VALUE n, VALUE scale, VALUE vector, VALUE incx) { dtype_t dtype = NM_DTYPE(vector); void* da = ALLOCA_N(char, DTYPE_SIZES[dtype]); rubyval_to_cval(scale, dtype, da); NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::clapack_scal, void, const int n, const void* da, void* dx, const int incx); ttable[dtype](FIX2INT(n), da, NM_STORAGE_DENSE(vector)->elements, FIX2INT(incx)); return vector; }
/* * call-seq: * NMatrix::BLAS.cblas_scal(n, alpha, vector, inc) -> NMatrix * * BLAS level 1 function +scal+. Works with all dtypes. * * Scale +vector+ in-place by +alpha+ and also return it. The operation is as * follows: * x <- alpha * x * * - +n+ -> Number of elements of +vector+. * - +alpha+ -> Scalar value used in the operation. * - +vector+ -> NMatrix of shape [n,1] or [1,n]. Modified in-place. * - +inc+ -> Increment used in the scaling function. Should generally be 1. */ static VALUE nm_lapacke_cblas_scal(VALUE self, VALUE n, VALUE alpha, VALUE vector, VALUE incx) { nm::dtype_t dtype = NM_DTYPE(vector); void* scalar = NM_ALLOCA_N(char, DTYPE_SIZES[dtype]); rubyval_to_cval(alpha, dtype, scalar); NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::lapacke::cblas_scal, void, const int n, const void* scalar, void* x, const int incx); ttable[dtype](FIX2INT(n), scalar, NM_STORAGE_DENSE(vector)->elements, FIX2INT(incx)); return vector; }
/* Call any of the cblas_xgemv functions as directly as possible. * * The cblas_xgemv functions (dgemv, sgemv, cgemv, and zgemv) define the following operation: * * y = alpha*op(A)*x + beta*y * * where op(A) is one of <tt>op(A) = A</tt>, <tt>op(A) = A**T</tt>, or the complex conjugate of A. * * Note that this will only work for dense matrices that are of types :float32, :float64, :complex64, and :complex128. * Other types are not implemented in BLAS, and while they exist in NMatrix, this method is intended only to * expose the ultra-optimized ATLAS versions. * * == Arguments * See: http://www.netlib.org/blas/dgemm.f * * You probably don't want to call this function. Instead, why don't you try cblas_gemv, which is more flexible * with its arguments? * * This function does almost no type checking. Seriously, be really careful when you call it! There's no exception * handling, so you can easily crash Ruby! */ static VALUE nm_cblas_gemv(VALUE self, VALUE trans_a, VALUE m, VALUE n, VALUE alpha, VALUE a, VALUE lda, VALUE x, VALUE incx, VALUE beta, VALUE y, VALUE incy) { NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::cblas_gemv, bool, const enum CBLAS_TRANSPOSE trans_a, int m, int n, void* alpha, void* a, int lda, void* x, int incx, void* beta, void* y, int incy); dtype_t dtype = NM_DTYPE(a); void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]), *pBeta = ALLOCA_N(char, DTYPE_SIZES[dtype]); rubyval_to_cval(alpha, dtype, pAlpha); rubyval_to_cval(beta, dtype, pBeta); return ttable[dtype](blas_transpose_sym(trans_a), FIX2INT(m), FIX2INT(n), pAlpha, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), NM_STORAGE_DENSE(x)->elements, FIX2INT(incx), pBeta, NM_STORAGE_DENSE(y)->elements, FIX2INT(incy)) ? Qtrue : Qfalse; }
/* * Transform the matrix (in-place) to its complex conjugate. Only works on complex matrices. * * FIXME: For non-complex matrices, someone needs to implement a non-in-place complex conjugate (which doesn't use a bang). * Bang should imply that no copy is being made, even temporarily. */ static VALUE nm_complex_conjugate_bang(VALUE self) { NMATRIX* m; void* elem; size_t size, p; UnwrapNMatrix(self, m); if (m->stype == DENSE_STORE) { size = nm_storage_count_max_elements(NM_STORAGE(self)); elem = NM_STORAGE_DENSE(self)->elements; } else if (m->stype == YALE_STORE) { size = nm_yale_storage_get_size(NM_STORAGE_YALE(self)); elem = NM_STORAGE_YALE(self)->a; } else { rb_raise(rb_eNotImpError, "please cast to yale or dense (complex) first"); } // Walk through and negate the imaginary component if (NM_DTYPE(self) == COMPLEX64) { for (p = 0; p < size; ++p) { reinterpret_cast<nm::Complex64*>(elem)[p].i = -reinterpret_cast<nm::Complex64*>(elem)[p].i; } } else if (NM_DTYPE(self) == COMPLEX128) { for (p = 0; p < size; ++p) { reinterpret_cast<nm::Complex128*>(elem)[p].i = -reinterpret_cast<nm::Complex128*>(elem)[p].i; } } else { rb_raise(nm_eDataTypeError, "can only calculate in-place complex conjugate on matrices of type :complex64 or :complex128"); } return self; }
/* * Find the capacity of an NMatrix. The capacity only differs from the size for * Yale matrices, which occasionally allocate more space than they need. For * list and dense, capacity gives the number of elements in the matrix. */ static VALUE nm_capacity(VALUE self) { VALUE cap; switch(NM_STYPE(self)) { case YALE_STORE: cap = UINT2NUM(((YALE_STORAGE*)(NM_STORAGE(self)))->capacity); break; case DENSE_STORE: cap = UINT2NUM(nm_storage_count_max_elements( NM_STORAGE_DENSE(self) )); break; case LIST_STORE: cap = UINT2NUM(nm_list_storage_count_elements( NM_STORAGE_LIST(self) )); break; default: rb_raise(nm_eStorageTypeError, "unrecognized stype in nm_capacity()"); } return cap; }
/* * Borrowed this function from NArray. Handles 'each' iteration on a dense * matrix. * * Additionally, handles separately matrices containing VALUEs and matrices * containing other types of data. */ static VALUE nm_each_dense(VALUE nmatrix) { DENSE_STORAGE* s = NM_STORAGE_DENSE(nmatrix); VALUE v; size_t i; if (NM_DTYPE(nmatrix) == RUBYOBJ) { // matrix of Ruby objects -- yield those objects directly for (i = 0; i < nm_storage_count_max_elements(s); ++i) rb_yield( *((VALUE*)((char*)(s->elements) + i*DTYPE_SIZES[NM_DTYPE(nmatrix)])) ); } else { // We're going to copy the matrix element into a Ruby VALUE and then operate on it. This way user can't accidentally // modify it and cause a seg fault. for (i = 0; i < nm_storage_count_max_elements(s); ++i) { v = rubyobj_from_cval((char*)(s->elements) + i*DTYPE_SIZES[NM_DTYPE(nmatrix)], NM_DTYPE(nmatrix)).rval; rb_yield(v); // yield to the copy we made } } return nmatrix; }
static VALUE rb_gsl_bspline_knots(VALUE obj, VALUE b) { gsl_bspline_workspace *w; Data_Get_Struct(obj, gsl_bspline_workspace, w); #ifdef HAVE_NMATRIX_H if (NM_IsNMatrix(b)) { NM_DENSE_STORAGE *nm_bpts; gsl_vector_view v; nm_bpts = NM_STORAGE_DENSE(b); v = gsl_vector_view_array((double*) nm_bpts->elements, NM_DENSE_COUNT(b)); gsl_bspline_knots(&v.vector, w); return Data_Wrap_Struct(cgsl_vector_view_ro, 0, NULL, w->knots); } #endif gsl_vector *bpts; CHECK_VECTOR(b); Data_Get_Struct(b, gsl_vector, bpts); gsl_bspline_knots(bpts, w); return Data_Wrap_Struct(cgsl_vector_view_ro, 0, NULL, w->knots); }
/* Call any of the cblas_xgemm functions as directly as possible. * * The cblas_xgemm functions (dgemm, sgemm, cgemm, and zgemm) define the following operation: * * C = alpha*op(A)*op(B) + beta*C * * where op(X) is one of <tt>op(X) = X</tt>, <tt>op(X) = X**T</tt>, or the complex conjugate of X. * * Note that this will only work for dense matrices that are of types :float32, :float64, :complex64, and :complex128. * Other types are not implemented in BLAS, and while they exist in NMatrix, this method is intended only to * expose the ultra-optimized ATLAS versions. * * == Arguments * See: http://www.netlib.org/blas/dgemm.f * * You probably don't want to call this function. Instead, why don't you try cblas_gemm, which is more flexible * with its arguments? * * This function does almost no type checking. Seriously, be really careful when you call it! There's no exception * handling, so you can easily crash Ruby! */ static VALUE nm_cblas_gemm(VALUE self, VALUE order, VALUE trans_a, VALUE trans_b, VALUE m, VALUE n, VALUE k, VALUE alpha, VALUE a, VALUE lda, VALUE b, VALUE ldb, VALUE beta, VALUE c, VALUE ldc) { NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::math::cblas_gemm, void, const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE trans_a, const enum CBLAS_TRANSPOSE trans_b, int m, int n, int k, void* alpha, void* a, int lda, void* b, int ldb, void* beta, void* c, int ldc); dtype_t dtype = NM_DTYPE(a); void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]), *pBeta = ALLOCA_N(char, DTYPE_SIZES[dtype]); rubyval_to_cval(alpha, dtype, pAlpha); rubyval_to_cval(beta, dtype, pBeta); ttable[dtype](blas_order_sym(order), blas_transpose_sym(trans_a), blas_transpose_sym(trans_b), FIX2INT(m), FIX2INT(n), FIX2INT(k), pAlpha, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), NM_STORAGE_DENSE(b)->elements, FIX2INT(ldb), pBeta, NM_STORAGE_DENSE(c)->elements, FIX2INT(ldc)); return c; }
static VALUE nm_cblas_trsm(VALUE self, VALUE order, VALUE side, VALUE uplo, VALUE trans_a, VALUE diag, VALUE m, VALUE n, VALUE alpha, VALUE a, VALUE lda, VALUE b, VALUE ldb) { static void (*ttable[nm::NUM_DTYPES])(const enum CBLAS_ORDER, const enum CBLAS_SIDE, const enum CBLAS_UPLO, const enum CBLAS_TRANSPOSE, const enum CBLAS_DIAG, const int, const int, const void* alpha, const void* a, const int lda, void* b, const int ldb) = { NULL, NULL, NULL, NULL, NULL, // integers not allowed due to division nm::math::cblas_trsm<float>, nm::math::cblas_trsm<double>, cblas_ctrsm, cblas_ztrsm, // call directly, same function signature! nm::math::cblas_trsm<nm::Rational32>, nm::math::cblas_trsm<nm::Rational64>, nm::math::cblas_trsm<nm::Rational128>, nm::math::cblas_trsm<nm::RubyObject> }; dtype_t dtype = NM_DTYPE(a); void *pAlpha = ALLOCA_N(char, DTYPE_SIZES[dtype]); rubyval_to_cval(alpha, dtype, pAlpha); ttable[dtype](blas_order_sym(order), blas_side_sym(side), blas_uplo_sym(uplo), blas_transpose_sym(trans_a), blas_diag_sym(diag), FIX2INT(m), FIX2INT(n), pAlpha, NM_STORAGE_DENSE(a)->elements, FIX2INT(lda), NM_STORAGE_DENSE(b)->elements, FIX2INT(ldb)); return Qtrue; }
/* void sincpt_c : Given an observer and a direction vector defining a ray, compute the surface intercept of the ray on a target body at a specified epoch, optionally corrected for light time and stellar aberration. void sincpt_c ( ConstSpiceChar * method, ConstSpiceChar * target, SpiceDouble et, ConstSpiceChar * fixref, ConstSpiceChar * abcorr, ConstSpiceChar * obsrvr, ConstSpiceChar * dref, ConstSpiceDouble dvec [3], SpiceDouble spoint [3], SpiceDouble * trgepc, SpiceDouble srfvec [3], SpiceBoolean * found ) */ static VALUE sincpt(VALUE self, VALUE method, VALUE target, VALUE et, VALUE fixref, VALUE abcorr, VALUE obsrvr, VALUE dref, VALUE dvec) { //C containers to pass on to SPICE function /* Input argv[0] -> Target argv[1] -> Ephemeris Time argv[2] -> Fixed Reference argv[3] -> Aberration Correcton *IGNORED* if Arguments passed is argv[4] -> Observer Body Name argv[5] -> Reference Frame of Ray's direction vector argv[6] -> Ray's direction Vector */ //Output parameters SpiceBoolean found; double intercept_epoch, surface_point[3], surface_vector[3]; //Arrays that we return to Ruby VALUE rb_vector; VALUE rb_point; sincpt_c(StringValuePtr(method), StringValuePtr(target), NUM2DBL(et), StringValuePtr(fixref), StringValuePtr(abcorr), StringValuePtr(obsrvr), StringValuePtr(dref), NM_STORAGE_DENSE(dvec)->elements, surface_point, &intercept_epoch, surface_vector, &found); if(!found) { return Qfalse; } else if(spice_error(SPICE_ERROR_SHORT)) { return Qnil; } rb_point = rb_nmatrix_dense_create(FLOAT64, (size_t *) VECTOR_SHAPE, 2, (void *) surface_point, 3); rb_vector = rb_nmatrix_dense_create(FLOAT64, (size_t *) VECTOR_SHAPE, 2, (void *) surface_vector, 3); return rb_ary_new3(3, rb_point, rb_vector, DBL2NUM(intercept_epoch)); }
/* * Calculates a function at x, and returns the rusult. */ static VALUE rb_gsl_function_eval(VALUE obj, VALUE x) { gsl_function *F = NULL; VALUE ary, proc, params, result, arynew, x2; gsl_vector *v = NULL, *vnew = NULL; gsl_matrix *m = NULL, *mnew = NULL; size_t i, j, n; Data_Get_Struct(obj, gsl_function, F); ary = (VALUE) F->params; proc = rb_ary_entry(ary, 0); params = rb_ary_entry(ary, 1); if (CLASS_OF(x) == rb_cRange) x = rb_gsl_range2ary(x); switch (TYPE(x)) { case T_FIXNUM: case T_BIGNUM: case T_FLOAT: if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x); else result = rb_funcall(proc, RBGSL_ID_call, 2, x, params); return result; break; case T_ARRAY: // n = RARRAY(x)->len; n = RARRAY_LEN(x); arynew = rb_ary_new2(n); for (i = 0; i < n; i++) { x2 = rb_ary_entry(x, i); Need_Float(x2); if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x2); else result = rb_funcall(proc, RBGSL_ID_call, 2, x2, params); rb_ary_store(arynew, i, result); } return arynew; break; default: #ifdef HAVE_NARRAY_H if (NA_IsNArray(x)) { double *ptr1, *ptr2; struct NARRAY *na; GetNArray(x, na); ptr1 = (double *) na->ptr; n = na->total; ary = na_make_object(NA_DFLOAT, na->rank, na->shape, CLASS_OF(x)); ptr2 = NA_PTR_TYPE(ary, double*); for (i = 0; i < n; i++) { x2 = rb_float_new(ptr1[i]); if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x2); else result = rb_funcall(proc, RBGSL_ID_call, 2, x2, params); ptr2[i] = NUM2DBL(result); } return ary; } #endif #ifdef HAVE_NMATRIX_H if (NM_IsNMatrix(x)) { double *ptr1, *ptr2; NM_DENSE_STORAGE *nm; nm = NM_STORAGE_DENSE(x); ptr1 = (double *) nm->elements; n = NM_DENSE_COUNT(x); ary = rb_nmatrix_dense_create(FLOAT64, nm->shape, nm->dim, nm->elements, n); ptr2 = (double*)NM_DENSE_ELEMENTS(ary); for (i = 0; i < n; i++) { x2 = rb_float_new(ptr1[i]); if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x2); else result = rb_funcall(proc, RBGSL_ID_call, 2, x2, params); ptr2[i] = NUM2DBL(result); } return ary; } #endif if (VECTOR_P(x)) { Data_Get_Struct(x, gsl_vector, v); vnew = gsl_vector_alloc(v->size); for (i = 0; i < v->size; i++) { x2 = rb_float_new(gsl_vector_get(v, i)); if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x2); else result = rb_funcall(proc, RBGSL_ID_call, 2, x2, params); gsl_vector_set(vnew, i, NUM2DBL(result)); } return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, vnew); } else if (MATRIX_P(x)) { Data_Get_Struct(x, gsl_matrix, m); mnew = gsl_matrix_alloc(m->size1, m->size2); for (i = 0; i < m->size1; i++) { for (j = 0; j < m->size2; j++) { x2 = rb_float_new(gsl_matrix_get(m, i, j)); if (NIL_P(params)) result = rb_funcall(proc, RBGSL_ID_call, 1, x2); else result = rb_funcall(proc, RBGSL_ID_call, 2, x2, params); gsl_matrix_set(mnew, i, j, NUM2DBL(result)); } } return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, mnew); } else { rb_raise(rb_eTypeError, "wrong argument type"); } break; } /* never reach here */ return Qnil; }