#include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* * Macros and symbolic constants */ #define RAND_WEIGHT ( ((float)rand() / (float)RAND_MAX) - 0.5 ) #define sqr(x) ((x) * (x)) typedef struct { double **input_to_hidden; double **hidden_to_output; } SYNAPSE; SYNAPSE weight; typedef struct { double *hidden; double *output; } ERROR; ERROR error; typedef struct { double *input; double *hidden; double *output; double *target; } LAYER; LAYER neuron; typedef struct { int input; int hidden; int output; } NEURON_COUNT; typedef struct { float learn_rate; double delta; int use_bipolar; SYNAPSE weight; ERROR error; LAYER neuron; NEURON_COUNT size; double *tmp; } NEURAL_NETWORK; int networks = 0; NEURAL_NETWORK **network = NULL; AV* get_array_from_aoa(SV* scalar, int index); AV* get_array(SV* aref); SV* get_element(AV* array, int index); double sigmoid(NEURAL_NETWORK *n, double val); double sigmoid_derivative(NEURAL_NETWORK *n, double val); float get_float_element(AV* array, int index); int is_array_ref(SV* ref); void c_assign_random_weights(NEURAL_NETWORK *); void c_back_propagate(NEURAL_NETWORK *); void c_destroy_network(int); void c_feed(NEURAL_NETWORK *, double *input, double *output, int learn); void c_feed_forward(NEURAL_NETWORK *); float c_get_learn_rate(int); void c_set_learn_rate(int, float); SV* c_export_network(int handle); int c_import_network(SV *); #define ABS(x) ((x) > 0.0 ? (x) : -(x)) int is_array_ref(SV* ref) { if (SvROK(ref) && SvTYPE(SvRV(ref)) == SVt_PVAV) return 1; else return 0; } double sigmoid(NEURAL_NETWORK *n, double val) { return 1.0 / (1.0 + exp(-n->delta * val)); } double sigmoid_derivative(NEURAL_NETWORK *n, double val) { /* * It's always called with val=sigmoid(x) and we want sigmoid'(x). * * Since sigmoid'(x) = delta * sigmoid(x) * (1 - sigmoid(x)) * the value we return is extremely simple. * * sigmoid_derivative(x) is NOT sigmoid'(x). */ return n->delta * val * (1.0 - val); } /* Not using tanh() as this is already defined in math headers */ double hyperbolic_tan(NEURAL_NETWORK *n, double val) { double epx = exp(n->delta * val); double emx = exp(-n->delta * val); return (epx - emx) / (epx + emx); } double hyperbolic_tan_derivative(NEURAL_NETWORK *n, double val) { /* * It's always called with val=tanh(delta*x) and we want tanh'(delta*x). * * Since tanh'(delta*x) = delta * (1 - tanh(delta*x)^2) * the value we return is extremely simple. * * hyperbolic_tan_derivative(x) is NOT tanh'(x). */ return n->delta * (1.0 - val * val); } AV* get_array(SV* aref) { if (! is_array_ref(aref)) croak("get_array() argument is not an array reference"); return (AV*) SvRV(aref); } float get_float_element(AV* array, int index) { SV **sva; SV *sv; sva = av_fetch(array, index, 0); if (!sva) return 0.0; sv = *sva; return SvNV(sv); } SV* get_element(AV* array, int index) { SV **temp; temp = av_fetch(array, index, 0); if (!temp) croak("Item %d in array is not defined", index); return *temp; } AV* get_array_from_aoa(SV* aref, int index) { SV *elem; AV *array; /* dereference array and get requested arrayref */ array = get_array(aref); elem = get_element(array, index); /* dereference array ref */ return get_array(elem); } NEURAL_NETWORK *c_get_network(int handle) { NEURAL_NETWORK *n; if (handle < 0 || handle >= networks) croak("Invalid neural network handle"); n = network[handle]; if (n == NULL) croak("Stale neural network handle"); return n; } int c_new_handle(void) { int handle = -1; /* * Allocate the network array if not already done. * Then allocate a new handle for the network. */ if (network == NULL) { int i; networks = 10; network = malloc(networks * sizeof(*network)); for (i = 0; i < networks; i++) network[i] = NULL; handle = 0; } else { int i; for (i = 0; i < networks; i++) { if (network[i] == NULL) { handle = i; break; } } if (handle == -1) { handle = networks; networks += 10; network = realloc(network, networks * sizeof(*network)); for (i = networks - 10; i < networks; i++) network[i] = NULL; } } network[handle] = malloc(sizeof(NEURAL_NETWORK)); return handle; } float c_get_learn_rate(int handle) { NEURAL_NETWORK *n = c_get_network(handle); return n->learn_rate; } void c_set_learn_rate(int handle, float rate) { NEURAL_NETWORK *n = c_get_network(handle); n->learn_rate = rate; } double c_get_delta(int handle) { NEURAL_NETWORK *n = c_get_network(handle); return n->delta; } void c_set_delta(int handle, double delta) { NEURAL_NETWORK *n = c_get_network(handle); n->delta = delta; } int c_get_use_bipolar(int handle) { NEURAL_NETWORK *n = c_get_network(handle); return n->use_bipolar; } void c_set_use_bipolar(int handle, int bipolar) { NEURAL_NETWORK *n = c_get_network(handle); n->use_bipolar = bipolar; } int c_create_network(NEURAL_NETWORK *n) { int i; /* each of the next two variables has an extra row for the "bias" */ int input_layer_with_bias = n->size.input + 1; int hidden_layer_with_bias = n->size.hidden + 1; n->learn_rate = .2; n->delta = 1.0; n->use_bipolar = 0; n->tmp = malloc(sizeof(double) * n->size.input); n->neuron.input = malloc(sizeof(double) * n->size.input); n->neuron.hidden = malloc(sizeof(double) * n->size.hidden); n->neuron.output = malloc(sizeof(double) * n->size.output); n->neuron.target = malloc(sizeof(double) * n->size.output); n->error.hidden = malloc(sizeof(double) * n->size.hidden); n->error.output = malloc(sizeof(double) * n->size.output); /* one extra for sentinel */ n->weight.input_to_hidden = malloc(sizeof(void *) * (input_layer_with_bias + 1)); n->weight.hidden_to_output = malloc(sizeof(void *) * (hidden_layer_with_bias + 1)); if(!n->weight.input_to_hidden || !n->weight.hidden_to_output) { printf("Initial malloc() failed\n"); return 0; } /* now allocate the actual rows */ for(i = 0; i < input_layer_with_bias; i++) { n->weight.input_to_hidden[i] = malloc(hidden_layer_with_bias * sizeof(double)); if(n->weight.input_to_hidden[i] == 0) { free(*n->weight.input_to_hidden); printf("Second malloc() to weight.input_to_hidden failed\n"); return 0; } } /* now allocate the actual rows */ for(i = 0; i < hidden_layer_with_bias; i++) { n->weight.hidden_to_output[i] = malloc(n->size.output * sizeof(double)); if(n->weight.hidden_to_output[i] == 0) { free(*n->weight.hidden_to_output); printf("Second malloc() to weight.hidden_to_output failed\n"); return 0; } } /* initialize the sentinel value */ n->weight.input_to_hidden[input_layer_with_bias] = 0; n->weight.hidden_to_output[hidden_layer_with_bias] = 0; return 1; } void c_destroy_network(int handle) { double **row; NEURAL_NETWORK *n = c_get_network(handle); for(row = n->weight.input_to_hidden; *row != 0; row++) { free(*row); } free(n->weight.input_to_hidden); for(row = n->weight.hidden_to_output; *row != 0; row++) { free(*row); } free(n->weight.hidden_to_output); free(n->neuron.input); free(n->neuron.hidden); free(n->neuron.output); free(n->neuron.target); free(n->error.hidden); free(n->error.output); free(n->tmp); network[handle] = NULL; } /* * Build a Perl reference on array `av'. * This performs something like "$rv = \@av;" in Perl. */ SV *build_rv(AV *av) { SV *rv; /* * To understand what is going on here, look at retrieve_ref() * in the Storable.xs file. In particular, we don't perform * an SvREFCNT_inc(av) because the av we're supplying is going * to be referenced only by the REF we're building here. * --RAM */ rv = NEWSV(10002, 0); sv_upgrade(rv, SVt_RV); SvRV(rv) = (SV *) av; SvROK_on(rv); return rv; } /* * Build reference to a 2-dimensional array, implemented as an array * or array references. The holding array has `rows' rows and each array * reference has `columns' entries. * * The name "axa" denotes the "product" of 2 arrays. */ SV *build_axaref(void *arena, int rows, int columns) { AV *av; int i; double **p; av = newAV(); av_extend(av, rows); for (i = 0, p = arena; i < rows; i++, p++) { int j; double *q; AV *av2; av2 = newAV(); av_extend(av2, columns); for (j = 0, q = *p; j < columns; j++, q++) av_store(av2, j, newSVnv((NV) *q)); av_store(av, i, build_rv(av2)); } return build_rv(av); } #define EXPORT_VERSION 1 #define EXPORTED_ITEMS 9 /* * Exports the C data structures to the Perl world for serialization * by Storable. We don't want to duplicate the logic of Storable here * even though we have to do some low-level Perl object construction. * * The structure we return is an array reference, which contains the * following items: * * 0 the export version number, in case format changes later * 1 the amount of neurons in the input layer * 2 the amount of neurons in the hidden layer * 3 the amount of neurons in the output layer * 4 the learning rate * 5 the sigmoid delta * 6 whether to use a bipolar (tanh) routine instead of the sigmoid * 7 [[weight.input_to_hidden[0]], [weight.input_to_hidden[1]], ...] * 8 [[weight.hidden_to_output[0]], [weight.hidden_to_output[1]], ...] */ SV *c_export_network(int handle) { NEURAL_NETWORK *n = c_get_network(handle); AV *av; int i = 0; av = newAV(); av_extend(av, EXPORTED_ITEMS); av_store(av, i++, newSViv(EXPORT_VERSION)); av_store(av, i++, newSViv(n->size.input)); av_store(av, i++, newSViv(n->size.hidden)); av_store(av, i++, newSViv(n->size.output)); av_store(av, i++, newSVnv(n->learn_rate)); av_store(av, i++, newSVnv(n->delta)); av_store(av, i++, newSViv(n->use_bipolar)); av_store(av, i++, build_axaref(n->weight.input_to_hidden, n->size.input + 1, n->size.hidden + 1)); av_store(av, i++, build_axaref(n->weight.hidden_to_output, n->size.hidden + 1, n->size.output)); if (i != EXPORTED_ITEMS) croak("BUG in c_export_network()"); return build_rv(av); } /* * Load a Perl array of array (a matrix) with "rows" rows and "columns" columns * into the pre-allocated C array of arrays. * * The "hold" argument is an holding array and the Perl array of array which * we expect is at index "idx" within that holding array. */ void c_load_axa(AV *hold, int idx, void *arena, int rows, int columns) { SV **sav; SV *rv; AV *av; int i; double **array = arena; sav = av_fetch(hold, idx, 0); if (sav == NULL) croak("serialized item %d is not defined", idx); rv = *sav; if (!is_array_ref(rv)) croak("serialized item %d is not an array reference", idx); av = get_array(rv); /* This is an array of array refs */ for (i = 0; i < rows; i++) { double *row = array[i]; int j; AV *subav; sav = av_fetch(av, i, 0); if (sav == NULL) croak("serialized item %d has undefined row %d", idx, i); rv = *sav; if (!is_array_ref(rv)) croak("row %d of serialized item %d is not an array ref", i, idx); subav = get_array(rv); for (j = 0; j < columns; j++) row[j] = get_float_element(subav, j); } } /* * Create new network from a retrieved data structure, such as the one * produced by c_export_network(). */ int c_import_network(SV *rv) { NEURAL_NETWORK *n; int handle; SV **sav; AV *av; int i = 0; /* * Unfortunately, since those data come from the outside, we need * to validate most of the structural information to make sure * we're not fed garbage or something we cannot process, like a * newer version of the serialized data. This makes the code heavy. * --RAM */ if (!is_array_ref(rv)) croak("c_import_network() not given an array reference"); av = get_array(rv); /* Check version number */ sav = av_fetch(av, i++, 0); if (sav == NULL || SvIVx(*sav) != EXPORT_VERSION) croak("c_import_network() given unknown version %d", sav == NULL ? 0 : SvIVx(*sav)); /* Check length -- at version 1, length is fixed to 13 */ if (av_len(av) + 1 != EXPORTED_ITEMS) croak("c_import_network() not given a %d-item array reference", EXPORTED_ITEMS); handle = c_new_handle(); n = c_get_network(handle); sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined input size (item %d)", i - 1); n->size.input = SvIVx(*sav); sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined hidden size (item %d), i - 1"); n->size.hidden = SvIVx(*sav); sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined output size (item %d)", i - 1); n->size.output = SvIVx(*sav); if (!c_create_network(n)) return -1; sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined learn_rate (item %d)", i - 1); n->learn_rate = SvNVx(*sav); sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined delta (item %d)", i - 1); n->delta = SvNVx(*sav); sav = av_fetch(av, i++, 0); if (sav == NULL) croak("undefined use_bipolar (item %d)", i - 1); n->use_bipolar = SvIVx(*sav); c_load_axa(av, i++, n->weight.input_to_hidden, n->size.input + 1, n->size.hidden + 1); c_load_axa(av, i++, n->weight.hidden_to_output, n->size.hidden + 1, n->size.output); return handle; } /* * Support functions for back propogation */ void c_assign_random_weights(NEURAL_NETWORK *n) { int hid, inp, out; for (inp = 0; inp < n->size.input + 1; inp++) { for (hid = 0; hid < n->size.hidden; hid++) { n->weight.input_to_hidden[inp][hid] = RAND_WEIGHT; } } for (hid = 0; hid < n->size.hidden + 1; hid++) { for (out = 0; out < n->size.output; out++) { n->weight.hidden_to_output[hid][out] = RAND_WEIGHT; } } } /* * Feed-forward Algorithm */ void c_feed_forward(NEURAL_NETWORK *n) { int inp, hid, out; double sum; double (*activation)(NEURAL_NETWORK *, double); activation = n->use_bipolar ? hyperbolic_tan : sigmoid; /* calculate input to hidden layer */ for (hid = 0; hid < n->size.hidden; hid++) { sum = 0.0; for (inp = 0; inp < n->size.input; inp++) { sum += n->neuron.input[inp] * n->weight.input_to_hidden[inp][hid]; } /* add in bias */ sum += n->weight.input_to_hidden[n->size.input][hid]; n->neuron.hidden[hid] = (*activation)(n, sum); } /* calculate the hidden to output layer */ for (out = 0; out < n->size.output; out++) { sum = 0.0; for (hid = 0; hid < n->size.hidden; hid++) { sum += n->neuron.hidden[hid] * n->weight.hidden_to_output[hid][out]; } /* add in bias */ sum += n->weight.hidden_to_output[n->size.hidden][out]; n->neuron.output[out] = (*activation)(n, sum); } } /* * Back-propogation algorithm. This is where the learning gets done. */ void c_back_propagate(NEURAL_NETWORK *n) { int inp, hid, out; double (*activation_derivative)(NEURAL_NETWORK *, double); activation_derivative = n->use_bipolar ? hyperbolic_tan_derivative : sigmoid_derivative; /* calculate the output layer error (step 3 for output cell) */ for (out = 0; out < n->size.output; out++) { n->error.output[out] = (n->neuron.target[out] - n->neuron.output[out]) * (*activation_derivative)(n, n->neuron.output[out]); } /* calculate the hidden layer error (step 3 for hidden cell) */ for (hid = 0; hid < n->size.hidden; hid++) { n->error.hidden[hid] = 0.0; for (out = 0; out < n->size.output; out++) { n->error.hidden[hid] += n->error.output[out] * n->weight.hidden_to_output[hid][out]; } n->error.hidden[hid] *= (*activation_derivative)(n, n->neuron.hidden[hid]); } /* update the weights for the output layer (step 4) */ for (out = 0; out < n->size.output; out++) { for (hid = 0; hid < n->size.hidden; hid++) { n->weight.hidden_to_output[hid][out] += (n->learn_rate * n->error.output[out] * n->neuron.hidden[hid]); } /* update the bias */ n->weight.hidden_to_output[n->size.hidden][out] += (n->learn_rate * n->error.output[out]); } /* update the weights for the hidden layer (step 4) */ for (hid = 0; hid < n->size.hidden; hid++) { for (inp = 0; inp < n->size.input; inp++) { n->weight.input_to_hidden[inp][hid] += (n->learn_rate * n->error.hidden[hid] * n->neuron.input[inp]); } /* update the bias */ n->weight.input_to_hidden[n->size.input][hid] += (n->learn_rate * n->error.hidden[hid]); } } /* * Compute the Mean Square Error between the actual output and the * targeted output. */ double mean_square_error(NEURAL_NETWORK *n, double *target) { double error = 0.0; int i; for (i = 0; i < n->size.output; i++) error += sqr(target[i] - n->neuron.output[i]); return 0.5 * error; } double c_train(int handle, SV* input, SV* output) { NEURAL_NETWORK *n = c_get_network(handle); int i,length; AV *array; double *input_array = malloc(sizeof(double) * n->size.input); double *output_array = malloc(sizeof(double) * n->size.output); double error; if (! is_array_ref(input) || ! is_array_ref(output)) { croak("train() takes two arrayrefs."); } array = get_array(input); length = av_len(array)+ 1; if (length != n->size.input) { croak("Length of input array does not match network"); } for (i = 0; i < length; i++) { input_array[i] = get_float_element(array, i); } array = get_array(output); length = av_len(array) + 1; if (length != n->size.output) { croak("Length of output array does not match network"); } for (i = 0; i < length; i++) { output_array[i] = get_float_element(array, i); } c_feed(n, input_array, output_array, 1); error = mean_square_error(n, output_array); free(input_array); free(output_array); return error; } int c_new_network(int input, int hidden, int output) { NEURAL_NETWORK *n; int handle; handle = c_new_handle(); n = c_get_network(handle); n->size.input = input; n->size.hidden = hidden; n->size.output = output; if (!c_create_network(n)) return -1; /* Perl already seeded the random number generator, via a rand(1) call */ c_assign_random_weights(n); return handle; } double c_train_set(int handle, SV* set, int iterations, double mse) { NEURAL_NETWORK *n = c_get_network(handle); AV *input_array, *output_array; /* perl arrays */ double *input, *output; /* C arrays */ double max_error = 0.0; int set_length=0; int i,j; int index; set_length = av_len(get_array(set))+1; if (!set_length) croak("_train_set() array ref has no data"); if (set_length % 2) croak("_train_set array ref must have an even number of elements"); /* allocate memory for out input and output arrays */ input_array = get_array_from_aoa(set, 0); input = malloc(sizeof(double) * set_length * (av_len(input_array)+1)); output_array = get_array_from_aoa(set, 1); output = malloc(sizeof(double) * set_length * (av_len(output_array)+1)); for (i=0; i < set_length; i += 2) { input_array = get_array_from_aoa(set, i); if (av_len(input_array)+1 != n->size.input) croak("Length of input data does not match"); /* iterate over the input_array and assign the floats to input */ for (j = 0; j < n->size.input; j++) { index = (i/2*n->size.input)+j; input[index] = get_float_element(input_array, j); } output_array = get_array_from_aoa(set, i+1); if (av_len(output_array)+1 != n->size.output) croak("Length of output data does not match"); for (j = 0; j < n->size.output; j++) { index = (i/2*n->size.output)+j; output[index] = get_float_element(output_array, j); } } for (i = 0; i < iterations; i++) { max_error = 0.0; for (j = 0; j < (set_length/2); j++) { double error; c_feed(n, &input[j*n->size.input], &output[j*n->size.output], 1); if (mse >= 0.0 || i == iterations - 1) { error = mean_square_error(n, &output[j*n->size.output]); if (error > max_error) max_error = error; } } if (mse >= 0 && max_error <= mse) /* Below their target! */ break; } free(input); free(output); return max_error; } SV* c_infer(int handle, SV *array_ref) { NEURAL_NETWORK *n = c_get_network(handle); int i; AV *perl_array, *result = newAV(); /* feed the data */ perl_array = get_array(array_ref); for (i = 0; i < n->size.input; i++) n->tmp[i] = get_float_element(perl_array, i); c_feed(n, n->tmp, NULL, 0); /* read the results */ for (i = 0; i < n->size.output; i++) { av_push(result, newSVnv(n->neuron.output[i])); } return newRV_noinc((SV*) result); } void c_feed(NEURAL_NETWORK *n, double *input, double *output, int learn) { int i; for (i=0; i < n->size.input; i++) { n->neuron.input[i] = input[i]; } if (learn) for (i=0; i < n->size.output; i++) n->neuron.target[i] = output[i]; c_feed_forward(n); if (learn) c_back_propagate(n); } /* * The original author of this code is M. Tim Jones and * written for the book "AI Application Programming", by Charles River Media. * * It's been so heavily modified that it bears little resemblance to the * original, but credit should be given where credit is due. Therefore ... * * Copyright (c) 2003 Charles River Media. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is hereby granted without fee provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. 3. * Neither the name of Charles River Media nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY CHARLES RIVER MEDIA AND CONTRIBUTORS 'AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CHARLES RIVER MEDIA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ MODULE = AI::NeuralNet::Simple PACKAGE = AI::NeuralNet::Simple PROTOTYPES: DISABLE int is_array_ref (ref) SV * ref AV * get_array (aref) SV * aref float get_float_element (array, index) AV * array int index SV * get_element (array, index) AV * array int index AV * get_array_from_aoa (aref, index) SV * aref int index float c_get_learn_rate (handle) int handle void c_set_learn_rate (handle, rate) int handle float rate PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; c_set_learn_rate(handle, rate); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ double c_get_delta (handle) int handle void c_set_delta (handle, delta) int handle double delta PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; c_set_delta(handle, delta); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ int c_get_use_bipolar (handle) int handle void c_set_use_bipolar (handle, bipolar) int handle int bipolar PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; c_set_use_bipolar(handle, bipolar); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ void c_destroy_network (handle) int handle PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; c_destroy_network(handle); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ SV * build_rv (av) AV * av SV * build_axaref (arena, rows, columns) void * arena int rows int columns SV * c_export_network (handle) int handle void c_load_axa (hold, idx, arena, rows, columns) AV * hold int idx void * arena int rows int columns PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; c_load_axa(hold, idx, arena, rows, columns); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ int c_import_network (rv) SV * rv double c_train (handle, input, output) int handle SV * input SV * output int c_new_network (input, hidden, output) int input int hidden int output double c_train_set (handle, set, iterations, mse) int handle SV * set int iterations double mse SV * c_infer (handle, array_ref) int handle SV * array_ref