The Speed of Thought

I've recently been doing some rudimentary experimentation with neural networks using basic C code. It was something I studiously avoided over the years, mostly because of how creepy I found it to be. But seeing as though neural networks are the most likely future direction for the discipline of computer science, I have only one of two choices: either embrace it, or be left behind by it.

And so, to that end, I did a little research on neurology, and then set to work writing a simple program that could mimic it. What I quickly realized was that the fundamental characteristics of neurons had to be as basic and generalized as possible, or else they could not be scaled up very well. I also learned that there were many different options for storing data, from text files to sql databases, all of which could be represented at the lowest level of abstraction.

The resulting C code is still very much a work in progress, but having run out of time to put into this project, I've decided to share it in the hopes that someone else can pick up where I left off. So, without further ado, the code of n.c:

 

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <limits.h>

 

#if defined(_WIN32)
    #include <process.h>
    #define OS_NAME "windows"
#elif defined(_WIN64)
    #include <process.h>
    #define OS_NAME "windows"
#elif defined(__CYGWIN__) && !defined(_WIN32)
    #include <process.h>
    #define OS_NAME "windows"
#elif defined(__ANDROID__)
    #include <sys/types.h>
    #define OS_NAME "android"
#elif defined(__linux__)
    #include <sys/types.h>
    #define OS_NAME "linux"
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
    #include <sys/types.h>
    #include <sys/param.h>
    #if defined(BSD)
        #define OS_NAME "bsd"
    #endif
#elif defined(__hpux)
    #define OS_NAME "hp-ux"
#elif defined(_AIX)
    #define OS_NAME "aix"
#elif defined(__APPLE__) && defined(__MACH__)
    #include <TargetConditionals.h>
    #if TARGET_IPHONE_SIMULATOR == 1
        #define OS_NAME "ios"
    #elif TARGET_OS_IPHONE == 1
        #define OS_NAME "ios"
    #elif TARGET_OS_MAC == 1
        #define OS_NAME "osx"
    #endif
#elif defined(__sun) && defined(__SVR4)
    #include <sys/types.h>
    #define OS_NAME "solaris"
#else
    #define OS_NAME NULL
#endif

#if !defined(P_NOWAIT)
    #define P_NOWAIT NULL
#endif

#define S_INIT 1L

#define F_DELIM "\n"
#define N_DELIM "\t"
#define S_DELIM "|"
#define C_STRUCT "%s\t%s"
#define F_SYNAPSE "%s|%032Le"
#define F_NEURON "%s|%s"
#define FD_PROC "/proc/%s/%s"
#define P_FP_LOAD "<fd>%s\n"
#define F_DATA_LOAD "<%s>\t%[^\n]\n"
#define F_NEURON_LOAD "<fd>%s\n<pid>%d\n<s>%[^\n]\n<e>%032Le\n<i>%032Le\n<bp>%032Le\n<p>%032Le\n"
#define F_NEURON_SAVE "<fd>%s\n<pid>%d\n<s>%s\n<e>%032Le\n<i>%032Le\n<bp>%032Le\n<p>%032Le\n"

struct neuron {
    char* f; // filename (+directory)
    char* fd; // input pipeline file descriptor
    pid_t pid; // process id
    struct synapse {
        char* f; // destination filename
        char* fd; // output pipeline file descriptor
        bool x; // process running
        struct strength {
            long double s;
        } strength;
        struct strength s; // connection strength
    } synapse;
    struct synapse** s; // connection array
    long s_cnt; // connection count
    long s_size; // connection array size
    struct threshold {
        long double th;
    } threshold;
    struct threshold e; // excite threshold
    struct threshold i; // inhibit threshold
    struct potential {
        long double p;
    } potential;
    struct potential bp; // base potential
    struct potential p; // actual potential
    struct strength sm; // strength mondifier
    struct strength sd; // strength decay
    struct strength bs; //base strength
} neuron;

struct neuron n;
char* cmd;

