#ifndef NNLAYER_INC_CPP
#define NNLAYER_INC_CPP
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
class Layer {
public:
Layer *prev, *next;
int size;
double trainRatio;
double *a, *d, *da;
Layer(Layer *prev, int size, double trainRatio = 0):
prev(), next(), size(size), trainRatio(trainRatio)
{
assert(size > 0);
a = new double[size*3];
d = a + size;
da = d + size;
memset(a, 0, sizeof(*a)*size*3);
if (prev) (this->prev = &prev->back())->next = 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; }
virtual ~Layer() {
if (next) delete next;
if (prev) prev->next = nullptr;
delete[] a;
}
virtual bool toStream(FILE *f)
{ return next ? next->toStream(f) : true; }
virtual bool fromStream(FILE *f)
{ return next ? next->fromStream(f) : true; }
virtual Layer& pass()
{ return next ? next->pass() : *this; }
virtual Layer& backpass()
{ return prev ? prev->backpass() : *this; }
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 *x, double *y, double qmin) {
assert(!prev);
double *fa = a;
a = x;
Layer &b = pass();
double qmax = 0;
for(double *pa = b.a, *pda = b.da, *e = pa + b.size; pa < e; ++pa, ++pda, ++y) {
double d = *y - *pa;
*pda = d;
double q = fabs(d);
if (qmax < q) qmax = q;
}
if (qmax > qmin) b.backpass();
a = fa;
return qmax;
}
};
class LayerPlain: public Layer {
public:
double *b, *w;
LayerPlain(Layer &prev, int size, double trainRatio = 0):
Layer(&prev, size, trainRatio)
{
b = new double[size*(1 + prev.size)];
w = b + size;
for(double *p = b, *e = p + size*(1 + prev.size); p < e; ++p)
*p = rand()/(double)RAND_MAX*2 - 1;
}
~LayerPlain()
{ delete[] b; }
bool toStream(FILE *f) override
{ return fwrite(b, sizeof(double)*size*(1 + prev->size), 1, f) && (!next || next->toStream(f)); }
bool fromStream(FILE *f)
{ return fread (b, sizeof(double)*size*(1 + prev->size), 1, f) && (!next || next->toStream(f)); }
Layer& pass() override {
double *prevA = prev->a, *ee = prevA + prev->size;
double *pw = w;
for(double *pa = a, *pb = b, *pd = d, *e = pa + size; pa < e; ++pa, ++pb, ++pd) {
double s = *pb;
for(double *ppa = prevA; ppa < ee; ++ppa, ++pw)
s += *ppa * *pw;
double ex = exp(-s);
double ex1 = ex + 1;
double ex2 = 1/ex1;
*pa = ex2; // sigmoid
*pd = ex*ex2*ex2; // sigmoid derivation
}
return next ? next->pass() : *this;
}
Layer& backpass() override {
double tr = trainRatio;
double *prevA = prev->a, *prevDa = prev->da, *ee = prevA + prev->size;
double *pw = w;
if (prev->prev) {
memset(prevDa, 0, sizeof(*prev->da) * prev->size);
for(double *pb = b, *pd = d, *pda = da, *e = pb + size; pb < e; ++pb, ++pd, ++pda) {
double ds = *pd * *pda;
double dst = ds*tr;
*pb += dst;
for(double *ppa = prevA, *ppda = prevDa; ppa < ee; ++ppa, ++ppda, ++pw) {
*ppda += ds * *pw;
*pw += dst * *ppa;
}
}
} else {
for(double *pb = b, *pd = d, *pda = da, *e = pb + size; pb < e; ++pb, ++pd, ++pda) {
double dst = *pd * *pda * tr;
*pb += dst;
for(double *ppa = prevA; ppa < ee; ++ppa, ++pw)
*pw += dst * *ppa;
}
}
return prev->backpass();
}
};
class LayerConvolution: public Layer {
public:
enum { W = 4, D = W-2, WW = W*W };
double *b, *w, *dw;
int sx, sy, sz;
int psx, psy, psz;
LayerConvolution(Layer &prev, int sx, int sy, int sz, double trainRatio = 0):
Layer(&prev, sx*sy*sz, trainRatio),
sx(sx), sy(sy), sz(sz),
psx(sx*2 + D), psy(sy*2 + D), psz(prev.size/(psx*psy))
{
assert(sx > 0 && sy > 0 && sz > 0);
assert(psx > 0 && psy > 0 && psz > 0);
assert(psx*psy*psz == prev.size);
b = new double[size + WW*psz*(sz+1)];
w = b + size;
dw = w + WW*psz*sz;
for(double *p = b, *e = p + (size + WW*psz*sz); p < e; ++p)
*p = rand()/(double)RAND_MAX*2 - 1;
}
~LayerConvolution()
{ delete[] b; }
bool toStream(FILE *f) override
{ return fwrite(b, sizeof(double)*(size + WW*psz*sz), 1, f) && (!next || next->toStream(f)); }
bool fromStream(FILE *f)
{ return fread (b, sizeof(double)*(size + WW*psz*sz), 1, f) && (!next || next->toStream(f)); }
Layer& pass() override {
double *pa = prev->a;
int sxy = sx*sy, stepW = psz*WW, stepPA = psx*2, stepIPA = psx - W;
for(double *ia = a, *ib = b, *id = d, *iw = w, *ea = ia + size; ia < ea; iw += stepW) {
double *eew = iw + stepW;
for(double *ipa = pa, *ea = ia + sxy; ia < ea; ipa += stepPA) {
for(double *ea = ia + sx; ia < ea; ++ia, ++ib, ++id, ipa += 2) {
double s = *ib;
for(double *iiw = iw, *iipa = ipa; iiw < eew; iipa = ipa)
for(int yy = 0; yy < W; ++yy, iipa += stepIPA)
for(int xx = 0; xx < W; ++xx, ++iipa, ++iiw)
s += *iipa * *iiw;
double ex = exp(-s);
double ex1 = ex + 1;
double ex2 = 1/ex1;
*ia = ex2; // sigmoid
*id = ex*ex2*ex2; // sigmoid derivation
}
}
}
return next ? next->pass() : *this;
}
Layer& backpass() override {
double tr = trainRatio;
int sxy = sx*sy, stepW = psz*WW, stepPA = psx*2, stepIPA = psx - W;
double *dw = this->dw, *edw = dw + stepW, *pa = prev->a, *pda = prev->da;
if (prev->prev) {
memset(pda, 0, sizeof(*pda) * prev->size);
for(double *ib = b, *id = d, *ida = da, *iw = w, *eda = ida + size; ida < eda; iw += stepW) {
double *eew = iw + stepW;
memset(dw, 0, sizeof(*dw) * stepW);
for(double *ipa = pa, *ipda = pda, *eda = ida + sxy; ida < eda; ipa += stepPA, ipda += stepPA) {
for(double *eda = ida + sx; ida < eda; ++ib, ++id, ++ida, ipa += 2) {
double ds = *id * *ida;
double dst = ds*tr;
*ib += dst;
for(double *iiw = iw, *idw = dw, *iipa = ipa, *iipda = ipda; idw < edw; iipa = ipa, iipda = ipda)
for(int yy = 0; yy < W; ++yy, iipa += stepIPA, iipda += stepIPA)
for(int xx = 0; xx < W; ++xx, ++iipa, ++iipda, ++iiw, ++idw)
{ *iipda += ds * *iiw; *idw += dst * *iipa; }
}
}
for(double *idw = dw; iw < eew; ++iw, ++idw)
*iw += *idw;
}
} else {
for(double *ib = b, *id = d, *ida = da, *iw = w, *eda = ida + size; ida < eda; iw += stepW) {
memset(dw, 0, sizeof(*dw) * stepW);
for(double *ipa = pa, *eda = ida + sxy; ida < eda; ipa += stepPA) {
for(double *eda = ida + sx; ida < eda; ++ib, ++id, ++ida, ipa += 2) {
double dst = *id * *ida * tr;
*ib += dst;
for(double *idw = dw, *iipa = ipa; idw < edw; iipa = ipa)
for(int yy = 0; yy < W; ++yy, iipa += stepIPA)
for(int xx = 0; xx < W; ++xx, ++iipa, ++idw)
*idw += dst * *iipa;
}
}
for(double *idw = dw; idw < edw; ++iw, ++idw)
*iw += *idw;
}
}
return prev->backpass();
}
};
#endif