docs: @Override
    public void calculate(double[] x) {
  
      double prob = 0.0; // the log prob of the sequence given the model, which is the negation of value at this point
      double[][] weights = to2D(x);
  
      // the expectations over counts
      // first index is feature index, second index is of possible labeling
      double[][] E = empty2D();
  
      // iterate over all the documents
      for (int m = 0; m < data.length; m++) {
        int[][][] docData = data[m];
        int[] docLabels = labels[m];
  
        // make a clique tree for this document
        CRFCliqueTree cliqueTree = CRFCliqueTree.getCalibratedCliqueTree(weights, docData, labelIndices, numClasses, classIndex, backgroundSymbol);
  
        // compute the log probability of the document given the model with the parameters x
        int[] given = new int[window - 1];
        Arrays.fill(given, classIndex.indexOf(backgroundSymbol));
        if (docLabels.length>docData.length) { // only true for self-training
          // fill the given array with the extra docLabels
          System.arraycopy(docLabels, 0, given, 0, given.length);
          // shift the docLabels array left
          int[] newDocLabels = new int[docData.length];
          System.arraycopy(docLabels, docLabels.length-newDocLabels.length, newDocLabels, 0, newDocLabels.length);
          docLabels = newDocLabels;
        }
        // iterate over the positions in this document
        for (int i = 0; i < docData.length; i++) {
          int label = docLabels[i];
          double p = cliqueTree.condLogProbGivenPrevious(i, label, given);
          if (VERBOSE) {
            System.err.println("P(" + label + "|" + ArrayMath.toString(given) + ")=" + p);
          }
          prob += p;
          System.arraycopy(given, 1, given, 0, given.length - 1);
          given[given.length - 1] = label;
        }
  
        // compute the expected counts for this document, which we will need to compute the derivative
        // iterate over the positions in this document
        for (int i = 0; i < data[m].length; i++) {
          // for each possible clique at this position
          for (int j = 0; j < data[m][i].length; j++) {
            Index<CRFLabel> labelIndex = labelIndices[j];
            // for each possible labeling for that clique
            for (int k = 0; k < labelIndex.size(); k++) {
              int[] label = labelIndex.get(k).getLabel();
              double p = cliqueTree.prob(i, label); // probability of these labels occurring in this clique with these features
              for (int n = 0; n < data[m][i][j].length; n++) {
                E[data[m][i][j][n]][k] += p;
              }
            }
          }
        }
      }
  
      if (Double.isNaN(prob)) { // shouldn't be the case
        throw new RuntimeException("Got NaN for prob in CRFLogConditionalObjectiveFunction.calculate()");
      }
  
      value = -prob;
      if(VERBOSE){
        System.err.println("value is " + value);
      }
      // compute the partial derivative for each feature by comparing expected counts to empirical counts
      int index = 0;
      for (int i = 0; i < E.length; i++) {
        for (int j = 0; j < E[i].length; j++) {
          derivative[index++] = (E[i][j] - Ehat[i][j]);
          if (VERBOSE) {
            System.err.println("deriv(" + i + "," + j + ") = " + E[i][j] + " - " + Ehat[i][j] + " = " + derivative[index - 1]);
          }
        }
      }
  
  
      // incorporate priors
      if (prior == QUADRATIC_PRIOR) {
        double sigmaSq = sigma * sigma;
        for (int i = 0; i < x.length; i++) {
          double k = 1.0;
          double w = x[i];
          value += k * w * w / 2.0 / sigmaSq;
          derivative[i] += k * w / sigmaSq;
        }
      } else if (prior == HUBER_PRIOR) {
        double sigmaSq = sigma * sigma;
        for (int i = 0; i < x.length; i++) {
          double w = x[i];
          double wabs = Math.abs(w);
          if (wabs < epsilon) {
            value += w * w / 2.0 / epsilon / sigmaSq;
            derivative[i] += w / epsilon / sigmaSq;
          } else {
            value += (wabs - epsilon / 2) / sigmaSq;
            derivative[i] += ((w < 0.0) ? -1.0 : 1.0) / sigmaSq;
          }
        }
      } else if (prior == QUARTIC_PRIOR) {
        double sigmaQu = sigma * sigma * sigma * sigma;
        for (int i = 0; i < x.length; i++) {
          double k = 1.0;
          double w = x[i];
          value += k * w * w * w * w / 2.0 / sigmaQu;
          derivative[i] += k * w / sigmaQu;
        }
      }
  
  
    }