char* n_eof() {
    char* eof = (char*)malloc(2 * sizeof(char)); // EOF
    sprintf(eof, "%d", EOF);
    return eof;
}

int fp_check(FILE* fp) {// file pointer
    if(fp != NULL) { return 1; }
    else return 0;
}

char* fscand(FILE* fp) { // file pointer
    int i, j;
    char c;
    char* r;
    for (i = 0, j = 1, r = (char*)malloc((j + 1) * sizeof(char)); (fscanf(fp, "%c", &c) != EOF) && (c != '\n'); sprintf(r, "%s%c", r, c))
        while (++i >= j)
            r = (char*)realloc(r, ((j = j * 2) + 1) * sizeof(char));
    return (c == EOF) ? n_eof() : r;
}

char* fscandf(FILE* fp, char* df) { // file pointer, data format
    char* d = fscand(fp);
    if (d[0] == EOF) return d;
    char* r = (char*)malloc((strlen(d) + 1) * sizeof(char));
    sprintf(r, df, d);
    return r;
}

char* sprintd(char* s1, char* s2) { // string A, string B
    char* r = (char*)malloc(((int)pow(2.0L, ceil(log((long double)(strlen(s1) + strlen(s2)))/log(2.0L))) + 1) * sizeof(char)); // return string
    sprintf(r, "%s\n%s", s1, s2);
    return r;
}

char* sprintdf(char* df, char* s1, char* s2) { // string A, string B, data format
    char* r = (char*)malloc(((int)pow(2.0L, ceil(log((long double)(strlen(s1) + strlen(s2) + strlen(df)))/log(2.0L))) + 1) * sizeof(char)); // return string
    sprintf(r, df, s1, s2);
    return r;
}

char* sscand(char* s) { // string
    int i, j;
    char c;
    char* r;
    for (i = 1, j = 1, r = (char*)malloc((j + 1) * sizeof(char)); (sscanf(s, "%c", &c)) != EOF && c != '\n'; r = sprintdf("%s%s", r, &c))
        r = (++i == j) ? (char*)realloc(r, (j = j * 2 + 1) * sizeof(char)) : r;
    return r;
}

char* sscandf(char* s, char* df) { // string, data format
    char* d = sscand(s); // interim string
    if (d[0] == EOF) return d;
    char* r = (char*)malloc((strlen(d) + 1) * sizeof(char)); // return string
    sscanf(d, df, r);
    return r;
}

char* f_conn(char* f, char* rw, char* df) { // filename, read-write format, data format
    if (strcmp(f, "") == 0)
        return NULL;
    FILE *fp; // file pointer
    char* l; // line
    char* d; // data
    char* c; // commands
    if(!fp_check(fp = fopen(f, "r"))) {
        if (strcmp(rw, "r") == 0) return "";
        else if (strcmp(rw, "w") == 0) {
            fp = fopen(f, "w");
            fprintf(fp, "<READ>\n</READ>\n<WRITE>\n");
            fclose(fp);
        }
    }
    if(!fp_check(fp = fopen(f, "r")))
        return NULL;
    do {} while((strcmp((l = fscand(fp)), n_eof()) != 0) && (strcmp(l, "<READ>") != 0));
    if (strcmp(rw, "r") == 0) {
        while((strcmp((l = fscand(fp)), n_eof()) != 0) && (strcmp(l, "</READ>") != 0))
            d = sprintd(d, l);
        fclose(fp);
        return d;
    }
    else if (strcmp(rw, "w")) {
        while((strcmp((l = fscand(fp)), n_eof()) != 0) && (strcmp(l, "</READ>") != 0))
            d = sprintd(d, l);
        do {} while((l = fscand(fp)) != n_eof() && (strcmp(l, "<WRITE>") != 0));
        while((strcmp((l = fscand(fp)), n_eof()) != 0))
            c = sprintd(c, l);
        fclose(fp);
        if(!fp_check(fp = fopen(f, "w"))) return "";
        fprintf(fp, "<READ>\n%s</READ>\n<WRITE>\n%s", df, c);
        fclose(fp);
    }
    else {
        fclose(fp);
        return NULL;
    }
}

