Index: /trunk/Ohana/src/opihi/cmd.data/nnet_commands.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/nnet_commands.c	(revision 40335)
+++ /trunk/Ohana/src/opihi/cmd.data/nnet_commands.c	(revision 40336)
@@ -105,4 +105,5 @@
       gprint (GP_ERR, "invalid bias vector length %s (%d vs %d)\n", argv[Narg + 1], vector->Nelements, nnet[0].Nnodes[L]);
       gprint (GP_ERR, "USAGE: nnet set (nnet) [weights] [biases] ... [weights] [biases] : set nnet weights (images) and biases (vectors)\n");
+      return (FALSE);    
     }
 
@@ -110,4 +111,5 @@
       gprint (GP_ERR, "invalid weight image size for %s (Nx = %d vs %d)\n", argv[Narg + 0], (int) matrix->matrix.Naxis[0], nnet[0].Nnodes[L-1]);
       gprint (GP_ERR, "USAGE: nnet set (nnet) [weights] [biases] ... [weights] [biases] : set nnet weights (images) and biases (vectors)\n");
+      return (FALSE);    
     }
 
@@ -115,4 +117,5 @@
       gprint (GP_ERR, "invalid weight image size for %s (Ny = %d vs %d)\n", argv[Narg + 0], (int) matrix->matrix.Naxis[1], nnet[0].Nnodes[L]);
       gprint (GP_ERR, "USAGE: nnet set (nnet) [weights] [biases] ... [weights] [biases] : set nnet weights (images) and biases (vectors)\n");
+      return (FALSE);    
     }
 
Index: /trunk/Ohana/src/opihi/cmd.data/nnet_train.c
===================================================================
--- /trunk/Ohana/src/opihi/cmd.data/nnet_train.c	(revision 40335)
+++ /trunk/Ohana/src/opihi/cmd.data/nnet_train.c	(revision 40336)
@@ -45,4 +45,26 @@
   }
 
+  Vector *resid = NULL;
+  if ((N = get_argument (argc, argv, "-resid"))) {
+    remove_argument (N, &argc, argv);
+    if ((resid = SelectVector (argv[N], ANYVECTOR, FALSE)) == NULL) {
+      gprint (GP_ERR, "USAGE: nnet train (nnet) [input] [input] ... [output] [output] ...\n");
+      gprint (GP_ERR, "cannot assign residual vector %s\n", argv[N]);
+      return FALSE;
+    }
+    remove_argument (N, &argc, argv);
+  }
+  
+  Buffer *result = NULL;
+  if ((N = get_argument (argc, argv, "-result"))) {
+    remove_argument (N, &argc, argv);
+    if ((result = SelectBuffer (argv[N], ANYBUFFER, FALSE)) == NULL) {
+      gprint (GP_ERR, "USAGE: nnet train (nnet) [input] [input] ... [output] [output] ...\n");
+      gprint (GP_ERR, "cannot assign result image %s\n", argv[N]);
+      return FALSE;
+    }
+    remove_argument (N, &argc, argv);
+  }
+  
   QUADRATIC_COST = 0;
   if ((N = get_argument (argc, argv, "-quadratic-cost"))) {
@@ -54,4 +76,5 @@
     gprint (GP_ERR, "USAGE: nnet train (nnet) [input] [input] ... [output] [output] ...\n");
     gprint (GP_ERR, "OPTIONS: -Nepoch [N] -Nmini [N]\n");
+    FREE (resid);
     return FALSE;
   }
@@ -60,4 +83,5 @@
   if (nnet == NULL) {
     gprint (GP_ERR, "nnet %s not found, create it first\n", argv[1]);
+    FREE (resid);
     return FALSE;
   }
@@ -71,4 +95,5 @@
   if (argc != Ninput + Noutput + 2) {
     gprint (GP_ERR, "need %d input and %d output vectors, but we have %d total\n", nnet[0].Nnodes[0], nnet[0].Nnodes[Nlayer - 1], argc - 2);
+    FREE (resid);
     return FALSE;
   }
@@ -86,5 +111,6 @@
       free (inVec);
       free (outVec);
-      return (FALSE);    
+      FREE (resid);
+      return FALSE;    
     }
     if (Ntrial && (inVec[i][0].Nelements != Ntrial)) {
@@ -92,5 +118,6 @@
       free (inVec);
       free (outVec);
-      return (FALSE);    
+      FREE (resid);
+      return FALSE;    
     }
     if (!Ntrial) Ntrial = inVec[i][0].Nelements;
@@ -99,5 +126,6 @@
       free (inVec);
       free (outVec);
-      return (FALSE);    
+      FREE (resid);
+      return FALSE;    
     }
   }    
