[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

C Extension help

thomas.luce

6/9/2006 2:46:00 AM

Hi all,

I was wondering if I could get some people to take a look at some code.
I have narrowed down where the problem is, just not what the problem
is. I haven't coded in C in quite some time, so I'm re-learning a lot.
Basically, I get a seg fault on the Data_Get_Struct when net_update is
called. I figured it has something to do with the stuff in net_new.
Heres the code:

<code>
typedef struct NN
{
//Activations of each of those layers
float *ai;
float *ah;
float *ao;

// We are making pointers to arrays, not arrays of pointers.
//float *nodes;
float **wi;
float **wo;

//Change in weights used for momentum calculations
float **ci;
float **co;

//Number of nodes in each layer
int ni;
int nh;
int no;
} NN;

static VALUE
net_new(VALUE self, VALUE ni, VALUE nh, VALUE no)
{
//TODO: something about the way I am allocating net is causing it to
crap out on me in update.
NN *net;
VALUE obj = Data_Make_Struct(self,NN,NULL,net_free,net);

//Set layer sizes
net->ni = NUM2INT(ni) + 1;
net->nh = NUM2INT(nh);
net->no = NUM2INT(no);

//Set activations
net->ai = calloc(NUM2INT(ni)+1, sizeof(float));
net->ah = calloc(NUM2INT(nh), sizeof(float));
net->ao = calloc(NUM2INT(no), sizeof(float));
if(net->ai == NULL || net->ah == NULL || net->ao == NULL)
rb_raise(rb_eRuntimeError, "could not allocate activations");

//Build and randomize the link weights
net->wi = malloc((net->ni+ 1) * sizeof(float));
net->wo = malloc(net->nh * sizeof(float));
if(net->wi == NULL || net->wo == NULL)
rb_raise(rb_eRuntimeError, "could not allocate weight tables [1]");

int i;
int y;
for(i = 0; i < NUM2INT(nh); i++)
{
net->wi[i] = malloc(NUM2INT(nh) * sizeof(float));
if(net->wi[i] == NULL)
rb_raise(rb_eRuntimeError, "could not allocate weight
tables[2]");

for(y = 0; y < net->nh; y++)
{
net->wi[i][y] = -1.0 + (float)rand()/RAND_MAX;
}
}
for(i = 0; i < NUM2INT(no); i++)
{
net->wo[i] = malloc(NUM2INT(no) * sizeof(float));
if(net->wo[i] == NULL)
rb_raise(rb_eRuntimeError, "could not allocate weight
tables[3]");

for(y = 0; y < net->no; y++)
{
net->wo[i][y] = -1.0 + (float)rand()/RAND_MAX;
}
}

//Build our change in weight (momentum) arrays
net->ci = malloc(sizeof(float)*(NUM2INT(ni)+1));
net->co = malloc(sizeof(float)*NUM2INT(nh));
if(net->ci == NULL || net->co == NULL)
rb_raise(rb_eRuntimeError, "could not allocate change tables[1]");

for(i = 0; i < NUM2INT(nh); i++)
{
net->ci[i] = calloc(NUM2INT(nh), sizeof(float));
if(net->ci[i] == NULL || net->co == NULL)
rb_raise(rb_eRuntimeError, "could not allocate change
tables[2]");
}
for(i = 0; i < NUM2INT(no); i++)
{
net->co[i] = calloc(NUM2INT(no), sizeof(float));
if(net->co[i] == NULL || net->co == NULL)
rb_raise(rb_eRuntimeError, "could not allocate change
tables[3]");
}

return obj;
}
static VALUE
net_update(VALUE self, VALUE input)
{
NN *net;
Data_Get_Struct(self,NN,net);

int i;
int y;
//Inputs
for(i = 0; i < net->ni - 1; i++)
{
net->ai[i] = (float)NUM2DBL(rb_ary_entry(input,i));
}

//Hidden
float sum = 0.0;
for(i = 0; i < net->nh; i++)
{
sum = 0.0;
for(y = 0; y < net->ni; y++)
{
sum = sum + net->ai[y] * net->wi[y][i];
}
net->ah[i] = sigmoid(sum);
}

//Outputs
for(i = 0; i < net->no; i++)
{
sum = 0.0;
for(y = 0; y < net->nh; y++)
{
sum = sum + net->ah[y] * net->wo[y][i];
}
net->ao[i] = sigmoid(sum);
}

//Now, stuff it all in an array, and throw it back.
VALUE ary = rb_ary_new();
for(i = 0; i < net->no; i++)
{
rb_ary_push(ary, rb_float_new(net->ao[i]));
}
return ary;
}
</code>

Thanks to anyone that can offer any insight.

-Thomas

2 Answers

gmurray

6/10/2006 10:16:00 AM

0

Hello Thomas,

There is still a learning curve ahead of you for the C.

There were parts of the code that were not posted, or missing:
net_free()
The function declarations (see Init_exx() following this part)
There was no call to initialize the class. In the following
example, look at rb_obj_call_init() which calls "initialize"

The code had malloc() and calloc() calls, but no corresponding calls
to free(). It would be a good improvement to use ruby's
allocation to avoid memory leaks. See how this was done in the
following example with the macro ALLOC(); this will be cleaned up
when exx_free() is called. Data_Wrap_Struct passes the address
of exx_free(). For just starting out, avoid using C allocation calls.
You might find the following example helpful. To keep the debugging
simple, start from a simple program that works; then add the more
complex stuff later. Other examples of wrapping can be found in
the ruby application archive at www.ruby-lang.org or www.rubyforge.net

Good luck,
Gerald

-- FILE exx.c--
/* exx.c */
#include <ruby.h>

typedef struct Udata{
int c;
}Udata;

VALUE cExx;

static VALUE
exx_init(VALUE self, VALUE unit){
return self;
}

static void
exx_free(Udata *pr)
{
free(pr);
}

static VALUE
exx_new(VALUE class)
{
VALUE argv[1],tt;
Udata *pr;
pr = ALLOC(Udata);
tt = Data_Wrap_Struct(class,0,exx_free,pr);
argv[0] = tt;
rb_obj_call_init(tt,1,argv); // call exx_init to initialize
pr->c = 0;

return tt;
}

static VALUE
exx_verify(VALUE self, VALUE num)
{
Udata *pr;
int c = FIX2INT(num);

Data_Get_Struct(self,Udata,pr);
printf("val was %d, changed to %d\n",pr->c,c);
pr->c = c;

return self;
}

void Init_exx(){
cExx = rb_define_class("Exx",rb_cObject);
rb_define_singleton_method(cExx,"new",exx_new,0);
rb_define_method(cExx,"initialize",exx_init,1);
rb_define_method(cExx,"verify",exx_verify,1);
}

// vim: sts=2 sw=2 ts=4 et ai tw=77

-- FILE extconf.rb --
#extconf.rb
require "mkmf"
create_makefile("exx")

-- FILE useexx.rb --
#!/usr/bin/ruby
#useexx.rb
require 'exx'

a = Exx.new()
a.verify(1)
a.verify(2)
puts "that's all"

-- PROGRAM --
% ./useexx.rb
val was 0, changed to 1
val was 1, changed to 2
that's all

thomas.luce

6/13/2006 2:38:00 PM

0

Yeah, I realize this. I didn't post some of the code because it wasn't
part of the issue I was having. I've been coding since I was 9, and
haven't done C in about 5 years, so it's starting to come back to me.

Anyway, I got it figured out already, but thank you for your input! It
is very appreciated!

-Thomas
Gerald Murray wrote:
> Hello Thomas,
>
> There is still a learning curve ahead of you for the C.
>
> There were parts of the code that were not posted, or missing:
> net_free()
> The function declarations (see Init_exx() following this part)
> There was no call to initialize the class. In the following
> example, look at rb_obj_call_init() which calls "initialize"
>
> The code had malloc() and calloc() calls, but no corresponding calls
> to free(). It would be a good improvement to use ruby's
> allocation to avoid memory leaks. See how this was done in the
> following example with the macro ALLOC(); this will be cleaned up
> when exx_free() is called. Data_Wrap_Struct passes the address
> of exx_free(). For just starting out, avoid using C allocation calls.
> You might find the following example helpful. To keep the debugging
> simple, start from a simple program that works; then add the more
> complex stuff later. Other examples of wrapping can be found in
> the ruby application archive at www.ruby-lang.org or www.rubyforge.net
>
> Good luck,
> Gerald
>
> -- FILE exx.c--
> /* exx.c */
> #include <ruby.h>
>
> typedef struct Udata{
> int c;
> }Udata;
>
> VALUE cExx;
>
> static VALUE
> exx_init(VALUE self, VALUE unit){
> return self;
> }
>
> static void
> exx_free(Udata *pr)
> {
> free(pr);
> }
>
> static VALUE
> exx_new(VALUE class)
> {
> VALUE argv[1],tt;
> Udata *pr;
> pr = ALLOC(Udata);
> tt = Data_Wrap_Struct(class,0,exx_free,pr);
> argv[0] = tt;
> rb_obj_call_init(tt,1,argv); // call exx_init to initialize
> pr->c = 0;
>
> return tt;
> }
>
> static VALUE
> exx_verify(VALUE self, VALUE num)
> {
> Udata *pr;
> int c = FIX2INT(num);
>
> Data_Get_Struct(self,Udata,pr);
> printf("val was %d, changed to %d\n",pr->c,c);
> pr->c = c;
>
> return self;
> }
>
> void Init_exx(){
> cExx = rb_define_class("Exx",rb_cObject);
> rb_define_singleton_method(cExx,"new",exx_new,0);
> rb_define_method(cExx,"initialize",exx_init,1);
> rb_define_method(cExx,"verify",exx_verify,1);
> }
>
> // vim: sts=2 sw=2 ts=4 et ai tw=77
>
> -- FILE extconf.rb --
> #extconf.rb
> require "mkmf"
> create_makefile("exx")
>
> -- FILE useexx.rb --
> #!/usr/bin/ruby
> #useexx.rb
> require 'exx'
>
> a = Exx.new()
> a.verify(1)
> a.verify(2)
> puts "that's all"
>
> -- PROGRAM --
> % ./useexx.rb
> val was 0, changed to 1
> val was 1, changed to 2
> that's all