char* p_conn(char* fd, char* rw, char* iof) { // pipeline file descriptor, read/write format, pipeline input/output format
    FILE* fp; // pipeline file pointer
    char* l; // line
    char* c; // commands
    if (strcmp(rw, "r") == 0) {
        fp = fopen(fd, "r+");
        while(strcmp(l = fscandf(fp, iof), n_eof()) != 0)
            c = sprintd(c, l);
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%d", EOF);
        fclose(fp);
        return c;
    }
    else if (strcmp(rw, "w") == 0) {
        fp = fopen(fd, "a");
        fprintf(fp, "%s", iof);
        fclose(fp);
        return "";
    }
    return n_eof();
}

char* p_read() {
    return p_conn(n.fd, "r", "%[^/n]/n");
}

int p_write(struct synapse* s, char* of) { // synapse, pipeline output format
    return (p_conn(s->fd, "w", of) == NULL) ? 1 : -1;
}

int c_send(struct synapse* s, char* c, char* d) { // synapse, command, data
    char* fc = sprintdf(C_STRUCT, c, d); // full command (+data)
    return p_write(s, fc);
}

int s_ping(struct synapse* s) { // synapse
    return c_send(s, "request", n.fd);
}

int p_init(struct synapse* s, char* fd) { // synapse
    if (s == NULL) return -1;
    if (fd == NULL) {
        char* d = f_conn(s->f, "r", "%s\t%[^\n]\n"); // data
        s->fd = sscandf(d, P_FP_LOAD);
        s_ping(s);
        return 0;
    }
    s->fd = fd;
    s->x = 1;
    return 1;
}

struct synapse** s_init() {
    return (struct synapse**)malloc((int)S_INIT * sizeof(struct synapse));
}

int s_load(struct synapse** s, long s_cnt) { // synapse list, synapse count
    long i;
    for (i = 0L; i < s_cnt; i++)
        s[i]->x = p_init(s[i], NULL);
    return 1;
}

int s_create(char* f, long double s) { // destination filename, connection strength
    while (n.s_cnt >= n.s_size)
        n.s = (struct synapse**)realloc(n.s, (n.s_size = n.s_size * 2L) * sizeof(struct synapse));
    struct synapse ss = (struct synapse){.f = f, .s = {.s = s}};
    n.s[n.s_cnt++] = &ss;
    return 1;
}

bool s_spawn(struct synapse* s) {
    return (strcmp(OS_NAME, "android") == 0 || strcmp(OS_NAME, "linux") == 0 || strcmp(OS_NAME, "bsd") == 0 || strcmp(OS_NAME, "solaris") == 0) ? (fork() == 0) : _spawnl(P_NOWAIT, cmd, "-f", s->f, "two", NULL);
}

int s_read(char* sa) { // synapse array
    char* f = (char*)malloc((PATH_MAX + 2 + 1) * sizeof(char)); // filename
    long double s; // connection strength
    char* l = strtok(sa, N_DELIM);
    int i = 0;
    while (l != NULL) {
        i++;
        int j = sscanf(l, F_SYNAPSE, f, &s);
        if (j >= 2 && strcmp(f, n_eof()) != 0)
            s_create(f, s);
        l = strtok(NULL, F_DELIM);
    }
    return i;
}

char* s_print() {
    char* r = ""; //return string
    char* ss = (char*)malloc((PATH_MAX + 32 + 2 + 1 + 1) * sizeof(char)); // synapse string
    char* sf; // string format
    sf = sprintdf("%%s%s%%s%s", S_DELIM, "");
    long i;
    for (i = 0L; i < n.s_cnt; i++) {
        sprintf(ss, F_SYNAPSE, n.s[i]->f, n.s[i]->s.s);
        r = sprintdf(sf, r, ss);
    }
    r = sprintdf("%s%s", r, S_DELIM);
    return (r + 1);
}

