public void Do() { var importer = new QuiverInPlaneFromMutationAppImporter(); var quiverInPlaneAfterAction = importer.ImportQuiverInPlane(path); // Copying the quiver is important, because actions done on this very object might be // undone on an equal but not reference-equal object. Consider the following example: /* * 1. Import (the action as a quiverInPlaneAfterAction) * 2. Actions * 3. Load/import (which stores a copy of quiverInPlane as quiverInPlaneBeforeAction) * 4. Undo load/import (line 3) * quiverInPlane is now not reference-equal to quiverInPlaneAfterAction on line 1; it is reference-equal to the copy made on line 3 * 5. Undo actions (line 2) * This step did not modify quiverInPlaneAfterAction on line 1 but rather the copy made on line 3 * 6. Undo import * 7. Redo import * This loads quiverInPlaneAfterAction, which is supposed to be equal to the quiver after line 1 but is equal to the quiver after line 2 ! * 8. Redo actions * This fails (e.g., if the action on line 2 was adding a vertex, then the vertex would already exist) */ this.quiverInPlaneAfterAction = quiverInPlaneAfterAction.Copy(); model.JustLoadQuiver(quiverInPlaneAfterAction); }
public void TryExtractQP_DoesNotThrow_WhenEncounteringFaceMoreThanOnce() { var vertices = new int[] { 1, 2, 3, 4, 5 }; var arrows = new Arrow <int>[] { new Arrow <int>(1, 2), new Arrow <int>(2, 3), new Arrow <int>(3, 1), new Arrow <int>(1, 4), new Arrow <int>(4, 5), new Arrow <int>(5, 1), new Arrow <int>(2, 5) }; var vertexPositions = new Dictionary <int, Point> { { 1, new Point(0, 0) }, { 2, new Point(1, -1) }, { 3, new Point(-1, -1) }, { 4, new Point(-1, 1) }, { 5, new Point(1, 1) }, }; var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositions); var extractor = CreateQPExtractor(); Assert.That(() => extractor.TryExtractQP(quiverInPlane, out _), Throws.Nothing); }
public void Analyze_WorksOnClockwiseCycle(int cycleLength) { const double Radius = 1000; var vertices = Enumerable.Range(0, cycleLength).ToList(); var arrows = vertices.Select(k => new Arrow <int>(k, (k + 1).Modulo(cycleLength))); double baseAngle = 2 * Math.PI / cycleLength; var vertexPositions = vertices.ToDictionary(k => k, k => new Point((int)(Radius * Math.Cos(-k * baseAngle)), (int)Math.Round(Radius * Math.Sin(-k * baseAngle)))); var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositions); var(analyzer, settings) = CreateAnalyzerWithSettings(); var results = analyzer.Analyze(quiverInPlane, settings); Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.Success)); Assert.That(results.MainResults.IndicatesSelfInjectivity()); var expectedMaximalPathRepresentatives = vertices.ToDictionary( k => k, k => new Path <int>[] { new Path <int>(Enumerable.Range(k, vertices.Count - 1).Select(l => l.Modulo(vertices.Count))) }); Assert.That(results.MaximalPathRepresentatives.ToList(), Is.EqualTo(expectedMaximalPathRepresentatives)); var expectedNakayamaPermutation = vertices.ToDictionary(k => k, k => (k - 2).Modulo(vertices.Count)); Assert.That(results.NakayamaPermutation.UnderlyingDictionary, Is.EqualTo(expectedNakayamaPermutation)); }
/// <summary> /// Exports the specified quiver to the file with the specified path. /// </summary> /// <param name="path">The path of the file to which to export the quiver.</param> /// <param name="quiverInPlane">The quiver to export.</param> /// <remarks> /// <para>The labels of the vertices are not stored explicitly in the Mutation App file /// format, so the labels are in general not preserved.</para> /// </remarks> /// <exception cref="ExporterException">The file was not exported successfully.</exception> public void ExportQuiverInPlane(string path, QuiverInPlane <int> quiverInPlane) { if (path is null) { throw new ArgumentNullException(nameof(path)); } if (quiverInPlane is null) { throw new ArgumentNullException(nameof(quiverInPlane)); } string data = GetMutationAppStringForQuiverInPlane(quiverInPlane); string errorMessage = "Failed to export quiver to file."; try { File.WriteAllText(path, data); } catch (ArgumentNullException ex) { throw new ExporterException(errorMessage, ex); } catch (ArgumentException ex) { throw new ExporterException(errorMessage, ex); } catch (PathTooLongException ex) { throw new ExporterException(errorMessage, ex); } catch (DirectoryNotFoundException ex) { throw new ExporterException(errorMessage, ex); } catch (IOException ex) { throw new ExporterException(errorMessage, ex); } catch (UnauthorizedAccessException ex) { throw new ExporterException(errorMessage, ex); } catch (NotSupportedException ex) { throw new ExporterException(errorMessage, ex); } catch (System.Security.SecurityException ex) { throw new ExporterException(errorMessage, ex); } }
/// <summary> /// Analyzes a <see cref="QuiverInPlane{TVertex}"/>. /// </summary> /// <typeparam name="TVertex">The type of the vertices in the quiver.</typeparam> /// <param name="quiverInPlane">The quiver in plane to analyze.</param> /// <returns>The analysis results.</returns> /// <remarks> /// <para>If the analysis is unsuccessful, the value of the <c>MainResult</c> property /// of the returned analysis results does not have the /// <see cref="QuiverInPlaneAnalysisMainResults.Success"/> or the /// <see cref="QuiverInPlaneAnalysisMainResults.QPIsSelfInjective"/> flags set and has at least /// one of the other flags (each of which indicates some sort of failure) set. However, in /// the case of multiple causes for failure (e.g., the quiver has loops and anti-parallel /// arrows), all the corresponding flags are not necessarily set (e.g., /// <see cref="QuiverInPlaneAnalysisMainResults.QuiverHasLoops"/> is set but /// <see cref="QuiverInPlaneAnalysisMainResults.QuiverHasAntiParallelArrows"/> is not set, /// or <see cref="QuiverInPlaneAnalysisMainResults.QuiverHasAntiParallelArrows"/> is set but /// <see cref="QuiverInPlaneAnalysisMainResults.QuiverHasLoops"/> is not set).</para> /// <para>This method does not throw any exceptions (unless I've forgotten something).</para> /// </remarks> public IQuiverInPlaneAnalysisResults <TVertex> Analyze <TVertex>( QuiverInPlane <TVertex> quiverInPlane, QuiverInPlaneAnalysisSettings settings) where TVertex : IEquatable <TVertex>, IComparable <TVertex> { if (quiverInPlane is null) { throw new ArgumentNullException(nameof(quiverInPlane)); } var qpExtractor = new QPExtractor(); var extractionResult = qpExtractor.TryExtractQP(quiverInPlane, out var qp); if (extractionResult != QPExtractionResult.Success) { return(AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults <TVertex>(extractionResult)); } var analyzer = new QPAnalyzer(); var qpAnalyzerSettings = AnalysisSettingsFactory.CreateQPAnalysisSettings(settings); var qpAnalysisResults = analyzer.Analyze(qp, qpAnalyzerSettings); var analysisResults = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults); return(analysisResults); }
public void Deconstruct( out LayerType layerType, out IEnumerable <Composition> compositions, out QuiverInPlane <int> quiverInPlane, out QuiverWithPotential <int> qp) { layerType = LayerType; compositions = Compositions; quiverInPlane = QuiverInPlane; qp = QP; }
/// <summary> /// Initializes a new instance of the <see cref="InteractiveLayeredQuiverGeneratorOutput"/> /// class. /// </summary> /// <param name="layerType">The layer type of the layered quiver.</param> /// <param name="compositions">The compositions that specify the arrows of the layered quiver.</param> /// <param name="quiverInPlane">A <see cref="QuiverInPlane{TVertex}"/> representing the /// layered quiver.</param> /// <param name="qp">A <see cref="QuiverWithPotential{TVertex}"/> representing the layered /// quiver.</param> public InteractiveLayeredQuiverGeneratorOutput( LayerType layerType, IEnumerable <Composition> compositions, QuiverInPlane <int> quiverInPlane, QuiverWithPotential <int> qp) { LayerType = layerType ?? throw new ArgumentNullException(nameof(layerType)); Compositions = compositions ?? throw new ArgumentNullException(nameof(compositions)); QuiverInPlane = quiverInPlane ?? throw new ArgumentNullException(nameof(quiverInPlane)); QP = qp ?? throw new ArgumentNullException(nameof(qp)); }
/// <summary> /// Initializes a new instance of the <see cref="LoadPredefinedQuiverAction"/> class. /// </summary> /// <param name="model">The quiver-editor model.</param> /// <param name="predefinedQuiver">The type of predefined quiver to load.</param> /// <param name="quiverParameter">The parameter for the predefined quiver to load.</param> /// <param name="quiverInPlaneBeforeAction">The quiver in plane before the action.</param> /// <remarks> /// <para>This constructor takes care of copying /// <paramref name="quiverInPlaneBeforeAction"/> to ensure that the quiver in plane before /// the action that is stored in this <see cref="LoadPredefinedQuiverAction"/> is not /// modified.</para> /// </remarks> public LoadPredefinedQuiverAction(QuiverEditorModel model, PredefinedQuiver predefinedQuiver, dynamic quiverParameter, QuiverInPlane <int> quiverInPlaneBeforeAction) { this.model = model ?? throw new ArgumentNullException(nameof(model)); if (!predefinedQuiver.IsInEnum()) { throw new ArgumentOutOfRangeException(nameof(predefinedQuiver)); } this.predefinedQuiver = predefinedQuiver; this.quiverParameter = quiverParameter; this.quiverInPlaneBeforeAction = quiverInPlaneBeforeAction?.Copy() ?? throw new ArgumentNullException(nameof(quiverInPlaneBeforeAction)); }
public void TryExtractQP_ReturnsQuiverHasFaceWithInconsistentOrientation_For4CycleWithMiddleArrow() { var vertices = new int[] { 1, 2, 3, 4 }; var arrows = vertices.Select(k => new Arrow <int>(k, (k % 4) + 1)); arrows = arrows.AppendElement(new Arrow <int>(2, 4)); var vertexPositions = vertices.ToDictionary(k => k, k => new Point( (int)(1000 * Math.Cos(k * 2 * Math.PI / vertices.Count())), (int)(1000 * Math.Sin(k * 2 * Math.PI / vertices.Count())))); var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositions); var extractor = CreateQPExtractor(); Assert.That(extractor.TryExtractQP(quiverInPlane, out _), Is.EqualTo(QPExtractionResult.QuiverHasFaceWithInconsistentOrientation)); }
private QuiverInPlane <int> GetQuiverInPlaneFromParsedData(int numVertices, bool[,] adjacencyMatrix, Point[] vertexPositions) { const int FirstVertex = 1; var vertices = Enumerable.Range(FirstVertex, numVertices); var arrows = adjacencyMatrix.Select((val, indices) => val ? new Arrow <int>(indices.Item1 + FirstVertex, indices.Item2 + FirstVertex) : null) .Where(val => val != null); var vertexPositionDict = vertexPositions.Select((point, i) => new KeyValuePair <int, Point>(i + FirstVertex, point)) .ToDictionary(p => p.Key, p => p.Value); var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositionDict); return(quiverInPlane); }
public void TryExtractQP_WorksOnClockwiseCycle(int cycleLength) { const double Radius = 1000; var vertices = Enumerable.Range(1, cycleLength); var arrows = vertices.Select(k => new Arrow <int>(k, k.Modulo(cycleLength) + 1)); double baseAngle = 2 * Math.PI / cycleLength; var vertexPositions = vertices.ToDictionary(k => k, k => new Point((int)(Radius * Math.Cos(-k * baseAngle)), (int)Math.Round(Radius * Math.Sin(-k * baseAngle)))); var quiverInPlane = new QuiverInPlane <int>(vertices, arrows, vertexPositions); var extractor = CreateQPExtractor(); var result = extractor.TryExtractQP(quiverInPlane, out var qp); var expectedQuiver = new Quiver <int>(vertices, arrows); var expectedPotential = new Potential <int>(new DetachedCycle <int>(vertices.AppendElement(1)), +1); var expectedQP = new QuiverWithPotential <int>(expectedQuiver, expectedPotential); Assert.That(result, Is.EqualTo(QPExtractionResult.Success)); Assert.That(qp, Is.EqualTo(expectedQP)); }
public IEnumerable <InteractiveLayeredQuiverGeneratorOutput> GenerateFromBaseForFixedLayerType( QuiverInPlane <int> quiverInPlane, Potential <int> potential, IEnumerable <int> boundaryLayer, LayerType layerType, ICompositionGenerator compositionGenerator, int nextVertex) { if (quiverInPlane is null) { throw new ArgumentNullException(nameof(quiverInPlane)); } if (potential is null) { throw new ArgumentNullException(nameof(potential)); } if (boundaryLayer is null) { throw new ArgumentNullException(nameof(boundaryLayer)); } if (layerType is null) { throw new ArgumentNullException(nameof(layerType)); } if (compositionGenerator is null) { throw new ArgumentNullException(nameof(compositionGenerator)); } var interactiveGenerator = new InteractiveLayeredQuiverGenerator(); if (!interactiveGenerator.TryStartGenerationFromBase(quiverInPlane, potential, boundaryLayer, layerType, nextVertex, out var nextCompositionParameters)) { return(new InteractiveLayeredQuiverGeneratorOutput[0]); } return(DoWork(interactiveGenerator, nextCompositionParameters, compositionGenerator)); }
/// <summary> /// Initializes a new instance of the <see cref="ImportQuiverFromMutationAppFileAction"/> /// class. /// </summary> /// <param name="model">The quiver-editor model.</param> /// <param name="path">The path of the file from which to import the quiver.</param> /// <param name="quiverInPlaneBeforeAction">The quiver in plane before the action.</param> /// <para>This constructor takes care of copying /// <paramref name="quiverInPlaneBeforeAction"/> to ensure that the quiver in plane before /// the action that is stored in this <see cref="ImportQuiverFromMutationAppFileAction"/> /// is not modified.</para> public ImportQuiverFromMutationAppFileAction(QuiverEditorModel model, string path, QuiverInPlane <int> quiverInPlaneBeforeAction) { this.model = model ?? throw new ArgumentNullException(nameof(model)); this.path = path ?? throw new ArgumentNullException(nameof(path)); this.quiverInPlaneBeforeAction = quiverInPlaneBeforeAction?.Copy() ?? throw new ArgumentNullException(nameof(quiverInPlaneBeforeAction)); }
/// <summary> /// Gets the string contents of a Mutation App file corresponding to the specified quiver /// in plane. /// </summary> /// <param name="quiverInPlane"></param> /// <returns></returns> private string GetMutationAppStringForQuiverInPlane(QuiverInPlane <int> quiverInPlane) { var builder = new StringBuilder(); var vertices = quiverInPlane.Vertices.Sorted().ToList(); builder.AppendLine("//Number of points"); builder.AppendLine($"{vertices.Count}"); builder.AppendLine("//Vertex radius"); const int VertexRadius = 9; builder.AppendLine($"{VertexRadius}"); builder.AppendLine("//Labels shown"); const bool LabelsShown = true; builder.AppendLine($"{(LabelsShown ? 1 : 0)}"); builder.AppendLine("//Matrix"); builder.AppendLine($"{vertices.Count} {vertices.Count}"); foreach (var sourceVertex in vertices.Sorted()) { var adjacencyValues = vertices.Sorted().Select(targetVertex => GetAdjacencyValue(sourceVertex, targetVertex)); var adjacencyValuesString = String.Join(" ", adjacencyValues); builder.AppendLine(adjacencyValuesString); } int GetAdjacencyValue(int source, int target) { if (quiverInPlane.AdjacencyLists[source].Contains(target)) { return(1); } else if (quiverInPlane.AdjacencyLists[target].Contains(source)) { return(-1); } else { return(0); } } builder.AppendLine("//Growth factor"); const double GrowthFactor = 0.2; builder.AppendLine(Invariant($"{GrowthFactor}")); builder.AppendLine("//Arrow label size"); const double ArrowLabelSize = 12.0; builder.AppendLine(Invariant($"{ArrowLabelSize:F1}")); builder.AppendLine("//Traffic lights"); const bool TrafficLights = false; builder.AppendLine($"{(TrafficLights ? 1 : 0)}"); builder.AppendLine("//Points"); foreach (var vertex in vertices.Sorted()) { const int ThisVertexRadius = 9; double x = quiverInPlane.GetVertexPosition(vertex).X; double y = quiverInPlane.GetVertexPosition(vertex).Y; const bool Frozen = false; builder.AppendLine(Invariant($"{ThisVertexRadius} {x:F1} {y:F1} {(Frozen ? 1 : 0)}")); } builder.AppendLine("//Historycounter"); const int HistoryCounter = -1; builder.AppendLine($"{HistoryCounter}"); builder.AppendLine("//History"); var history = new int[] { }; var historyString = String.Join(" ", history); builder.AppendLine(historyString); builder.AppendLine("//Cluster is null"); return(builder.ToString()); }
public QuiverLoadedEventArgs(QuiverInPlane <int> quiverInPlane) { QuiverInPlane = quiverInPlane ?? throw new ArgumentNullException(nameof(quiverInPlane)); }