@@ -108,5 +136,6 @@
       free (inVec);
       free (outVec);
-      return (FALSE);    
+      FREE (resid);
+      return FALSE;    
     }
     if (outVec[i][0].Nelements != Ntrial) {
@@ -114,5 +143,6 @@
       free (inVec);
       free (outVec);
-      return (FALSE);    
+      FREE (resid);
+      return FALSE;    
     }
   }    
@@ -123,4 +153,31 @@
   ALLOCATE_PTR (seq, int,   Ntrial);
   ALLOCATE_PTR (rnd, float, Ntrial);
+
+  if (resid) ResetVector (resid, OPIHI_FLT, Nepoch);
+
+  if (1) {
+    int Npts = 0;
+    float s1 = 0.0, s2 = 0.0;
+    for (int i = 0; i < Ntrial; i++) {
+
+      // store the input values for this row (trial) in the input value vector
+      for (int j = 0; j < Ninput; j++) {
+	nnet[0].svalue[0][j] = inVec[j][0].elements.Flt[i];
+      }
+	  
+      nnet_feedforward (nnet);
+	  
+      // store the output value vector values in output vectors for this row
+      for (int j = 0; j < Noutput; j++) {
+	float dS = outVec[j][0].elements.Flt[i] - nnet[0].svalue[Nlayer-1][j];
+	s1 += dS;
+	s2 += SQ(dS);
+	Npts ++;
+      }
+    }
+    float mean = s1 / Npts;
+    float sigma = sqrt(s2 / Npts - mean*mean);
+    gprint (GP_ERR, "start, %f +/- %f\n", mean, sigma);
+  }
 
   // train for Nepochs
@@ -137,7 +194,57 @@
       // update the weights and biases using the mini batch subset
       nnet_descent_step (nnet, inVec, outVec, seq, pass, Nmini, eta, lambda);
-      gprint (GP_ERR, "epoch %d of %d, pass %d of %d\n", epoch, Nepoch, pass, Npass);
+    }
+
+    if (resid) {
+      int Npts = 0;
+      float s1 = 0.0, s2 = 0.0;
+      for (int i = 0; i < Ntrial; i++) {
+
+	// store the input values for this row (trial) in the input value vector
+	for (int j = 0; j < Ninput; j++) {
+	  nnet[0].svalue[0][j] = inVec[j][0].elements.Flt[i];
+	}
+	  
+	nnet_feedforward (nnet);
+	  
+	// store the output value vector values in output vectors for this row
+	for (int j = 0; j < Noutput; j++) {
+	  float dS = outVec[j][0].elements.Flt[i] - nnet[0].svalue[Nlayer-1][j];
+	  s1 += dS;
+	  s2 += SQ(dS);
+	  Npts ++;
+	}
+      }
+      float mean = s1 / Npts;
+      float sigma = sqrt(s2 / Npts - mean*mean);
+      if (epoch % 10 == 0) gprint (GP_ERR, "epoch %d of %d, %f +/- %f\n", epoch, Nepoch, mean, sigma);
+      resid[0].elements.Flt[epoch] = sigma;
+    } else {
+      if (epoch % 10 == 0) gprint (GP_ERR, "epoch %d of %d\n", epoch, Nepoch);
     }
   }  
+
+  if (result) {
+
+    gfits_free_matrix (&result[0].matrix);
+    gfits_free_header (&result[0].header);
+    if (!CreateBuffer (result, Noutput, Ntrial, -32, 1.0, 0.0)) return FALSE;
+
+
+    float *value = (float *) result[0].matrix.buffer;
+
+    for (int i = 0; i < Ntrial; i++) {
+      // store the input values for this row (trial) in the input value vector
+      for (int j = 0; j < Ninput; j++) {
+	nnet[0].svalue[0][j] = inVec[j][0].elements.Flt[i];
+      }
+	  
+      nnet_feedforward (nnet);
+	  
+      for (int j = 0; j < Noutput; j++) {
+	value[j + i*Noutput] = nnet[0].svalue[Nlayer-1][j];
+      }
+    }
+  }
 
   free (seq);
@@ -329,8 +436,8 @@
 void nnet_sortseq (float *X, int *Y, int N) {
 
-# define SWAPFUNC(A,B){ float ftmp; int itmp; \
+# define SWAPFUNC(A,B){ float ftmp; int itmp;	\
     ftmp = X[A]; X[A] = X[B]; X[B] = ftmp;      \
-    itmp = Y[A]; Y[A] = Y[B]; Y[B] = itmp;       \
-}
+    itmp = Y[A]; Y[A] = Y[B]; Y[B] = itmp;	\
+  }
 # define COMPARE(A,B)(X[A] < X[B])
 
