#ifndef NNLAYER_INC_CPP
#define NNLAYER_INC_CPP
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
class Layer {
public:
Layer *prev, *next;
size_t memsize;
int size, wsize, links;
double *a, *da, *w;
Layer(Layer *prev, int size):
prev(), next(), memsize(), size(size), wsize(), links(), w()
{
assert(size > 0);
a = new double[size*2];
da = a + size;
memset(a, 0, sizeof(*a)*size*2);
if (prev) (this->prev = &prev->back())->next = this;
memsize += size*2*sizeof(double);
}
virtual ~Layer() {
if (next) delete next;
if (prev) prev->next = nullptr;
delete[] a;
if (w) delete[] w;
}
virtual Layer& pass()
{ return next ? next->pass() : *this; }
virtual Layer& backpass(double trainRatio)
{ return prev ? prev->backpass(trainRatio) : *this; }
inline Layer& front()
{ Layer *l = this; while(l->prev) l = l->prev; return *l; }
inline Layer& back()
{ Layer *l = this; while(l->next) l = l->next; return *l; }
inline size_t totalMemSize() const
{ size_t s = 0; for(const Layer *l = this; l; l = l->next) s += l->memsize; return s; }
inline int totalSize() const
{ int c = 0; for(const Layer *l = this; l; l = l->next) c += l->size; return c; }
inline int totalLinks() const
{ int c = 0; for(const Layer *l = this; l; l = l->next) c += l->links; return c; }
bool toStream(FILE *f)
{ return (!w || fwrite(w, sizeof(double)*wsize, 1, f)) && (!next || next->toStream(f)); }
bool fromStream(FILE *f)
{ return (!w || fread (w, sizeof(double)*wsize, 1, f)) && (!next || next->fromStream(f)); }
bool save(const char *filename) {
assert(!prev);
FILE *f = fopen(filename, "wb");
if (!f) return printf("cannot open file '%s' for write\n", filename), false;
if (!toStream(f)) return printf("cannot write to file '%s'\n", filename), fclose(f), false;
fclose(f);
return 1;
}
bool load(const char *filename) {
assert(!prev);
FILE *f = fopen(filename, "rb");
if (!f) return printf("cannot open file '%s' for read\n", filename), false;
if (!fromStream(f)) return printf("cannot read from file '%s'\n", filename), fclose(f), false;
fclose(f);
return 1;
}
double trainPass(double ratio, double *x, double *y, double qmin) {
assert(!prev);
memcpy(a, x, sizeof(*a)*size);
Layer &b = pass();
double qmax = 0;
for(double *pa = b.a, *pda = b.da, *e = pa + b.size; pa < e; ++pa, ++pda, ++y) {
assert(*pa == *pa);
double d = *y - *pa;
*pda = d;
//qmax += d*d;
double q = fabs(d);
if (qmax < q) qmax = q;
}
//qmax = sqrt(qmax/b.size);
if (qmax > qmin)
b.backpass(ratio);
//if (qmax < 1e-6) {
// printf("strange:\n");
// y -= b.size;
// for(double *pa = b.a, *e = pa + b.size; pa < e; ++pa, ++y)
// printf("%f - %f = %f\n", *y, *pa, *y - *pa);
//}
return qmax;
}
};
class LayerSimple: public Layer {
public:
LayerSimple(Layer &prev, int size):
Layer(&prev, size)
{
links = wsize = size * this->prev->size;
w = new double[wsize];
double k = 1.0/this->prev->size;
for(double *iw = w, *e = iw + wsize; iw < e; ++iw)
*iw = (rand()/(double)RAND_MAX*2 - 1)*k;
memsize += wsize*sizeof(double);
}
Layer& pass() override {
double *pa = prev->a, *ee = pa + prev->size;
double *iw = w;
for(double *ia = a, *e = ia + size; ia < e; ++ia) {
double s = 0;
for(double *ipa = pa; ipa < ee; ++ipa, ++iw)
s += *ipa * *iw;
*ia = 1/(1 + exp(-s)); // sigmoid
//*ia = s > 0 ? s : 0; // RaLU
}
return next ? next->pass() : *this;
}
Layer& backpass(double trainRatio) override {
double *pa = prev->a, *pda = prev->da, *ee = pa + prev->size;
double *iw = w;
if (prev->prev) {
memset(pda, 0, sizeof(*prev->da) * prev->size);
for(double *ia = a, *ida = da, *e = ia + size; ia < e; ++ia, ++ida) {
double a = *ia;
double ds = a * (1-a) * *ida; // sigmoid derivation * ida
//if (!*ia) continue; // RaLU derivation is zero
//double ds = *ida;
double dst = ds*trainRatio;
for(double *ipa = pa, *ipda = pda; ipa < ee; ++ipa, ++ipda, ++iw) {
*ipda += ds * *iw;
*iw += dst * *ipa;
}
}
} else {
for(double *ia = a, *ida = da, *e = ia + size; ia < e; ++ia, ++ida) {
double a = *ia;
double dst = a * (1-a) * *ida * trainRatio; // sigmoid derivation * ida * trainRatio
//if (!*ia) continue; // RaLU derivation is zero
//double dst = *ida * trainRatio;
for(double *ipa = pa; ipa < ee; ++ipa, ++iw)
*iw += dst * *ipa;
}
}
return prev->backpass(trainRatio);
}
};
#endif