Blob Blame Raw
#ifndef NNLAYER2_INC_CPP
#define NNLAYER2_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(x), 1, f); }
template<typename T>
bool tread(const T &x, FILE *f)
  { return fwrite(&x, sizeof(x), 1, f); }


struct Link;
struct Neuron {
  Real v, d;
  inline Neuron(): v(), d() { }
};

struct Link {
  int nprev, lnext;
  Real w;
  inline Link(): nprev(), lnext(), w((Real)rand()/(Real)RAND_MAX*2 - 1) { }
};


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

  int size, lsize;
  Neuron *neurons;
  Link *links;
  int lfirst;

  explicit Layer(Layer *prev = nullptr, int size = 0, int lsize = 0):
    prev(), next(), size(), lsize(), neurons(), links(), lfirst()
  {
    while(prev && prev->next) prev = prev->next;
    if (prev) prev->next = this;
    this->prev = prev;
    init(size, lsize);
  }

  virtual ~Layer() {
    if (next) delete next;
    if (prev) prev->next = nullptr;
    delete[] neurons;
    if (links) delete[] links;
  }

  bool init(int size, int lsize = 0) {
    clear();
    if (size <= 0) return false;
    if (lsize < 0) lsize = 0;

    if (prev ? lsize <= 0 : lsize) return false;
    if (size) neurons = new Neuron[size];
    if (lsize) links = new Link[lsize*size];
    this->size = size;
    this->lsize = lsize;
    return true;
  }

  void clear() {
    if (neurons) delete[] neurons;
    if (links) delete[] links;
    this->size = lsize = 0;
    neurons = nullptr;
    links = nullptr;
    lfirst = 0;
  }

  void prepareBackLinks() {
    if (!links || lsize <= 0) return;

    lfirst = 0;
    int lend = size*lsize;
    for(Link *il = links, *e = il + lend; il < e; ++il) {
      assert(prev && prev->neurons && il->nprev >= 0 && il->nprev < prev->size);
      il->lnext = il - links + 1;
    }

    while(true) {
      bool done = true;
      for(int *il = &lfirst; links[*il].lnext != lend; il = &links[*il].lnext) {
        int a = *il, b = links[a].lnext;
        if (links[a].nprev > links[b].nprev) {
          links[a].lnext = links[b].lnext;
          links[b].lnext = a;
          *il = b;
          done = false;
        }
      }
      if (done) break;
    }

    #ifndef NDEBUG
    if (lsize) {
      int next = 0;
      for(int il = lfirst; il != lend; il = links[il].lnext) {
        assert(links[il].nprev == next || links[il].nprev == next-1);
        if (links[il].nprev == next) ++next;
      }
      assert(next = prev->size);
    }
    #endif
  }

  bool save(FILE *f) const {
    if (!twrite(size, f) || !twrite(lsize, f))
      return false;
    for(Link *il = links, *e = il + lsize*size; il < e; ++il)
      if (!twrite(il->nprev, f) || !twrite(il->w, f))
          return false;
    return true;
  }

  bool load(FILE *f) {
    clear();
    int size = 0, lsize = 0;
    if (!tread(size, f) || !tread(lsize, f) || !init(size, lsize))
      return false;
    for(Link *il = links, *e = il + lsize*size; il < e; ++il)
      if (!tread(il->nprev, f) || !tread(il->w, f) || !prev || !prev->neurons || il->nprev < 0 || il->nprev >= prev->size)
          return false;
    prepareBackLinks();
    return true;
  }

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

  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->size; return t; }
  int totalLinks() const
    { int t = 0; for(const Layer *l = this; l; l = l->next) t += l->size*l->lsize; return t; }
  inline size_t totalMemSize() const
    { return totalNeurons()*sizeof(Neuron) + totalLinks()*sizeof(Link); }

  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 nb, int ne) {
    assert(prev);
    int lsize = this->lsize;
    Neuron *pn = prev->neurons;
    Link *il = links + nb*lsize;
    for(Neuron *in = neurons + nb, *e = neurons + ne; in < e; ++in) {
      double s = 0;
      for(Link *e = il + lsize; il < e; ++il)
        s += pn[il->nprev].v * il->w;

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

  template<bool hasPrev>
  void backpassTpl(int lb, int le) {
    assert(hasPrev == (bool)prev);
    if (lb == le) return;
    Link *links = this->links;
    Neuron *neurons = this->neurons;
    Neuron *pneurons = prev->neurons;

    double s = 0;
    int ipn = links[lb].nprev;
    for(int il = lb; il != le; ) {
      Link &l = links[il];
      if (hasPrev)
        if (ipn != l.nprev)
          { pneurons[ipn].d *= s; ipn = l.nprev; }
      Neuron &n = neurons[ il/lsize ];
      Real d = n.d;
      if (hasPrev) s += d * l.w;
      l.w += d * n.v;
      il = l.lnext;
    }
    if (hasPrev)
        pneurons[ipn].d *= s;

    assert(le == size*lsize || ipn < links[le].nprev);
  }

  void backpass(int lb, int le) {
    if (prev) backpassTpl<true>(lb, le); else backpassTpl<false>(lb, le);
  }

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

  void backpassAll(Real k) {
    if (next) backpass(lfirst, size*lsize);
    if (prev) prev->passAll();
  }
};


#endif