Blob Blame Raw
#ifndef NNLAYER3_INC_CPP
#define NNLAYER3_INC_CPP


#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>

#include <vector>
#include <algorithm>


typedef float Real;


template<typename T>
bool twrite(const T &x, FILE *f)
  { return fwrite(&x, sizeof(T), 1, f); }
template<typename T>
bool tread(T &x, FILE *f)
  { return fread(&x, sizeof(T), 1, f); }


struct Neuron {
  Real v, d;
};


class Layer {
public:
  Layer *prev, *next;

  int sx, sy, sz, sl;
  Neuron *neurons;
  Real *weights;
  int *invWeights;
  int *invNeurons;


  explicit Layer(Layer *prev = nullptr, int sx = 0, int sy = 0, int sz = 0, int sl = 0):
    prev(), next(), sx(), sy(), sz(), sl(), neurons(), weights(), invWeights(), invNeurons()
  {
    while(prev && prev->next) prev = prev->next;
    if (prev) prev->next = this;
    this->prev = prev;
    init(sx, sy, sz, sl);
  }


  virtual ~Layer() {
    clear();
    if (next) delete next;
    if (prev) prev->next = nullptr;
  }


  void clear() {
    if (next) next->clear();
    if (neurons) delete[] neurons;
    if (weights) delete[] weights;
    if (invWeights) delete[] invWeights;
    sx = sy = sz = sl = 0;
    neurons = nullptr;
    weights = nullptr;
    invWeights = nullptr;
    invNeurons = nullptr;
  }


  bool init(int sx, int sy, int sz, int sl = 0) {
    clear();
    if (prev && !prev->countNeurons()) return false;
    if (sx <= 0 || sy <= 0 || sz <= 0) return false;
    if (prev ? (sl <= 0 || sl > prev->sx || sl > prev->sy) : sl != 0) return false;
    printf("init %d %d %d %d\n", sx, sy, sz, sl);

    this->sx = sx;
    this->sy = sy;
    this->sz = sz;

    int size = sx*sy*sz;
    neurons = new Neuron[size];
    memset(neurons, 0, sizeof(*neurons)*size);

    if (prev) {
      int psx = prev->sx;
      int psy = prev->sy;
      int psz = prev->sz;
      int psize = psx*psy*psz;

      this->sl = sl;
      int wsize = size*sl*sl*psz;
      weights = new Real[wsize];
      double k = 1.0/(sl*sl*psz);
      for(int i = 0; i < wsize; ++i)
        weights[i] = Real( (rand()/(double)RAND_MAX*2 - 1)*k );

      struct Link {
        int n, w;
        inline bool operator< (const Link &b) const
          { return n < b.n ? true : (b.n < n ? false : w < b.w); }
      } *links = new Link[wsize], *il = links;

      int dx = sx > 1 ? sx - 1 : 1;
      int dy = sy > 1 ? sy - 1 : 1;

      for(int y = 0; y < sy; ++y)
      for(int x = 0; x < sx; ++x) {
        int py = y*(psy-sl)/dy;
        int px = x*(psx-sl)/dx;
        assert(py >= 0 && py <= psy-sl);
        assert(px >= 0 && px <= psx-sl);

        for(int z = 0; z < sz; ++z)
        for(int ly = 0; ly < sl; ++ly)
        for(int lx = 0; lx < sl; ++lx)
        for(int pz = 0; pz < psz; ++pz, ++il) {
          assert(il < links + wsize);
          il->n = ((py+ly)*psx + px+lx)*psz + pz;
          il->w = il - links;
          assert(il->n >= 0 && il->n < psize);
        }
      }
      assert(il == links + wsize);
      std::sort(links, il);

      invWeights = new int[wsize + psize + 1];
      invNeurons = invWeights + wsize;
      invNeurons[0] = 0;
      int pni = 0;
      for(int i = 0; i < wsize; ++i) {
        assert(pni == links[i].n || pni == links[i].n + 1);
        invWeights[i] = links[i].w;
        if (pni == links[i].n)
          invNeurons[pni++] = i;
      }
      assert(pni == psize);
      invNeurons[psize] = wsize;

      delete[] links;
    }
    return true;
  }

  bool save(FILE *f) const {
    return twrite(sx, f) && twrite(sy, f) && twrite(sz, f) && twrite(sl, f)
        && (!weights || fwrite(weights, sizeof(*weights)*sx*sy*sz*sl*sl*prev->sz, 1, f));
  }

  bool load(FILE *f) {
    clear();
    int sx = 0, sy = 0, sz = 0, sl = 0;
    return tread(sx, f) && tread(sy, f) && tread(sz, f) && tread(sl, f) && init(sx, sy, sz, sl)
        && (!weights || fread(weights, sizeof(*weights)*sx*sy*sz*sl*sl*prev->sz, 1, f));
  }

  bool saveAll(FILE *f) const
    { return save(f) && (!next || next->saveAll(f)); }
  bool loadAll(FILE *f)
    { return load(f) && (!next || next->loadAll(f)); }

  bool saveAll(const char *filename) const {
    assert(!prev);
    FILE *f = fopen(filename, "wb");
    if (!f)
      { printf("cannot open file for write: %s\n", filename); return false; }
    int count = totalLayers();
    if (!twrite(count, f) || !saveAll(f))
      { printf("cannot save to file: %s\n", filename); fclose(f); return false; }
    fclose(f);
    return true;
  }

  bool loadAll(const char *filename) {
    assert(!prev);
    FILE *f = fopen(filename, "rb");
    if (!f)
      { printf("cannot open file for read: %s\n", filename); return false; }
    int count = 0;
    if (!tread(count, f) || count <= 0)
      { printf("cannot load from file: %s\n", filename); fclose(f); return false; }
    if (next)
      delete next;
    while(--count)
      new Layer(this);
    if (!loadAll(f))
      { printf("cannot load from file: %s\n", filename); fclose(f); return false; }
    fclose(f);
    return true;
  }