int s_sort() {
    if (n.s_cnt == 0)
        return 0;
    bool swp; // swap tracker
    long i, j;
    for (i = 0L; i < n.s_cnt - 1L; i++) {
        swp = 0;
        for (j = 0L; j < n.s_cnt - i - 1L; j++)  
            if (n.s[j]->f > n.s[j+1]->f) {
                swp = 1;
                struct synapse* ts = (struct synapse*)malloc(sizeof(struct synapse*)); // temporary swap synapse
                ts = n.s[j];
                n.s[j] = n.s[j+1];
                n.s[j+1] = ts;
            }
        if (swp == 0) return 1;
    }
    return 1;
}

long s_index(char* f) { // filename
    long i, j, k;
    for (i = 0L, j = n.s_cnt - 1L, k = (i + j) / 2L; i <= j; k = (i + j) / 2L) {
        if (strcmp(n.s[k]->f, f) < 0)
            i = k + 1L;
        else if (strcmp(n.s[k]->f, f) > 0)
            j = k - 1L;
        else if (strcmp(n.s[k]->f, f) == 0)
            return k;
    }
    return -1L;
}

struct synapse* s_select(char* f) { // filename
    long i;
    if ((i = s_index(f)) == -1L) return NULL;
    return n.s[i];
}

int s_fire(struct synapse* s, bool ei) { // synapse, excitory/inhibitory qualifier
    char* ss = (char*)malloc((PATH_MAX + 32 + 2 + 1 + 1) * sizeof(char));
    sprintf(ss, F_SYNAPSE, n.f, s->s.s);
    return c_send(s, (ei) ? "excite" : "inhibit", ss);
}

int s_kill(char* f) { // filename
    long i;
    if ((i = s_index(f)) == -1L) return 0;
    for (i = i + 1L; i < n.s_cnt; i++)
        n.s[i-1L] = n.s[i];
    n.s_cnt--;
    if (n.s_cnt < n.s_size / 3L) n.s = (struct synapse**)realloc(n.s, (n.s_size = n.s_size * 2L) * sizeof(struct synapse));
    return 1;
}

int n_fire(bool ei) { // excitory/inhibitory qualifier
    long i;
    for (i = 0L; i < n.s_cnt; i++)
        s_fire(n.s[i], ei);
    n.p.p = n.bp.p;
    return 1;
}

int n_check() {
    if (n.p.p >= n.e.th) return n_fire(1);
    else if (n.p.p <= n.i.th) return n_fire(0);
    return 1;
}

char* n_read() {
    char* c = ""; // list of commands
    long i;
    for (i = 0L; i < n.s_cnt; i++)
        c = sprintdf("%s\n%s", c, p_read(n.s[i]->fd));
    return c;
}

int n_reply(char* fd) { // source file descriptor
    char* d; // data
    d = sprintdf(F_NEURON, n.f, n.fd);
    struct synapse s = (struct synapse){.f = NULL, .fd = fd, .x = 1, .s = {.s = 0.0L}};
    return c_send(&s, "reply", d);
}

int n_excite(char* f, long double e) { // source filename, excitory signal
    if (e < 0.0L) return 0;
    n.p.p += e;
    struct synapse* s = (struct synapse*)malloc(sizeof(struct synapse*));
    if ((s = s_select(f)) != NULL)
        s->s.s *= n.sm.s;
    return 1;
}

int n_inhibit(char* f, long double i) { // source filename, inhibitory signal
    if (i < 0.0L) return 0;
    n.p.p -= i;
    struct synapse* s = (struct synapse*)malloc(sizeof(struct synapse*));
    if ((s = s_select(f)) != NULL)
        s->s.s *= n.sm.s;
    return 1;
}

char* fd_fetch(pid_t pid) { // process id
    char* r; // return string
    char* p = (char*)malloc(((int)log10(pow(2.0L, ((double)(sizeof(pid_t) * 8L)))) + 1) * sizeof(char));
    char* fd = (char*)malloc(((PATH_MAX + 2 + 1) * sizeof(char)));
    sprintf(p, "%d", pid);
    sprintf(fd, "%d", STDIN_FILENO);
    r = sprintdf(FD_PROC, p, fd);
    return r;
}

