Returning array from Raku and C function does not match in nativecall

207 Views Asked by At

I was looking to call a C function https://github.com/wch/r-source/blob/trunk/src/nmath/rmultinom.c#L47 from Raku. For this I have defined Raku function as

use NativeCall;
constant RMATH = "./Rmath.dll";
multi set_seed(uint32, uint32)
    is native( RMATH ) { * };
multi set_seed(UInt() $a, UInt() $b) is export {
    set_seed(my uint32 $ = $a, my uint32 $ = $b)
}
sub rmultinom(int32, CArray[num64], int32, CArray[int32])
    is native( RMATH ) { * }; 

sub raku_rmultinom($n , $size, @prob is copy) is export {
    @prob = @prob.map: {$_.Num};
    my $prob = CArray[num64].new(@prob);
    my $ints = CArray[int32].allocate($size);
    rmultinom($n, $prob, $size, $ints);
    return $ints.list
}

A call

my @prob = [0.1, 0.3, 0.5, 0.1];
set_seed(123,456);
say raku_rmultinom(100, 5, @prob);

gives

(9 29 49 13 0)

conformant with C code:

#define MATHLIB_STANDALONE 1
#include "Rmath.h"
#include <stdio.h>

int main(int argc, char** argv) {
    int draws = 100;
    int classes = 5;
    int vals[classes];
    double probs[4] = {0.1, 0.3, 0.5, 0.1};

    set_seed(123, 456);
    rmultinom(draws, probs, classes, vals);

    for(int j=0; j < classes; j++) {
        printf("Count of class %i drawn: %i\n", j, vals[j]);
    }
    return 0;
}
Count of class 0 drawn: 9
Count of class 1 drawn: 29
Count of class 2 drawn: 49
Count of class 3 drawn: 13
Count of class 4 drawn: 0

But when I increase size to 10, see example below, the output seems weird:

my @prob = [0.1, 0.3, 0.5, 0.1];
set_seed(123,456);
say raku_rmultinom(100, 10, @prob);
(0 0 0 0 0 -1 0 0 0 0) # expectation was (9 29 49 13 0 0 0 0 0 0)

while in C:

int main(int argc, char** argv) {
    int draws = 100;
    int classes = 10;
    int vals[classes];
    double probs[4] = {0.1, 0.3, 0.5, 0.1};

    set_seed(123, 456);
    rmultinom(draws, probs, classes, vals);

    for(int j=0; j < classes; j++) {
        printf("Count of class %i drawn: %i\n", j, vals[j]);
    }
    return 0;
}
Count of class 0 drawn: 9
Count of class 1 drawn: 29
Count of class 2 drawn: 49
Count of class 3 drawn: 13
Count of class 4 drawn: 0
Count of class 5 drawn: 0
Count of class 6 drawn: 0
Count of class 7 drawn: 0
Count of class 8 drawn: 0
Count of class 9 drawn: 0

Is there any issue with implementation of raku_rmultinom in my Raku code?

System info: This is done in Windows 10 64-bit. For reproducibility, I have provided the header and dynamic library here: https://replit.com/@sumankhanal/rakunativecall.

To test C file with the Rmath.h header and library in the same folder, move to that directory and run from cmd line:

gcc -I. -L. <cfile> -lRmath
.\a.exe

Or else, I am the author of this package https://github.com/sumanstats/Statistics which has raku_rmultinom available globally, anyone can install and test the output and its behavior.


The version of Raku used is as downloaded from https://rakudo.org/dl/rakudo/rakudo-moar-2023.09-01-win-x86_64-msvc.zip; raku -v gives:

Welcome to Rakudo™ v2023.09.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2023.09.
1

There are 1 best solutions below

0
raiph On

I think it's worth summing up what happened in this SO.

First, what @Suman (the asker) concluded (in the comments below their question) was what "looks like" the answer:

@prob.append(0 xx $size - @prob)

This reflects the fact that for this Q, as is sometimes the case when bridging the gap between C and Raku, the problem is mismatches between Raku norms (which generally make sense when coding in Raku) and C norms (which generally make sense when coding in C) rather than problems with the technology on either side. Some excerpts from the comment exchange are illustrative of the process of getting to the bottom of such a mismatch:

Tested on Ubuntu ... Output is as expected here: (9 29 49 13 0 0 0 0 0 0)

Raku is a dynamic language, C is static. You need to be very careful with array sizes. There's no problem with 4 because it's exactly the size of the @prob array.

In the Raku part, when $size > +@prob, you are invoking undefined behaviour ... A remedy could be making sure @prob has enough elements with 0s padded, e.g., via @prob.append(0 xx $size - @prob); in raku_rmultinom.

in C, when you declare a variable but not initialize it, it will be given a "garbage" value, i.e., whatever value happens to sit at the memory address appointed to the variable. For arrays, all of its elements will have who-knows-what values like 1635326112 or 32758 or 0 as you observed. In Rakudo's NativeCall, on the other hand, the .allocate method happens to initialize the array elements with 0s explicitly, so you have all 0s (except for that -1) there.