/// <summary>Find the the parameters resulting in the minimal results for a given evaluation measure (2D)</summary>
        /// <remarks>The recommender will be set to the best parameter value after calling this method.</remarks>
        /// <param name="evaluation_measure">the name of the evaluation measure</param>
        /// <param name="hp_name1">the name of the first hyperparameter to optimize</param>
        /// <param name="hp_values1">the values of the first hyperparameter to try out</param>
        /// <param name="hp_name2">the name of the second hyperparameter to optimize</param>
        /// <param name="hp_values2">the values of the second hyperparameter to try out</param>
        /// <param name="recommender">the recommender</param>
        /// <param name="split">the dataset split to use</param>
        /// <returns>the best (lowest) average value for the hyperparameter</returns>
        public static double FindMinimum(
            string evaluation_measure,
            string hp_name1, string hp_name2,
            double[] hp_values1, double[] hp_values2,
            RatingPredictor recommender,
            ISplit <IRatings> split)
        {
            double min_result = double.MaxValue;
            int    min_i      = -1;
            int    min_j      = -1;

            for (int i = 0; i < hp_values1.Length; i++)
            {
                for (int j = 0; j < hp_values2.Length; j++)
                {
                    recommender.SetProperty(hp_name1, hp_values1[i].ToString(CultureInfo.InvariantCulture));
                    recommender.SetProperty(hp_name2, hp_values2[j].ToString(CultureInfo.InvariantCulture));

                    Console.Error.WriteLine("reg_u={0} reg_i={1}", hp_values1[i].ToString(CultureInfo.InvariantCulture), hp_values2[j].ToString(CultureInfo.InvariantCulture));                     // TODO this is not generic
                    double result = recommender.DoCrossValidation(split)[evaluation_measure];
                    if (result < min_result)
                    {
                        min_i      = i;
                        min_j      = j;
                        min_result = result;
                    }
                }
            }

            // set to best hyperparameter values
            recommender.SetProperty(hp_name1, hp_values1[min_i].ToString(CultureInfo.InvariantCulture));
            recommender.SetProperty(hp_name2, hp_values2[min_j].ToString(CultureInfo.InvariantCulture));

            return(min_result);
        }
        /// <summary>Find the the parameters resulting in the minimal results for a given evaluation measure (1D)</summary>
        /// <remarks>The recommender will be set to the best parameter value after calling this method.</remarks>
        /// <param name="evaluation_measure">the name of the evaluation measure</param>
        /// <param name="hyperparameter_name">the name of the hyperparameter to optimize</param>
        /// <param name="hyperparameter_values">the values of the hyperparameter to try out</param>
        /// <param name="recommender">the recommender</param>
        /// <param name="split">the dataset split to use</param>
        /// <returns>the best (lowest) average value for the hyperparameter</returns>
        public static double FindMinimum(
            string evaluation_measure,
            string hyperparameter_name,
            double[] hyperparameter_values,
            RatingPredictor recommender,
            ISplit <IRatings> split)
        {
            double min_result = double.MaxValue;
            int    min_i      = -1;

            for (int i = 0; i < hyperparameter_values.Length; i++)
            {
                recommender.SetProperty(hyperparameter_name, hyperparameter_values[i].ToString(CultureInfo.InvariantCulture));
                double result = recommender.DoCrossValidation(split)[evaluation_measure];

                if (result < min_result)
                {
                    min_i      = i;
                    min_result = result;
                }
            }
            recommender.SetProperty(hyperparameter_name, hyperparameter_values[min_i].ToString(CultureInfo.InvariantCulture));

            return(min_result);
        }