int n_load(char* f) {// filename
    char* d = f_conn(f, "r", F_DATA_LOAD); // data
    char* s = (char*)malloc((strlen(d) + 1) * sizeof(char)); // connections
    pid_t pid; // process id (saved)
    if (strcmp(d, "") != 0) {
        int i = sscanf(d, F_NEURON_LOAD, n.fd, &pid, s, &n.e.th, &n.i.th, &n.bp.p, &n.p.p);
        return (i >= 3) ? s_read(s) : 0;
    }
    else
        return 0;
}

int n_save(char* f) {// filename
    char* of; // ouput format
    char* s = s_print();
    of = (char*)malloc((strlen(F_NEURON_SAVE) + strlen(n.fd) + strlen(s) + 32 * 4 + (int)log10(pow(2.0L, ((double)(sizeof(pid_t) * 8L)))) + 1) * sizeof(char));
    sprintf(of, F_NEURON_SAVE, n.fd, n.pid, s, n.e.th, n.i.th, n.bp.p, n.p.p);
    return (f_conn(f, "w", of) != NULL);
}

int n_close(bool sv) { // save
    if (sv) n_save(n.f);
    return 1;
}

int n_die() {
    remove(n.f);
    n_close(0);
    return 1;
}

int n_exec(char* c) { // command
    char* x = (char*)malloc((strlen(c) + 1) * sizeof(char)); // function call
    char* t = (char*)malloc((strlen(c) + 1) * sizeof(char)); // target synapse
    int i = sscanf(c, C_STRUCT, x, t);
    if (i >= 2) {
        if (strcmp(x, "request") == 0) {
            char* tfd = malloc((strlen(t) + 1) * sizeof(char));
            int j = sscanf(t, "%s", tfd);
            if (j >= 1)
                n_reply(tfd);
        }
        else if (strcmp(x, "reply") == 0) {
            char* tf = (char*)malloc((strlen(t) + 1) * sizeof(char));
            char* tfd = (char*)malloc((strlen(t) + 1) * sizeof(char));
            int j = sscanf(t, "%s|%s", tf, tfd);
            if (j >= 1)
                p_init(s_select(tf), tfd);
        }
        else if (strcmp(x, "create") == 0) {
            char* tf = (char*)malloc((strlen(t)  + 1) * sizeof(char));
            long double ts;
            int j = sscanf(t, "%s|%032Le", tf, &ts);
            if (j >= 2) {
                s_create(tf, ts);
                s_sort();
            }
        }
        else if (strcmp(x, "excite") == 0) {
            char* ts = (char*)malloc((strlen(t)  + 1) * sizeof(char));
            long double tp;
            int j = sscanf(t, "%s|%032Le", ts, &tp);
            if (j >= 2)
                n_excite(ts, tp);
        }
        else if (strcmp(x, "inhibit") == 0) {
            char* ts = (char*)malloc((strlen(t)  + 1) * sizeof(char));
            long double tld;
            int j = sscanf(t, "%s|%032Le", ts, &tld);
            if (j >= 2)
                n_inhibit(ts, tld);
        }
        else if (strcmp(x, "kill") == 0)
            s_kill(t);
        else if (strcmp(x, "die") == 0)
            n_die();
        else if (strcmp(x, "close") == 0) {
            int tl;
            int j = sscanf(t, "%d", &tl);
            if (j >= 1) {
                n_close(1);
                return (tl == 0L) ? 0 : 1;
            }
        }
    }
    return 1;
}

int n_init(char* f, long double e, long double i, long double bp, long double p) { // file name, excite threshold, inhibit threshold, base potential, potential
    n = (struct neuron){.f = f, .fd = fd_fetch(getpid()), .pid = getpid(), .s = s_init(), .s_cnt = 0L, .s_size = S_INIT, .e = {.th = e}, .i = {.th = i}, .bp = {.p = bp}, .p = {.p = p}};
    n_load(n.f);
    s_load(n.s, n.s_cnt);
    s_sort();
    return 1;
}

