/**
     * The constructor of OrdRecParameter. It use the quantized values of rating to initialize t1
     * and beta. Each threshold is initialized as the mean of two contiguous rating values. Since
     * the index of quantizer is always an successive non-negative integer begin from 0, so t1 will
     * initialize as 0.5, and the interval between two thresholds will be 1.
     *
     * @param qtz The quantizer for ratings
     */
    private OrdRecModel(Quantizer qtz) {
      qtzValues = qtz.getValues();
      levelCount = qtzValues.length();
      t1 = (qtzValues.get(0) + qtzValues.get(1)) / 2;
      beta = Vector.createLength(levelCount - 2);

      double tr = t1;
      for (int i = 1; i <= beta.length(); i++) {
        double trnext = (qtzValues.get(i) + qtzValues.get(i + 1)) * 0.5;
        beta.set(i - 1, Math.log(trnext - tr));
        tr = trnext;
      }
    }
  public SerFunkSVDModel(
      ImmutableMatrix umat,
      ImmutableMatrix imat,
      IdIndexMapping uidx,
      IdIndexMapping iidx,
      List<FeatureInfo> features) {
    super(umat, imat, uidx, iidx);

    featureInfo = ImmutableList.copyOf(features);

    double[] means = new double[featureCount];
    for (int f = featureCount - 1; f >= 0; f--) {
      means[f] = featureInfo.get(f).getUserAverage();
    }
    averageUser = ImmutableVector.wrap(means);
  }
 static ImmutableVector makeValues(PreferenceDomain domain) {
   if (!domain.hasPrecision()) {
     throw new IllegalArgumentException("domain is not discrete");
   }
   final double min = domain.getMinimum();
   final double max = domain.getMaximum();
   final double prec = domain.getPrecision();
   final double nv = (max - min) / prec;
   int n = (int) nv;
   if (Math.abs(nv - n) > 1.0e-6) {
     n += 1; // one more to cover everything...
   }
   if (n == 0) {
     throw new IllegalArgumentException("range has no elements");
   }
   double[] values = new double[n + 1];
   for (int i = 0; i <= n; i++) {
     values[i] = min + (prec * i);
   }
   return ImmutableVector.wrap(values);
 }