Blob Blame Raw
#ifndef LAYER_CONV_TEST_INC_CPP
#define LAYER_CONV_TEST_INC_CPP



#include "layer.test.inc.cpp"
#include "layer.conv.inc.cpp"


class ConvTest: public Test {
public:
  static void init(const Layout &cl, const Layout &pl, const Kernel &k, bool shared = false)
    { Test::init(cl.getCount(), pl.getCount(), (shared ? 1 : cl.getActiveCount())*k.sx*k.sy*pl.getD()); }


  static bool verifyWeights(const char *name, const Layout &cl, const Layout &pl, const Kernel &k) {
    Stage st(name);
    for(int cy = cl.y0; cy < cl.y1; ++cy)
    for(int cx = cl.x0; cx < cl.x1; ++cx)
    for(int cz = cl.z0; cz < cl.z1; ++cz) {
      int ci = (cy*cl.sx + cx)*cl.sz + cz;
      for(int ky = 0; ky < k.sy; ++ky)
      for(int kx = 0; kx < k.sx; ++kx)
      for(int pz = pl.z0; pz < pl.z1; ++pz) {
        int wi = ((cy - cl.y0)*cl.getW() + cx - cl.x0)*cl.getD() + cz - cl.z0;
        wi = ((wi*k.sy + ky)*k.sx + kx)*pl.getD() + pz - pl.z0;

        int px = pl.x0 + (cx - cl.x0)*k.dx + k.ox + kx;
        int py = pl.y0 + (cy - cl.y0)*k.dy + k.oy + ky;
        if ( px < pl.x0 || px >= pl.x1
          || py < pl.y0 || py >= pl.y1 ) continue;

        int pi = (py*pl.sx + px)*pl.sz + pz;

        int s = (int)p_neurons.size();
        int w = weights[wi].i;
        int i = ci*s + pi + 1;

        if (w != i) {
          int ww = w;
          int wpz = ww%pl.sz; ww /= pl.sz;
          int wpx = ww%pl.sx; ww /= pl.sx;
          int wpy = ww%pl.sy; ww /= pl.sy;
          int wcz = ww%cl.sz; ww /= cl.sz;
          int wcx = ww%cl.sx; ww /= cl.sx;
          int wcy = ww;

          printf(
            "wrong index: %d = ((%d*%d + %d)*%d + %d)*%d + (%d*%d + %d)*%d + %d + 1,\n"
            "expected:    %d = ((%d*%d + %d)*%d + %d)*%d + (%d*%d + %d)*%d + %d + 1\n"
            "wi %d, ky %d, kx %d \n",
            w,
            wcy, cl.sx, wcx, cl.sz, wcz, s,
            wpy, pl.sx, wpx, pl.sz, wpz,
            i,
            cy, cl.sx, cx, cl.sz, cz, s,
            py, pl.sx, px, pl.sz, pz,
            wi, ky, kx );
          pl.printYXZ("prev layout");
          cl.printYXZ("curr layout");
          k.printYX("kernel");
          ++errors;
          return st;
        }
      }
    }
    return st;
  }

  static bool testIterators(const char *name, const Layout &cl, const Layout &pl, const Kernel &k, int threads) {
    Stage st(name);

    assert(cl && pl && k && threads > 0);
    Layout::List clist, plist;
    cl.split(clist, threads);
    pl.split(plist, threads);
    
    struct I: public Iter {
      static inline void init(Neuron &n, Iter::AccumType &a) { ++n.a.i; a.i = (AccumInt)(&n - c_neurons.data()); }
      static inline void iter(Neuron &n, Weight &w, Iter::AccumType &a) {
        if (w.i)
          ++errors;
        w.i = (WeightInt)(&n - p_neurons.data() + a.i*p_neurons.size() + 1);
      }
      static inline void iter2(Neuron &cn, Neuron &pn, Weight &w) {
        if (w.i)
          ++errors;
        w.i = (WeightInt)((&cn - c_neurons.data())*p_neurons.size() + &pn - p_neurons.data() + 1);
        pn.v = pn.v + 1;
      }
    };

    if (threads == 1) {
      Stage st("iterateTestConvolution");
      init(cl, pl, k);
      iterateTestConvolution<I>(cl, pl, k, c_neurons.data(), p_neurons.data(), weights.data());
      verifyNeurons("conv-neurons", cl, c_neurons.data());
      verifyWeights("conv-weights", cl, pl, k);
    }

    {
      Stage st("iterateConvolution");
      init(cl, pl, k);
      for(int i = 0; i < threads; ++i)
        iterateConvolution<I>(clist[i], pl, cl, k, c_neurons.data(), p_neurons.data(), weights.data());
      verifyNeurons("conv-neurons", cl, c_neurons.data());
      verifyWeights("conv-weights", cl, pl, k);
    }

    {
      Stage st("iterateConvolutionPoint");
      init(cl, pl, k);
      int e = errors;
      for(int ky = 0; ky < k.sy && errors == e; ++ky)
      for(int kx = 0; kx < k.sx && errors == e; ++kx) {
        for(int i = 0; i < threads; ++i)
          iterateConvolutionPoint<I>(clist[i], pl, cl, k, kx, ky, c_neurons.data(), p_neurons.data(), weights.data());
        if (!verifyNeuronsAccum(pl, p_neurons.data(), cl.getD(), true))
          printf("kx: %d, ky: %d\n", kx, ky), k.printYX("kernel");
      }
      verifyNeurons("conv-neurons", pl, p_neurons.data(), true);
      verifyWeights("conv-weights", cl, pl, k);
    }

    return st;
  }

  static bool testIterators(const char *name, const Layout &cl, const Layout &pl, const Kernel &k) {
    Stage st(name);
    testIterators( "single-thread", cl, pl, k,   1 );
    testIterators( "2-threads",     cl, pl, k,   2 );
    testIterators( "7-threads",     cl, pl, k,   7 );
    testIterators( "8-threads",     cl, pl, k,   8 );
    testIterators( "512-threads",   cl, pl, k, 512 );
    return st;
  }
  
  static bool test(const char *name, const Layout &cl, const Layout &pl, const Kernel &k) {
    Stage st(name);
    
    testIterators("iterators", cl, pl, k);

    {
      Layer l(nullptr, pl);
      new LayerConv<funcSigmoidExp>(l, cl, k);
      Test::testLayer("LayerConv", l);
    }

    {
      Layer l(nullptr, pl);
      new LayerConv<funcSigmoidExp>(l, cl, k);
      Test::testLayer("LayerDeconv", l);
    }

    return st;
  }

  static bool test(const char *name = "convolution") {
    Stage st(name);
    test( "square", Layout(64, 64, 4), Layout(128, 128, 4).expandXY(2),                 Kernel(5, 2, -2)           );
    test( "rect1",  Layout(63, 43, 5), Layout( 63,  85, 3).expandX(2).   expandY(3),    Kernel(5, 7, 1, 2, -2, -3) );
    test( "rect2",  Layout(43, 63, 3), Layout( 85,  63, 5).expandX(3).   expandY(3, 2), Kernel(7, 5, 2, 1, -3, -2) );
    test( "rect3",  Layout(64, 48, 5), Layout( 64,  96, 3).expandX(1, 2).expandY(3, 1), Kernel(4, 6, 1, 2, -1, -3) );
    test( "pad",    Layout(64, 48, 5).expandX(3, 4).expandY(4, 3).expandZ(5, 4),
                    Layout(64, 96, 3).expandX(6, 5).expandY(7, 6).expandZ(0, 1),        Kernel(4, 6, 1, 2, -1, -3) );
    return st;
  }
};


#endif