2

I am passing an array (matrix) from Ruby to a C function. At the moment I am using the following code

VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
{
    int rowsA = RARRAY_LEN(matrixA);
    VALUE firstElement = rb_ary_entry(matrixA, 0);
    int colsA = RARRAY_LEN(firstElement);
    int rowsB = RARRAY_LEN(matrixB);
    firstElement = rb_ary_entry(matrixB, 0);
    int colsB = RARRAY_LEN(firstElement);

    int i,j;
    double *matA = (double *)malloc(rowsA * colsA * sizeof(double));
    double *matB = (double *)malloc(rowsB * colsB * sizeof(double));

    VALUE rowA;
    for (i=0; i<rowsA; i++)
    {
        rowA = rb_ary_entry(matrixA, i);
        for (j=0; j<colsA; j++)
        {
            matA[i * colsA + j] = NUM2DBL(rb_ary_entry( rowA, j));
        }
    }

    //  same for matrix B
    ....
    ....

    // Perform operation C = A x B

    VALUE matrixC = rb_ary_new2(rowsC);
    VALUE rowC;
     for (i=0; i<rowsC; i++) {
         rowC = rb_ary_new2(colsC);
         for (j=0; j<colsC; j++) {
              rb_ary_store(rowC, j, DBL2NUM(matC[i * colsC + j]));
         }
         rb_ary_store(matrixC, i, rowC);
     }


return matrixC
}

Is there a better/quicker way to convert a Ruby array to a C array and viceversa?

1 Answer 1

3

No there is not a quicker way to convert Ruby Array to a C structure. That's because the Ruby Array could contain a mixture of any other kind of Ruby object, many of which could not be converted to a C double

There is another option though - NArray. This is a very efficient way of dealing with numerical multi-dimensional arrays in Ruby. There is a lot less procedure converting from an NArray to C, but it is entirely different way of doing things.

Some of it is a little complex. In summary . . .

Load the narray.h library in extconf.rb

Original version of this was from fftw3 gem (I have simplified a little):

require "mkmf"
require "narray"

narray_dir = File.dirname(Gem.find_files("narray.h").first) rescue $sitearchdir
dir_config('narray', narray_dir, narray_dir)

if ( ! ( have_header("narray.h") && have_header("narray_config.h") ) )
   puts "Header narray.h or narray_config.h is not found."
   exit(-1)
end

create_makefile( 'my_lib_name/my_lib_name' )

Cast input NArray objects to the data type you want to work with

Here's an example instance method that can access the NArray

VALUE example_narray_param( VALUE self, VALUE rv_narray ) {
  // Cast the input to the data type you want - here 32-bit ints
  volatile VALUE new_narray = na_cast_object(rv_narray, NA_LINT);

  // NARRAY is the C struct interface to NArray data
  struct NARRAY *na_items;

  // This macro is NArray's equivalent of NUM2DBL, pointing na_items 
  // at the data
  GetNArray( new_narray, na_items );

  // row now points natively to the data
  int * row = (int*) na_items->ptr;

For multi-dimensional arrays like your matrix, NArray uses a single pointer with multiplier offsets, similar to your matA[i * colsA + j] - going into full detail on this would be too long, but hopefully this is enough of a start to help you decide if this is the right solution for you.

I actually use this approach a lot in some personal projects. They are MIT licensed, so feel free to look through them and copy or re-use anything. This neural network layer class might contain some useful reference code.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the additional information. I will stick with my approach for now as I have other priorities, but I will definitely look back at your proposed alternative in the near future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.