  inline int countNeurons() const
    { return sx*sy*sz; }
  inline int linksPerNeuron() const
    { return prev ? sl*sl*prev->sz : 0; }
  inline int countLinks() const
    { return countNeurons() * linksPerNeuron(); }
  inline size_t memSize() const {
    return sizeof(*neurons)*countNeurons()
         + (sizeof(*weights) + sizeof(*invWeights))*countLinks()
         + (prev ? sizeof(*invNeurons)*(prev->countNeurons() + 1) : 0);
  }

  int totalLayers() const
    { int t = 0; for(const Layer *l = this; l; l = l->next) ++t; return t; }
  int totalNeurons() const
    { int t = 0; for(const Layer *l = this; l; l = l->next) t += l->countNeurons(); return t; }
  int totalLinks() const
    { int t = 0; for(const Layer *l = this; l; l = l->next) t += l->countLinks(); return t; }
  size_t totalMemSize() const
    { size_t t = 0; for(const Layer *l = this; l; l = l->next) t += l->memSize(); return t; }

  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; }


  void pass(int y0, int y1) {
    if (!prev) return;

    int sx = this->sx;
    int sy = this->sy;
    int sz = this->sz;
    int sxz = sx*sz;

    int psx = prev->sx;
    int psy = prev->sy;
    int psz = prev->sz;
    int psxz = psx*psz;
    Neuron *pneurons = prev->neurons;

    int sl = this->sl;
    int slpz = sl*psz;
    int sllpz = sl*slpz;

    int ldy = psxz - slpz;

    int kpx = psx-sl, dpx = sx>1 ? sx-1 : 1;
    int kpy = psy-sl, dpy = sy>1 ? sy-1 : 1;

    Real *iw = weights + y0*sxz*sllpz;
    Neuron *in = neurons + y0*sxz;
    int fpy = y0*kpy;
    for(Neuron *e = in + (y1-y0)*sxz; in < e; fpy += kpy) {
      Neuron *pnrow = pneurons + fpy/dpy*psxz;

      int fpx = 0;
      for(Neuron *e = in + sxz; in < e; fpx += kpx) {
        Neuron *pn = pnrow + fpx/dpx*psz;

        for(Neuron *e = in + sz; in < e; ++in) {
          double s = 0;

          Neuron *ipn = pn;
          for(Real *e = iw + sllpz; iw < e; ipn += ldy)
          for(Real *e = iw + slpz; iw < e; ++iw, ++ipn)
            s += *iw * ipn->v;

          // exp sigmoid
          //double ss = 1/(1 + exp(-s));
          //in->v = ss;
          //in->d = ss * (1-ss);

          // 1/(x+1) sigmoid
          double ss = 1/(1+fabs(s));
          double ss2 = ss*0.5;
          in->v = s > 0 ? 1 - ss2 : ss2;
          in->d = ss2 * ss;
        }
      }
    }
  }


  void backpassWeights(int y0, int y1) {
    assert(prev);

    int sx = this->sx;
    int sy = this->sy;
    int sz = this->sz;
    int sxz = sx*sz;

    int psx = prev->sx;
    int psy = prev->sy;
    int psz = prev->sz;
    int psxz = psx*psz;
    Neuron *pneurons = prev->neurons;

    int sl = this->sl;
    int slpz = sl*psz;
    int sllpz = sl*slpz;

    int ldy = psxz - slpz;

    int kpx = psx-sl, dpx = sx>1 ? sx-1 : 1;
    int kpy = psy-sl, dpy = sy>1 ? sy-1 : 1;

    Real *iw = weights + y0*sxz*sllpz;
    Neuron *in = neurons + y0*sxz;
    int fpy = y0*kpy;
    for(Neuron *e = in + (y1-y0)*sxz; in < e; fpy += kpy) {
      Neuron *pnrow = pneurons + fpy/dpy*psxz;

      int fpx = 0;
      for(Neuron *e = in + sxz; in < e; fpx += kpx) {
        Neuron *pn = pnrow + fpx/dpx*psz;

        for(Neuron *e = in + sz; in < e; ++in) {
          Real d = in->d;

          Neuron *ipn = pn;
          for(Real *e = iw + sllpz; iw < e; ipn += ldy)
          for(Real *e = iw + slpz; iw < e; ++iw, ++ipn)
            *iw += ipn->v * d;
        }
      }
    }
  }

  template<bool WithWeights>
  void backpassTpl(int y0, int y1) {
    assert(next);

    Neuron *nneurons = next->neurons;
    Real *nweights = next->weights;
    int *nInvWeights = next->invWeights;
    int lpn = next->linksPerNeuron();

    int sxz = sx*sz;

    Neuron *in = neurons + y0*sxz;
    int *inni = next->invNeurons + y0*sxz;
    for(Neuron *e = in + (y1-y0)*sxz; in < e; ++in) {
      Real v = in->v;
      double s = 0;

      int *iw = nInvWeights + *inni++;
      for(int *e = nInvWeights + *inni; iw < e; ++iw) {
        int wi = *iw;
        Real d = nneurons[wi/lpn].d;
        Real &nw = nweights[wi];
        s += nw * d;
        if (WithWeights) nw += d * v;
      }

      in->d *= s;
    }
  }

  void passAll() {
    if (prev) pass(0, sy);
    if (next) next->passAll();
  }

  void backpassAll() {
    if (!prev)
      return;
    if (!prev->prev)
      { backpassWeights(0, sy); return; }
    if (next)
      backpassTpl<true>(0, sy);
    return prev->backpassAll();
  }
};


#endif