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