int n_iterate(bool sv, bool sp) { // save flag, spawn flag
    char* l = n_read(); // list of commands
    char* c = (char*)malloc((PATH_MAX * 2 + 1) * sizeof(char)); // command
    bool x = 0; // exit command
    bool ch = 0; // child process
    while ((sscanf(l, "%[^\n]\n", c)) != EOF)
        x |= !n_exec(c);
    n_check();
    long i;
    if (sp)
        for (i = 0; i < n.s_cnt; i++)
            if (!n.s[i]->x && (ch = s_spawn(n.s[i])) == 0)
                n_init(n.s[i]->f, n.e.th, n.i.th, n.bp.p, n.p.p);
    if (sv) n_save(n.f);
    return (ch) ? 0 : ((!x) ? 1 : -1);
}

int n_run(long double uf, long double sf, long w) { // update frequency, save frequency, spawn wait
    int r = 0; // return state (1=continue, 0=restart, -1=exit)
    do {
        long j = 0L, k = 0L;
        bool sv = 0; // save qualifier
        bool sp = 0; // spawn qualifier
        long to = (long)((1.0L / uf) * 1000000.0L); // timeout
        long sd = (long)((1.0L / sf) * 1000000.0L); // save timeout
        long st = w * 1000L; // spawn timer
        do {
            usleep(to);
            sv = (to * ++j >= sd);
            sp = (to * ++k >= st);
            j = (sv) ? 0 : j;
        } while ((r = (n_iterate(sv, sp))) != -1 && r != 0);
    } while (r != -1);
    return 1;
}

int main(int argc, char* argv[]) {
    char* f = (char*)malloc((PATH_MAX + 2 + 1) * sizeof(char));
    char* f_in = (char*)malloc((PATH_MAX + 2 + 1) * sizeof(char));
    sprintf(f, "./n");
    long double e = 1.0L;
    long double e_in = 1.0L;
    long double i = -1.0L;
    long double i_in = -1.0L;
    long double bp = 0.0L;
    long double bp_in = 0.0L;
    long double p = 0.0L;
    long double p_in = 0.0L;
    long double uf = 1000.0; // update frequency
    long double uf_in = 1000.0;
    long double sf = 10.0L; // save frequency
    long double sf_in = 10.0L;
    long w = 1000L; // ping wait (in milliseconds)
    long w_in = 1000L;
    int k, l;
    cmd = argv[0];
    for (k = 1; k < argc; k++) {
        if (strcmp(argv[k], "-f") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%s", f_in);
            f = (l >= 1) ? f_in : f;
        }
        else if (strcmp(argv[k], "-e") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &e_in);
            e = (l >= 1) ? e_in : e;
        }
        else if (strcmp(argv[k], "-i") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &i_in);
            i = (l >= 1) ? i_in : i;
        }
        else if (strcmp(argv[k], "-bp") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &bp_in);
            bp = (l >= 1) ? bp_in : bp;
        }
        else if (strcmp(argv[k], "-p") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &p_in);
            p = (l >= 1) ? p_in : p;
        }
        else if (strcmp(argv[k], "-uf") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &uf_in);
            uf = (l >= 1) ? uf_in : uf;
        }
        else if (strcmp(argv[k], "-sf") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%032Le", &sf_in);
            sf = (l >= 1) ? sf_in : sf;
        }
        else if (strcmp(argv[k], "-w") == 0 && k+1 <= argc) {
            l = sscanf(argv[k+1], "%ld", &w_in);
            w = (l >= 1) ? w_in : w;
        }
    }
    sprintf(f, "%s%s", f, ".n");
    n_init(f, i, e, bp, p);
    n_run(uf, sf, w);
    return 0;
}

 

 

... and the build code:

gcc -O3 -c -o .\src\o\n.o .\src\n.c
gcc -o n.exe .\src\o\n.o

 

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.