docs: private void sgdTrain(ClassificationDataSet D, MatrixOfVecs W, Vec b, int sign_mul, boolean parallel)
      {
          IntList order = new IntList(D.size());
          ListUtils.addRange(order, 0, D.size(), 1);
          
          final double lambda_adj = lambda/(D.size()*epochs);
          
          int[] owned = new int[K];//how many points does thsi guy own?
          int assigned_positive_instances = 0;//how many points in the positive class have been assigned?
          int[] assignments = new int[D.size()];//who owns each data point
          Arrays.fill(assignments, -1);//Starts out that no one is assigned! 
          
          Vec dots = new DenseVector(W.rows());
          
          long t = 0;
          for(int epoch = 0; epoch < epochs; epoch++)
          {
              Collections.shuffle(order);
              for(int i : order)
              {
                  t++;
                  double eta = 1/(lambda_adj*t);
                  Vec x_i = D.getDataPoint(i).getNumericalValues();
                  int y_i = (D.getDataPointCategory(i)*2-1)*sign_mul;
                  
                  //this sets dots = bias, which we then add to with matrix-vector product
                  //result is the same as dots =  W x_i + b
                  b.copyTo(dots);
                  W.multiply(x_i, 1.0, dots);
                  
                  if(y_i == -1)
                  {
                      for(int k = 0; k < K; k++)
                          if(dots.get(k) > -1)
                          {
                              W.getRowView(k).mutableSubtract(eta, x_i);
                              b.increment(k, -eta);
                          }
                  }
                  else//y_i == 1
                  {
                      int k_true_max = 0;
                      for(int k = 1; k < dots.length(); k++)
                          if(dots.get(k) > dots.get(k_true_max))
                              k_true_max = k;
                      
                      if(dots.get(k_true_max) < 1)
                      {
                          int z = ASSIGN(dots, i, k_true_max, owned, assignments, assigned_positive_instances);
                          W.getRowView(z).mutableAdd(eta, x_i);
                          b.increment(z, eta);
                          
                          //book keeping
                          if(assignments[i] < 0)//first assignment, inc counter
                              assigned_positive_instances++;
                          else//change owner, decrement ownership count
                              owned[assignments[i]]--;
                          owned[z]++;
                          assignments[i] = z;
                          
                      }
                  }
                  
  //                W.mutableMultiply(1-eta*lambda);
                  //equivalent form, more stable
                  W.mutableMultiply(1-1.0/t);
                  b.mutableMultiply(1-1.0/t);
              }
          }
      }