public void TestCudafyMacLane() { var random = new Random(); for (int k = 0; k < 10; k++) { var matrix = new BooleanMatrix( Enumerable.Range(0, 5).Select(i1 => Enumerable.Range(0, 10).Select(i => random.Next() % 2 == 0))); int[] indexes = Enumerable.Range(1, 5).Select(i => random.Next() % 5).ToArray(); Console.WriteLine(string.Join(",", indexes.Select(i => i.ToString()).ToList())); /////////////////////////////////////////////////// // Вычисление целевой функции обычным методом IEnumerable <KeyValuePair <int, int> > dictionary = indexes.Select((item, value) => new KeyValuePair <int, int>(value, item)); var matrix2 = new BooleanMatrix( dictionary.Select( pair1 => dictionary .Where(pair2 => pair2.Value == pair1.Key && pair2.Key != pair1.Key) .Select(pair => matrix[pair.Key]) .Aggregate(matrix[pair1.Key], BooleanVector.Xor))); int macLane1 = matrix2.MacLane; int rows = matrix.Count; int columns = matrix.Length; lock (CudafyMatrix.Semaphore) { CudafyMatrix.SetMatrix( new ArrayOfArray <int>(matrix.Select(vector => vector.Select(b => b ? 1 : 0).ToArray()).ToArray()) .ToTwoDimensional()); CudafyMatrix.SetIndexes(indexes.ToArray()); CudafyMatrix.ExecuteMacLane(); int macLane2 = CudafyMatrix.GetMacLane(); CudafyMatrix.ExecuteUpdate(); Console.WriteLine(string.Join(",", CudafyMatrix.GetIndexes().Select(i => i.ToString()).ToList())); Console.WriteLine(); Console.WriteLine(matrix); Console.WriteLine(); Console.WriteLine(matrix2); Console.WriteLine(); Console.WriteLine(string.Join(Environment.NewLine, Enumerable.Range(0, rows) .Select(row => string.Join("", Enumerable.Range(0, columns) .Select( column => CudafyMatrix.GetMatrix()[row, column].ToString()) .ToList())).ToList())); Console.WriteLine(); //Console.WriteLine(string.Join(Environment.NewLine, // Enumerable.Range(0, rows) // .Select(row => string.Join("", Enumerable.Range(0, columns) // .Select(column => CudafyMatrix.GetMatrix()[row * columns + column].ToString()).ToList())).ToList())); Console.WriteLine(macLane1); Console.WriteLine(macLane2); Assert.AreEqual(macLane1, macLane2); } } }
public bool IsPlanar(Graph graphArgument) { if (WorkerLog != null) { WorkerLog("Начало алгоритма Мак-Лейна"); } var graph = new Graph(graphArgument); Debug.Assert( graph.Children.All(pair => pair.Value .All(value => graph.Children.ContainsKey(value) && graph.Children[value].Contains(pair.Key)))); if (WorkerBegin != null) { WorkerBegin(); } // Шаг первый - удаляем все листья и узлы степени 2 if (WorkerLog != null) { WorkerLog("Удаляем все листья и узлы степени 2"); } // листья представляют собой дерево и нарисовать его плоскую укладку тривиально. graph.RemoveAllTrees(); // Замечание. Если на ребра планарного графа нанести произвольное число вершин степени 2, // то он останется планарным; равным образом, если на ребра непланарного графа // нанести вершины степени 2, то он планарным не станет. graph.RemoveIntermedians(); // Шаг второй - граф нужно укладывать отдельно по компонентам связности. var stackListQueue = new StackListQueue <Graph> { graph.GetAllSubGraphs() }; if (WorkerLog != null) { WorkerLog("Находим ВСЕ пути в графе длины не более размера графа + 1"); } // Глобальные кэшированные данные Dictionary <int, PathDictionary> cachedAllGraphPaths = graph.GetAllGraphPaths(); foreach (Graph subGraph in stackListQueue) { if (WorkerLog != null) { WorkerLog("Проверка связанной компоненты " + subGraph); } // листья представляют собой дерево и нарисовать его плоскую укладку тривиально. subGraph.RemoveAllTrees(); if (subGraph.Vertices.Count() < 2) { continue; } Dictionary <int, PathDictionary> cachedSubGraphPaths = Graph.GetSubgraphPaths(subGraph.Vertices, cachedAllGraphPaths); if (WorkerLog != null) { WorkerLog("Находим ВСЕ циклы в графе длины не менее 2 и не более размера графа"); } var circles = new StackListQueue <Circle>(cachedSubGraphPaths.Where(pair => pair.Key > 2) .SelectMany(pair => subGraph.Vertices .SelectMany(vertex => pair.Value.Where(pair2 => pair2.Key.Key.Equals(pair2.Key.Value)) .SelectMany( pair2 => pair2.Value.Select(path => new Circle(path.GetRange(0, path.Count - 1))))))); if (WorkerLog != null) { WorkerLog(string.Format("Количество циклов {0}", circles.Count())); } //if (WorkerLog != null) WorkerLog("Находим все циклы в графе"); //IEnumerable<Circle> circles = subGraph.GetAllGraphCircles(cachedSubGraphPaths); if (!circles.Any()) { continue; // граф — дерево и нарисовать его плоскую укладку тривиально. } if (WorkerLog != null) { WorkerLog("Удаляем дубликаты"); } circles = new StackListQueue <Circle>(circles.Distinct(CircleComparer)); if (WorkerLog != null) { WorkerLog(string.Format("Количество циклов {0}", circles.Count())); } //Debug.Assert(subGraph.Vertices.Count() == // circles.SelectMany(circle => circle.ToList()).Distinct().Count()); // С технической точки зрения проверять, что цикл является простым и тау-циклом нет необходимости, поскольку не // приведён алгорим позволяющий проверить , что цикл является тау-циклом за количество операций меньшее чем приведение // матрицы к каноническому виду. Поэтому если действительно надо сделать хорошую реализацию, то либо надо закоментировать // проверки циклов на простоту и что они являются тау-циклами с помощью приведения к каноническому виду , либо // предложить алгоритм быстрой проверки, что цикл является тау-циклом if (WorkerLog != null) { WorkerLog("Ограничиваем простыми циклами"); } circles.RemoveAll(Circle.IsNotSimple); if (WorkerLog != null) { WorkerLog(string.Format("Количество циклов {0}", circles.Count())); } if (WorkerLog != null) { WorkerLog("Удаляем элементарные циклы и петли"); } circles.RemoveAll(circle => circle.Count < 3); if (WorkerLog != null) { WorkerLog(string.Format("Количество циклов {0}", circles.Count())); } //if (WorkerLog != null) WorkerLog("Ограничиваем тау-циклами"); //circles.RemoveAll(circle => !circle.IsTau()); //if (WorkerLog != null) WorkerLog(string.Format("Количество циклов {0}", circles.Count())); Debug.WriteLine(string.Join(Environment.NewLine, circles.Select(circle => circle.ToString()))); if (WorkerLog != null) { WorkerLog("Строим матрицу над GF2 из найденных циклов"); } var booleanMatrix = new BooleanMatrix(circles.Select(subGraph.GetVector)); if (WorkerLog != null) { WorkerLog(string.Format("Размер матрицы {0}х{1}", booleanMatrix.Count(), booleanMatrix.Length)); } foreach (var r in booleanMatrix) { if (r.Count() < booleanMatrix.Length) { r.Add(Enumerable.Repeat(false, booleanMatrix.Length - r.Count())); } } Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); // отыскание минимума некоторого функционала на множестве базисов подпространства квазициклов // Шаг 1. Приведение матрицы к каноническому виду if (WorkerLog != null) { WorkerLog("Приводим матрицу к каноническому виду"); } if (Settings.EnableCudafy) { lock (CudafyMatrix.Semaphore) { try { ///////////////////////////////////////////////////// // Использование параллельных вычислений CUDA // для приведения матрицы к каноническому виду CudafyMatrix.SetMatrix( new ArrayOfArray <int>( booleanMatrix.Select(vector => vector.Select(b => b ? 1 : 0).ToArray()).ToArray()) .ToTwoDimensional()); // Использование алгоритма Гаусса-Жордана // Для приведения матрицы к каноническому виду CudafyMatrix.ExecuteGaussJordan(); // Удаляем нулевые строки int[][] arrayOfArray = new TwoDimensionalArray <int>(CudafyMatrix.GetMatrix()).ToArrayOfArray(); booleanMatrix = new BooleanMatrix(CudafyMatrix.GetIndexes() .Select((first, row) => new KeyValuePair <int, int>(row, first)) .Where(pair => pair.Value >= 0) .Select(pair => arrayOfArray[pair.Key].Select(value => value != 0))); CudafyMatrix.SetMatrix( new ArrayOfArray <int>( booleanMatrix.Select(vector => vector.Select(b => b ? 1 : 0).ToArray()).ToArray()) .ToTwoDimensional()); } catch (Exception ex) { if (WorkerLog != null) { WorkerLog(ex.ToString()); } ///////////////////////////////////////////////////// // Приведение матрицы к каноническому виду обычным способом booleanMatrix.GaussJordan(); booleanMatrix.RemoveAll(BooleanVector.IsZero); } if (WorkerLog != null) { WorkerLog(string.Format("Размер матрицы {0}х{1}", booleanMatrix.Count(), booleanMatrix.Length)); } // Матрица имеет канонический вид Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); Debug.Assert(booleanMatrix.Select(vector => vector.IndexOf(true)).Distinct().Count() == booleanMatrix.Count); Debug.Assert(booleanMatrix.Select(vector => vector.IndexOf(true)) .SelectMany( index => booleanMatrix.Where(vector => vector.Count > index && vector[index])) .Count() == booleanMatrix.Count); // Поскольку в колонках содержится по одной единице, то к строке можно прибавить только одну другую строку int n = booleanMatrix.Count; int macLane; try { ///////////////////////////////////////////////////// // Использование параллельных вычислений CUDA // для расчёта целевой функции симплекс-метода Debug.Assert(CudafyMatrix.GetMatrix() != null); CudafyMatrix.SetIndexes(Enumerable.Range(0, n).ToArray()); CudafyMatrix.ExecuteMacLane(); macLane = CudafyMatrix.GetMacLane(); #if DEBUG int[][] arrayOfArray = new TwoDimensionalArray <int>(CudafyMatrix.GetMatrix()).ToArrayOfArray(); Debug.WriteLine(string.Join(Environment.NewLine, arrayOfArray.Select(v => string.Join(",", v.Select(i => i.ToString()))))); #endif } catch (Exception ex) { if (WorkerLog != null) { WorkerLog(ex.ToString()); } /////////////////////////////////////////////////// // Вычисление целевой функции обычным методом macLane = booleanMatrix.MacLane; } Debug.WriteLine("macLane = " + macLane); Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); int k = Math.Min(2, Math.Max(1, n)); k = Math.Min(n, k); if (WorkerLog != null) { WorkerLog("Начало симплекс-метода"); } if (WorkerLog != null) { WorkerLog("Текущий macLane = " + macLane); } for (bool updated = true; k <= n && updated && macLane > 0;) { Debug.Assert(booleanMatrix.Length == subGraph.Count()); List <int> values = Enumerable.Range(0, n).ToList(); updated = false; List <int> indexOfIndex = Enumerable.Range(n - k, k).ToList(); while (macLane > 0) { if (WorkerLog != null) { WorkerLog(string.Format("Перебираем индексы в позициях {0}", string.Join(",", indexOfIndex.Select(index => index.ToString(CultureInfo.InvariantCulture))))); } CudafyMatrix.SetMatrix( new ArrayOfArray <int>( booleanMatrix.Select(vector => vector.Select(b => b ? 1 : 0).ToArray()) .ToArray()) .ToTwoDimensional()); List <int> indexes = values.ToList(); foreach (int index in indexOfIndex) { indexes[index] = n - 1; } while (macLane > 0) { Debug.Write(string.Format("Проверяем индексы {0} ... ", string.Join(",", indexes.Select(index => index.ToString(CultureInfo.InvariantCulture))))); // Проверяем, что матрица образованная indexes является обратимой var detMatrix = new BooleanMatrix(indexes); if (detMatrix.Det()) { BooleanMatrix matrix2; int macLane2 = 0; try { ///////////////////////////////////////////////////// // Использование параллельных вычислений CUDA // для расчёта целевой функции симплекс-метода Debug.Assert(CudafyMatrix.GetMatrix() != null); CudafyMatrix.SetIndexes(indexes.ToArray()); CudafyMatrix.ExecuteMacLane(); macLane2 = CudafyMatrix.GetMacLane(); #if DEBUG CudafyMatrix.ExecuteUpdate(); int[][] arrayOfArray = new TwoDimensionalArray <int>(CudafyMatrix.GetMatrix()).ToArrayOfArray(); matrix2 = new BooleanMatrix( arrayOfArray.Select(r => new BooleanVector(r.Select(c => c != 0)))); CudafyMatrix.SetMatrix( new ArrayOfArray <int>( booleanMatrix.Select(v => v.Select(b => b ? 1 : 0).ToArray()) .ToArray()) .ToTwoDimensional()); #endif } catch (Exception ex) { if (WorkerLog != null) { WorkerLog(ex.ToString()); } /////////////////////////////////////////////////// // Вычисление целевой функции обычным методом Dictionary <int, int> dictionary = indexes.Select((item, value) => new KeyValuePair <int, int>(value, item)) .ToDictionary(pair => pair.Key, pair => pair.Value); matrix2 = new BooleanMatrix( dictionary.Select( pair1 => dictionary .Where( pair2 => pair2.Value == pair1.Key && pair2.Key != pair1.Key) .Select(pair => booleanMatrix[pair.Key]) .Aggregate(booleanMatrix[pair1.Key], BooleanVector.Xor))); macLane2 = matrix2.MacLane; } finally { Debug.WriteLine("macLane = " + macLane2); } if (macLane > macLane2) { if (WorkerLog != null) { WorkerLog("Найденое решение улучшилось ( " + macLane + " -> " + macLane2 + " )"); } Debug.WriteLine("macLane: " + macLane + "->" + macLane2); values = indexes.ToList(); macLane = macLane2; updated = true; Debug.WriteLine(string.Join(",", values.Select(item => item.ToString()))); Debug.WriteLine("matrix2:"); Debug.WriteLine(matrix2); } if (macLane == 0) { break; } } else { Debug.WriteLine("Матрица не обратима"); } int i = k; while (i-- > 0) { if (indexes[indexOfIndex[i]]-- > 0) { break; } else { indexes[indexOfIndex[i]] = n - 1; } } if (i < 0) { break; } } int count = k; while (count-- > 0) { if (indexOfIndex[count]-- > (count == 0 ? 0 : (indexOfIndex[count - 1] + 1))) { break; } else { indexOfIndex[count] = (count == (k - 1) ? n - 1 : (indexOfIndex[count + 1] - 1)); } } if (count < 0) { break; } } if (WorkerLog != null) { WorkerLog("Смена начальной точки симплекс-метода"); } try { ///////////////////////////////////////////////////// // Использование параллельных вычислений CUDA // для смены базиса симплекс-метода Debug.Assert(CudafyMatrix.GetMatrix() != null); CudafyMatrix.SetIndexes(values.ToArray()); CudafyMatrix.ExecuteUpdate(); #if DEBUG int[][] arrayOfArray = new TwoDimensionalArray <int>(CudafyMatrix.GetMatrix()).ToArrayOfArray(); booleanMatrix = new BooleanMatrix(arrayOfArray.Select(r => new BooleanVector(r.Select(c => c != 0)))); #endif } catch (Exception ex) { if (WorkerLog != null) { WorkerLog(ex.ToString()); } /////////////////////////////////////////////////// // Cмена базиса симплекс-метода обычным методом Dictionary <int, int> dictionary = values.Select((item, value) => new KeyValuePair <int, int>(value, item)) .ToDictionary(pair => pair.Key, pair => pair.Value); booleanMatrix = new BooleanMatrix( dictionary.Select( pair1 => dictionary .Where(pair2 => pair2.Value == pair1.Key && pair2.Key != pair1.Key) .Select(pair => booleanMatrix[pair.Key]) .Aggregate(booleanMatrix[pair1.Key], BooleanVector.Xor))); } Debug.WriteLine(string.Join(",", values.Select(item => item.ToString()))); Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); } if (macLane > 0) { if (WorkerLog != null) { WorkerLog("Не найдено нулевое значение фунции Мак-Лейна"); } if (WorkerLog != null) { WorkerLog("Граф не планарен"); } bool result = false; if (WorkerComplite != null) { WorkerComplite(result); } return(result); } } } else { // Приведение матрицы к каноническому виду обычным способом booleanMatrix.GaussJordan(); booleanMatrix.RemoveAll(BooleanVector.IsZero); if (WorkerLog != null) { WorkerLog(string.Format("Размер матрицы {0}х{1}", booleanMatrix.Count(), booleanMatrix.Length)); } int macLane = booleanMatrix.MacLane; Debug.WriteLine("macLane = " + macLane); Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); int n = booleanMatrix.Count; int k = Math.Min(2, Math.Max(1, n)); k = Math.Min(n, k); if (WorkerLog != null) { WorkerLog("Начало симплекс-метода"); } if (WorkerLog != null) { WorkerLog("Текущий macLane = " + macLane); } for (bool updated = true; k <= n && updated && macLane > 0;) { Debug.Assert(booleanMatrix.Length == subGraph.Count()); List <int> values = Enumerable.Range(0, n).ToList(); updated = false; List <int> indexOfIndex = Enumerable.Range(n - k, k).ToList(); while (macLane > 0) { if (WorkerLog != null) { WorkerLog(string.Format("Перебираем индексы в позициях {0}", string.Join(",", indexOfIndex.Select(index => index.ToString(CultureInfo.InvariantCulture))))); } CudafyMatrix.SetMatrix( new ArrayOfArray <int>( booleanMatrix.Select(vector => vector.Select(b => b ? 1 : 0).ToArray()) .ToArray()) .ToTwoDimensional()); List <int> indexes = values.ToList(); foreach (int index in indexOfIndex) { indexes[index] = n - 1; } while (macLane > 0) { Debug.Write(string.Format("Проверяем индексы {0} ... ", string.Join(",", indexes.Select(index => index.ToString(CultureInfo.InvariantCulture))))); // Проверяем, что матрица образованная indexes является обратимой var detMatrix = new BooleanMatrix(indexes); if (detMatrix.Det()) { int macLane2 = 0; /////////////////////////////////////////////////// // Вычисление целевой функции обычным методом Dictionary <int, int> dictionary = indexes.Select((item, value) => new KeyValuePair <int, int>(value, item)) .ToDictionary(pair => pair.Key, pair => pair.Value); var matrix2 = new BooleanMatrix( dictionary.Select( pair1 => dictionary .Where( pair2 => pair2.Value == pair1.Key && pair2.Key != pair1.Key) .Select(pair => booleanMatrix[pair.Key]) .Aggregate(booleanMatrix[pair1.Key], BooleanVector.Xor))); macLane2 = matrix2.MacLane; Debug.WriteLine("macLane = " + macLane2); if (macLane > macLane2) { if (WorkerLog != null) { WorkerLog("Найденое решение улучшилось ( " + macLane + " -> " + macLane2 + " )"); } Debug.WriteLine("macLane: " + macLane + "->" + macLane2); values = indexes.ToList(); macLane = macLane2; updated = true; Debug.WriteLine(string.Join(",", values.Select(item => item.ToString()))); Debug.WriteLine("matrix2:"); Debug.WriteLine(matrix2); } if (macLane == 0) { break; } } else { Debug.WriteLine("Матрица не обратима"); } int i = k; while (i-- > 0) { if (indexes[indexOfIndex[i]]-- > 0) { break; } else { indexes[indexOfIndex[i]] = n - 1; } } if (i < 0) { break; } } int count = k; while (count-- > 0) { if (indexOfIndex[count]-- > (count == 0 ? 0 : (indexOfIndex[count - 1] + 1))) { break; } else { indexOfIndex[count] = (count == (k - 1) ? n - 1 : (indexOfIndex[count + 1] - 1)); } } if (count < 0) { break; } } if (WorkerLog != null) { WorkerLog("Смена начальной точки симплекс-метода"); } /////////////////////////////////////////////////// // Cмена базиса симплекс-метода обычным методом Dictionary <int, int> dictionary2 = values.Select((item, value) => new KeyValuePair <int, int>(value, item)) .ToDictionary(pair => pair.Key, pair => pair.Value); booleanMatrix = new BooleanMatrix( dictionary2.Select( pair1 => dictionary2 .Where(pair2 => pair2.Value == pair1.Key && pair2.Key != pair1.Key) .Select(pair => booleanMatrix[pair.Key]) .Aggregate(booleanMatrix[pair1.Key], BooleanVector.Xor))); Debug.WriteLine(string.Join(",", values.Select(item => item.ToString()))); Debug.WriteLine("matrix:"); Debug.WriteLine(booleanMatrix); } if (macLane > 0) { if (WorkerLog != null) { WorkerLog("Не найдено нулевое значение фунции Мак-Лейна"); } if (WorkerLog != null) { WorkerLog("Граф не планарен"); } bool result = false; if (WorkerComplite != null) { WorkerComplite(result); } return(result); } } if (WorkerLog != null) { WorkerLog("Конец проверки связанной компоненты"); } } { if (WorkerLog != null) { WorkerLog("Конец алгоритма Мак-Лейна"); } if (WorkerLog != null) { WorkerLog("Граф планарен"); } bool result = true; if (WorkerComplite != null) { WorkerComplite(result); } return(result); } }