// NOTE: Неизвстно, как преобразовать Func<Vector<double>, double> к Func<PointN, double> // Поэтому будут использованы две аналогичные функции // Для них используются ужасно похожие лямбды. // Возможно, этот конфликт можно решить // TODO: надо бы сравнить производительность алгоритмов // Можно привлечь сюда число итераций например // TODO: см в тетради и реши все тестовые задачи // COMPLETE #region TEST2_1 static void Test2_1() { // Input data FunctionNDByAlex funcND = (PointN point) => { return(4 * System.Math.Pow(point.At(0) - 5.0, 2.0) + System.Math.Pow(point.At(1) - 6.0, 2.0)); }; FunctionNDMathNet funcNDMathNet = (VectorNMathNet point) => { return(4 * System.Math.Pow(point.At(0) - 5.0, 2.0) + System.Math.Pow(point.At(1) - 6.0, 2.0)); }; PointN x0 = new PointN(100.0, 100.0); VectorNMathNet x0MathNet = Helpers.PointNToMathNet(x0); const double eps = 0.01; // Testing TestOptimalGradientMethod(funcND, x0, eps); TestVariableMetricMethod(funcNDMathNet, x0MathNet, eps); }
// Это хорошая идея - выносить параметры вроде eps сюда // Можно добавить список строк List<String> // И выводить в него информацию по алгоритму // Затем, в output мы берем его и выводим на экран (по желанию) public PointNMathNet FindMin(double eps) { int k = 0; PointNMathNet xk = x0; // x(k) PointNMathNet xk_1 = x0; // x(k-1) Matrix <double> Hk = H0.Clone(); // Квазиньютоновская матрицы double ak = 0.0; // Вспомогательные вектора VectorNMathNet sk = VectorNMathNet.Build.Dense(n); VectorNMathNet yk = VectorNMathNet.Build.Dense(n); while (true) { VectorNMathNet gradient = Gradient.Calculate(funcND, xk); Log.Add(String.Format("Grad(F(\n{0, -23})) = \n{1, -25}", xk, gradient)); // TODO: некрасивый вывод if (gradient.L2Norm() <= eps) // градиент слишком мал, максимум очень близко { return(xk); } if (k != 0) { sk = xk - xk_1; //Log.Add(String.Format("sk:\n{0}", sk)); Matrix <double> m_sk = sk.ToColumnMatrix(); Matrix <double> m_sk_t = m_sk.Transpose(); yk = Gradient.Calculate(funcND, xk) - Gradient.Calculate(funcND, xk_1); // TODO Один из членов этого вектора становится слишком мал (или Hk или M_yk_t) //Log.Add(String.Format("yk:\n{0}", yk)); Matrix <double> m_yk = yk.ToColumnMatrix(); Matrix <double> m_yk_t = m_yk.Transpose(); Hk = Hk - ((Hk * m_yk * m_yk_t * Hk) / (m_yk_t * Hk * m_yk)[0, 0]) + ((m_sk * m_sk_t)[0, 0] / (m_yk_t * m_sk)[0, 0]); Log.Add(String.Format("Hk:\n{0}", Hk)); } VectorNMathNet pk = -Hk * gradient; Function1D func1D = (double alpha) => { return(funcND((xk + pk * alpha))); }; ak = OneDimensionalMinimization.FindMin(func1D, ak, eps); Log.Add(String.Format("ak = {0}", ak)); // Инкремент xk_1 = xk; xk = xk + ak * pk; k++; Log.Add("==================Next Iteration===================="); } }
public static VectorNMathNet Calculate(Func <PointNMathNet, double> f, PointNMathNet point, double h = 0.1) { int n = point.Count; VectorNMathNet gradient = VectorNMathNet.Build.Dense(n); for (int i = 0; i < n; i++) { PointNMathNet xMinusH = point.Clone(); xMinusH[i] -= h; PointNMathNet xPlusH = point.Clone(); xPlusH[i] += h; gradient[i] = (f(xPlusH) - f(xMinusH)) / (2 * h); } return(gradient); }
// Работает с MathNet.Numerics.LinearAlgebra.Vector<double>; static void TestVariableMetricMethod(FunctionNDMathNet funcNDMathNet, VectorNMathNet x0, double eps) { VariableMetricMethod vmm = new VariableMetricMethod(funcNDMathNet, x0); sw.Start(); VectorNMathNet result = vmm.FindMin(eps); sw.Stop(); //Console.Write(String.Join("\n", vmm.Log)); // Выводим все сообщения от этого метода Console.WriteLine("Рабочая информация метода VariableMetric лежит в txt файле VariableMetricOutput"); System.IO.File.WriteAllText(@"D:\Study\OptimizationMethods\3\VariableMetricOutput.txt", String.Join("\n", vmm.Log)); Console.WriteLine("\n---------------------------------------------"); Console.WriteLine("Variable metric method: x_min = {{{0} {1}}}", result[0].ToString("N6"), result[1].ToString("N6")); Console.WriteLine("F(x_min) = {0}", funcNDMathNet(result)); Console.WriteLine("Time: {0}ms", sw.ElapsedMilliseconds); Console.WriteLine("============================================="); }
// FAILED (первый несет чушь) // 1) Градиент все время прыгает во все стороны, а xk все наращивается // Все потому, что вектор указыает направление наискорейшего возрастания и очень большой // xk быстро наращивается // 2) yk или Hk или M_yk_t становится слишком мал // вследствие этого, на одной из итераций (когда близко к минимуму), мы получаем деление на нуль // Поэтому я выставил eps = 0.5 для того, чтобы метод остановился до деления на нуль // Вывод: Методы чуть менее чем полностью не приспособлены для минимизации не униминимальных функций #region TEST2_1_1 static void Test2_1_1() { // Input data FunctionNDByAlex funcND = (PointN point) => { return(SMath.Pow(SMath.Pow(point.At(0), 2) + point.At(1) - 11.0, 2.0) + SMath.Pow(point.At(0) + SMath.Pow(point.At(1), 2) - 7.0, 2.0)); }; FunctionNDMathNet funcNDMathNet = (VectorNMathNet point) => { return(SMath.Pow(SMath.Pow(point.At(0), 2) + point.At(1) - 11.0, 2.0) + SMath.Pow(point.At(0) + SMath.Pow(point.At(1), 2) - 7.0, 2.0)); }; PointN x0 = new PointN(3.0, 3.0); VectorNMathNet x0MathNet = Helpers.PointNToMathNet(x0); const double eps = 0.1; // TODO при слишком малых eps происходит деление на 0 в формуле пересчета Hk // Testing TestOptimalGradientMethod(funcND, x0, eps); TestVariableMetricMethod(funcNDMathNet, x0MathNet, eps); }