@Override
 public double computeLoss() {
   return Ratings.evaluate(this, ratings).get("RMSE")
       + regU * Math.pow(org.mymedialite.datatype.VectorExtensions.euclideanNorm(userBiases), 2)
       + regI * Math.pow(org.mymedialite.datatype.VectorExtensions.euclideanNorm(itemBiases), 2);
 }
  public static void main(String[] args) throws Exception {

    // Handlers for uncaught exceptions and interrupts
    Thread.setDefaultUncaughtExceptionHandler(new Handlers());
    Runtime.getRuntime()
        .addShutdownHook(
            new Thread() {
              @Override
              public void run() {
                displayStats();
              }
            });

    // Recommender arguments
    String method = null;
    String recommender_options = "";

    // Help/version
    boolean show_help = false;
    boolean show_version = false;

    // Arguments for iteration search
    int max_iter = 100;
    double epsilon = 0;
    double rmse_cutoff = Double.MAX_VALUE;
    double mae_cutoff = Double.MAX_VALUE;

    // Data arguments
    String data_dir = "";

    // Other arguments
    boolean search_hp = false;
    int random_seed = -1;
    String prediction_line = "{0}\t{1}\t{2}";

    for (String arg : args) {
      int div = arg.indexOf("=") + 1;
      String name;
      String value;
      if (div > 0) {
        name = arg.substring(0, div);
        value = arg.substring(div);
      } else {
        name = arg;
        value = null;
      }

      // String-valued options
      if (name.equals("--training-file=")) training_file = value;
      else if (name.equals("--test-file=")) test_file = value;
      else if (name.equals("--recommender=")) method = value;
      else if (name.equals("--recommender-options=")) recommender_options += " " + value;
      else if (name.equals("--data-dir=")) data_dir = value;
      else if (name.equals("--user-attributes=")) user_attributes_file = value;
      else if (name.equals("--item-attributes=")) item_attributes_file = value;
      else if (name.equals("--user-relations=")) user_relations_file = value;
      else if (name.equals("--item-relations=")) item_relations_file = value;
      else if (name.equals("--save-model=")) save_model_file = value;
      else if (name.equals("--load-model=")) load_model_file = value;
      else if (name.equals("--prediction-file=")) prediction_file = value;
      else if (name.equals("--prediction-line=")) prediction_line = value;
      else if (name.equals("--chronological-split=")) chronological_split = value;

      // Integer-valued options
      else if (name.equals("--find-iter=")) find_iter = Integer.parseInt(value);
      else if (name.equals("--max-iter=")) max_iter = Integer.parseInt(value);
      else if (name.equals("--random-seed=")) random_seed = Integer.parseInt(value);
      else if (name.equals("--cross-validation=")) cross_validation = Integer.parseInt(value);

      // Double-valued options
      else if (name.equals("--epsilon=")) epsilon = Double.parseDouble(value);
      else if (name.equals("--rmse-cutoff=")) rmse_cutoff = Double.parseDouble(value);
      else if (name.equals("--mae-cutoff=")) mae_cutoff = Double.parseDouble(value);
      else if (name.equals("--test-ratio=")) test_ratio = Double.parseDouble(value);

      // Enum options
      else if (name.equals("--rating-type=")) rating_type = RatingType.valueOf(value);
      else if (name.equals("--file-format=")) file_format = RatingFileFormat.valueOf(value);

      // Boolean options
      else if (name.equals("--compute-fit")) compute_fit = true;
      else if (name.equals("--online-evaluation")) online_eval = true;
      else if (name.equals("--show-fold-results")) show_fold_results = true;
      else if (name.equals("--search-hp")) search_hp = true;
      else if (name.equals("--help")) show_help = true;
      else if (name.equals("--version")) show_version = true;
    }
    // ... some more command line parameter actions ...
    boolean no_eval = true;
    if (test_ratio > 0 || test_file != null || chronological_split != null) no_eval = false;

    if (show_version) showVersion();

    if (show_help) usage(0);

    if (random_seed != -1) org.mymedialite.util.Random.initInstance(random_seed);

    // Set up recommender
    if (load_model_file != null) recommender = (RatingPredictor) Model.load(load_model_file);
    else if (method != null) recommender = Recommender.createRatingPredictor(method);
    else recommender = Recommender.createRatingPredictor("BiasedMatrixFactorization");

    // In case something went wrong ...
    if (recommender == null && method != null) usage("Unknown rating prediction method: " + method);
    if (recommender == null && load_model_file != null)
      usage("Could not load model from file " + load_model_file);

    checkParameters();

    try {
      recommender = Recommender.configure(recommender, recommender_options, new ErrorHandler());
    } catch (IllegalAccessException e) {
      System.err.println("Unable to instantiate recommender: " + recommender.toString());
      System.exit(0);
    }

    // ID mapping objects
    if (file_format == RatingFileFormat.KDDCUP_2011) {
      user_mapping = new IdentityMapping();
      item_mapping = new IdentityMapping();
    }

    // Load all the data
    loadData(
        data_dir,
        user_attributes_file,
        item_attributes_file,
        user_relations_file,
        item_relations_file,
        !online_eval);

    System.out.println(
        "Ratings range: " + recommender.getMinRating() + ", " + recommender.getMaxRating());

    if (test_ratio > 0) {
      RatingsSimpleSplit split = new RatingsSimpleSplit(training_data, test_ratio);
      // TODO check
      training_data = split.train().get(0);
      recommender.setRatings(training_data);
      // TODO check
      test_data = split.test().get(0);
      System.out.println("Test ratio: " + test_ratio);
    }

    if (chronological_split != null) {
      RatingsChronologicalSplit split =
          chronological_split_ratio != -1
              ? new RatingsChronologicalSplit(
                  (ITimedRatings) training_data, chronological_split_ratio)
              : new RatingsChronologicalSplit(
                  (ITimedRatings) training_data, chronological_split_time);
      training_data = split.train().get(0);
      recommender.setRatings(training_data);
      test_data = split.test().get(0);
      if (test_ratio != -1)
        System.out.println("Test ratio (chronological): " + chronological_split_ratio);
      else System.out.println("Split time:" + chronological_split_time);
    }

    System.out.print(
        Extensions.statistics(training_data, test_data, user_attributes, item_attributes, false));

    if (find_iter != 0) {
      if (!(recommender instanceof IIterativeModel))
        usage("Only iterative recommenders (interface IIterativeModel) support --find-iter=N.");

      System.out.println("Recommender: " + recommender.toString());

      if (cross_validation > 1) {
        RatingsCrossValidation.doIterativeCrossValidation(
            recommender, cross_validation, max_iter, find_iter);
      } else {
        IIterativeModel iterative_recommender = (IIterativeModel) recommender;

        if (load_model_file == null) recommender.train();

        if (compute_fit)
          System.out.println(
              "Fit "
                  + Ratings.evaluate(recommender, training_data)
                  + " iteration "
                  + iterative_recommender.getNumIter());

        System.out.println(
            Ratings.evaluate(recommender, test_data)
                + " iteration "
                + iterative_recommender.getNumIter());

        for (int it = iterative_recommender.getNumIter() + 1; it <= max_iter; it++) {
          long start = Calendar.getInstance().getTimeInMillis();
          iterative_recommender.iterate();
          training_time_stats.add(
              (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000);

          if (it % find_iter == 0) {
            if (compute_fit) {
              start = Calendar.getInstance().getTimeInMillis();
              System.out.println(
                  "Fit " + Ratings.evaluate(recommender, training_data) + " iteration " + it);
              fit_time_stats.add(
                  (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000);
            }

            HashMap<String, Double> results = null;
            start = Calendar.getInstance().getTimeInMillis();
            results = Ratings.evaluate(recommender, test_data);
            eval_time_stats.add((double) (Calendar.getInstance().getTimeInMillis() - start) / 1000);
            rmse_eval_stats.add(results.get("RMSE"));
            System.out.println(results + " iteration " + it);

            Model.save(recommender, save_model_file, it);
            if (prediction_file != null)
              org.mymedialite.ratingprediction.Extensions.writePredictions(
                  recommender,
                  test_data,
                  prediction_file + "-it-" + it,
                  user_mapping,
                  item_mapping,
                  prediction_line);

            if (epsilon > 0.0 && results.get("RMSE") - Collections.min(rmse_eval_stats) > epsilon) {
              System.out.println(results.get("RMSE") + " >> " + Collections.min(rmse_eval_stats));
              System.out.println(
                  "Reached convergence on training/validation data after " + it + " iterations.");
              break;
            }
            if (results.get("RMSE") > rmse_cutoff || results.get("MAE") > mae_cutoff) {
              System.out.println("Reached cutoff after " + it + " iterations.");
              break;
            }
          }
        } // for
      }
    } else {
      long start = Calendar.getInstance().getTimeInMillis();

      System.out.println("Recommender: " + recommender);

      if (load_model_file == null) {
        if (cross_validation > 1) {
          RatingPredictionEvaluationResults results =
              RatingsCrossValidation.doCrossValidation(
                  recommender, cross_validation, compute_fit, show_fold_results);
          System.out.println(results);
          no_eval = true;
        } else {
          if (search_hp) {
            double result = NelderMead.findMinimum("RMSE", recommender);
            System.out.println("Estimated quality (on split): " + result);
          }

          recommender.train();
          System.out.println(
              "Training time: "
                  + (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000
                  + " seconds");
        }
      }

      if (!no_eval) {
        start = Calendar.getInstance().getTimeInMillis();
        if (online_eval) System.out.println(RatingsOnline.evaluateOnline(recommender, test_data));
        else System.out.println(Ratings.evaluate(recommender, test_data));

        System.out.println(
            "Testing time: "
                + (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000
                + " seconds");

        if (compute_fit) {
          System.out.print("Fit:");
          start = Calendar.getInstance().getTimeInMillis();
          System.out.print(Ratings.evaluate(recommender, training_data));
          System.out.println(
              " fit time: " + (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000);
        }

        if (prediction_file != null) {
          System.out.print("Predict:");
          start = Calendar.getInstance().getTimeInMillis();
          org.mymedialite.ratingprediction.Extensions.writePredictions(
              recommender, test_data, prediction_file, user_mapping, item_mapping, prediction_line);
          System.out.println(
              " prediction time "
                  + (double) (Calendar.getInstance().getTimeInMillis() - start) / 1000);
        }
      }
      // System.out.println();
    }
    Model.save(recommender, save_model_file);
    // displayStats();

  }