Writing to a C array of floats

Started by dexterlagan, August 21, 2018, 06:55:11 AM

Previous topic - Next topic

dexterlagan

Hi There!



  I'm attempting to implement the second example for the GENANN neural network library in newLISP. I made a module which works fine for the first example, however the second example requires writing random floats to an array. A pointer to the array is found in the struct sent back from GENANN's init function. Here is the module code for background:



; genann.lsp
;
(context 'genann)
(println "GENANN for newLISP by Dexter Santucci v1.0. July 2018 - Support: dexterlagan@gmail.com")

(setq is-64-bit (= 0x100 (& 0x100 (sys-info -1))))
(setq has-ffi (= 1024 (& 1024 (sys-info -1))))

(if has-ffi
    (println "FFI detected and fully supported. Initializing library...")
    (println "Warning: this library may require newLISP to be compiled with FFI."))

;;; constants

(define RAND_MAX 32767)

;;; structures

; Define a genann structure
; IMPORTANT NOTE: all pointers shall be described at 'void*'
(struct 'network "int"     ; number of inputs
                 "int"     ; number of hidden_layers
                 "int"     ; number of hidden_neurons
                 "int"     ; number of outputs
                 "void*"   ; activation_hidden_func
                 "void*"   ; activation_output_func
                 "int"     ; total_weights
                 "int"     ; total_neurons
                 "void*"  ; weight - pointer to array of weights (array size: total_weights)
                 "void*"  ; pointer to array of input/output (array size: total_neurons)
                 "void*") ; pointer to array of delta of each hidden and output neuron
                           ; (array size: total_neurons - inputs)

;;; public functions

; Creates and returns a new ann.
; genann *genann_init(int inputs, int hidden_layers, int hidden, int outputs)
(setq init (import "genann.dll" "genann_init" "void*" "int" "int" "int" "int"))

; Creates ANN from file saved with genann_write.
; genann *genann_read(FILE *in)
(setq read-ann (import "genann.dll" "genann_read" "void*" "void*"))

; Sets weights randomly. Called by init.
; void genann_randomize(genann *ann)
(setq randomize-ann (import "genann.dll" "genann_randomize" "void" "void*"))

; Returns a new copy of ann.
; genann *genann_copy(genann const *ann)
(setq copy-ann (import "genann.dll" "genann_copy" "void*" "void*"))

; Frees the memory used by an ann.
; void genann_free(genann *ann)
(setq free (import "genann.dll" "genann_free" "void" "void*"))

; Runs the feedforward algorithm to calculate the ann's output.
; double const *genann_run(genann const *ann, double const *inputs)
(setq run (import "genann.dll" "genann_run" "void*" "void*" "void*"))

; Does a single backprop update.
; void genann_train(genann const *ann, double const *inputs,
;                   double const *desired_outputs, double learning_rate)
(setq train (import "genann.dll" "genann_train" "void" "void*" "void*" "void*" "double"))

; Saves the ann.
; void genann_write(genann const *ann, FILE *out)
(setq write-ann (import "genann.dll" "genann_write" "void" "void*" "void*"))

;;; internal functions

; void genann_init_sigmoid_lookup(const genann *ann)
(setq init_sigmoid_lookup (import "genann.dll" "genann_init_sigmoid_lookup" "void" "void*"))

; double genann_act_sigmoid(const genann *ann, double a)
(setq act_sigmoid (import "genann.dll" "genann_act_sigmoid" "double" "void*" "double"))

; double genann_act_sigmoid_cached(const genann *ann, double a)
(setq act_sigmoid_cached (import "genann.dll" "genann_act_sigmoid_cached" "double" "void*" "double"))

; double genann_act_threshold(const genann *ann, double a)
(setq act_threshold (import "genann.dll" "genann_act_threshold" "double" "void*" "double"))

; double genann_act_linear(const genann *ann, double a)
(setq act_linear (import "genann.dll" "genann_act_linear" "double" "void*" "double"))

(println "Library initialized successfully.")

; EOF

Here is the original example2 C source (for background) :



#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "genann.h"

int main(int argc, char *argv[])
{
    printf("GENANN example 2.n");
    printf("Train a small ANN to the XOR function using random search.n");

    /* Input and expected out data for the XOR function. */
    const double input[4][2] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
    const double output[4] = {0, 1, 1, 0};
    int i;

    /* New network with 2 inputs,
     * 1 hidden layer of 2 neurons,
     * and 1 output. */
    genann *ann = genann_init(2, 1, 2, 1);

    double err;
    double last_err = 1000;
    int count = 0;

    do {
        ++count;
        if (count % 1000 == 0) {
            /* We're stuck, start over. */
            genann_randomize(ann);
        }

        genann *save = genann_copy(ann);

        /* Take a random guess at the ANN weights. */
        for (i = 0; i < ann->total_weights; ++i) {
            ann->weight[i] += ((double)rand())/RAND_MAX-0.5;
        }

        /* See how we did. */
        err = 0;
        err += pow(*genann_run(ann, input[0]) - output[0], 2.0);
        err += pow(*genann_run(ann, input[1]) - output[1], 2.0);
        err += pow(*genann_run(ann, input[2]) - output[2], 2.0);
        err += pow(*genann_run(ann, input[3]) - output[3], 2.0);

        /* Keep these weights if they're an improvement. */
        if (err < last_err) {
            genann_free(save);
            last_err = err;
        } else {
            genann_free(ann);
            ann = save;
        }

    } while (err > 0.01);

    printf("Finished in %d loops.n", count);

    /* Run the network and see what it predicts. */
    printf("Output for [%1.f, %1.f] is %1.f.n", input[0][0], input[0][1], *genann_run(ann, input[0]));
    printf("Output for [%1.f, %1.f] is %1.f.n", input[1][0], input[1][1], *genann_run(ann, input[1]));
    printf("Output for [%1.f, %1.f] is %1.f.n", input[2][0], input[2][1], *genann_run(ann, input[2]));
    printf("Output for [%1.f, %1.f] is %1.f.n", input[3][0], input[3][1], *genann_run(ann, input[3]));

    genann_free(ann);
    return 0;
}

Finally, here is my attempt at a rewrite of this example2 in newLISP :



; Load genann wrapper
(load "genann.lsp")

;;; main

(println "GENANN example 2.")
(println "Train a small ANN to the XOR function using random search.")

(constant 'FLOAT-LENGTH 8)

; Input and expected out data for the XOR function.
(define input  '((0 0) (0 1) (1 0) (1 1)))
(define output '(0 1 1 0))

; Initialize a new neural network with 2 inputs, 1 hidden layer of 2 neurons and 1 output.
(setq ann (genann:init 2 1 2 1))

; Unpack the ann into a struct
(setq ann-contents (unpack genann:network ann))

; Extract the ann structure
(context genann)
(setq inputs         (MAIN:ann-contents 0))
(setq hidden_layers (MAIN:ann-contents 1))
(setq hidden_neurons (MAIN:ann-contents 2))
(setq outputs           (MAIN:ann-contents 3))
(setq activation_hidden_func (MAIN:ann-contents 4))
(setq activation_output_func (MAIN:ann-contents 5))
(setq total_weights (MAIN:ann-contents 6))  
(setq total_neurons (MAIN:ann-contents 7))
(setq weight-ptr (MAIN:ann-contents 8))
(setq output-ptr (MAIN:ann-contents 9))
(setq delta-ptr (MAIN:ann-contents 10))
(context MAIN)

(setq err 0)
(setq last-err 1000)
(setq counter 0)

(do-while (< counter 3)

  (inc counter)
  (if (= (mod counter 1000) 0) ; randomize ANN every 1000 iteration.
    ; We're stuck, start over.
    (genann:randomize-ann ann))

  ; make a backup copy of the ANN for later retrieval in case our error increases.
  (setq backup-network (genann:copy-ann ann))

(println "ann addr   : " (string ann))
(println "weight addr: " (string genann:weight-ptr)) ; weight address is ALWAYS the same!
(println "bak addr   : " (string backup-network))

  ; Take a random guess at the ANN weights.
  (for (i 0 (- genann:total_weights 1)) ; step 1 by default
    (letn (src-addr     (+ genann:weight-ptr (* i FLOAT-LENGTH))
           existing   (get-float src-addr)
    rand-num     (random -0.5 1) ; (sub (div (rand genann:RAND_MAX) genann:RAND_MAX) 0.5) ;
    new-value    (add existing rand-num)
    packed-value (pack "lf" new-value))
      (cpymem packed-value src-addr FLOAT-LENGTH)
      (println "iteration #" i ", src-addr: " src-addr ", existing: " existing ", random number: " rand-num ", new: " new-value)
    ))                           ; using the 64 bits double size = 8 bytes

  ; See how we did
  (setq err 0)
  (for (input-num 0 3)
  (letn (input-pair (input input-num)
packed-input (pack "lf lf" input-pair)
          result (get-float (genann:run ann packed-input))
  delta (sub result (output input-num))
  powered (pow delta 2.0))
 (begin (setq err (add err powered))
            (println "input #" input-num ", input: " input-pair ", output: " result ", delta: " delta ", powered: " powered ", err: " err))) )
 
  (println "Error: " err ". Last Error: " last-err ". ")

  ; Keep these weights if they're an improvement.
  (if (< err last-err) ; if error is lower than previous,
      (begin (println "err < last-err. Freeing backup and saving last error.")
(genann:free backup-network) ; clear backup
             (setq last-err err)) ; save as new last error
      (begin (println "err > last-err. Freeing ann and restoring backup ann.")
(genann:free ann) ; else, clear current ann
(setq ann backup-network))) ; restore backup.             ; (set 'ann (genann:copy-ann backup-network))))

)

; Free memory
(genann:free ann)
(genann:free backup-network)

;(exit)

; EOF

  As you can see I limited the loop in order to examine results. The code works great, apart from the fact that the GENANN run function always returns the same kind of output no matter how I randomize weights. If anybody has experience with C and newLISP, I would really appreciate their insights. If you see anything obvious please let me know.



Dexter

TedWalther

#1
Dexter, have you looked at the pack and unpack functions?  I think they'll do what you want.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

dexterlagan

#2
Hi there,



  Yes I use pack / unpack in my code, and it seems to be working, but I must be missing something, as the genann procedure does not seem to notice that I'm writing to its array.



Dex


Quote from: "TedWalther"Dexter, have you looked at the pack and unpack functions?  I think they'll do what you want.