/// <remarks>Like <see cref="TryExecute(RecipeExecutorState, out RecipeExecutorState)"/> /// but with a boolean parameter for throwing.</remarks> private bool InternalExecute(RecipeExecutorState state, out RecipeExecutorState stateAfter, bool shouldThrow) { int boundaryLength = state.Boundary.Count; if (!boundaryLength.IsMultipleOf(NumPeriods)) { if (shouldThrow) { throw new PotentialRecipeExecutionException($"The length of the boundary ({boundaryLength}) is not a multiple of the number of periods ({NumPeriods})."); } stateAfter = null; return(false); } int periodLength = boundaryLength / NumPeriods; var atomicInstructions = Enumerable.Range(0, NumPeriods).Select(k => new AtomicGlueInstruction(k * periodLength + AtomicInstruction.Index, AtomicInstruction.Count, AtomicInstruction.CycleLength)); var compositeInstruction = new CompositeGlueInstruction(atomicInstructions); if (shouldThrow) { state = compositeInstruction.Execute(state); } else if (!compositeInstruction.TryExecute(state, out state)) { stateAfter = null; return(false); } stateAfter = state; return(true); }
public IEnumerable <QuiverWithPotential <int> > GenerateQPsFromGivenBase( RecipeExecutorState baseState, int numPeriods, int numRounds, int maxCycleLength) { if (baseState == null) { throw new ArgumentNullException(nameof(baseState)); } if (numPeriods <= 0) { throw new ArgumentOutOfRangeException(nameof(numPeriods)); } if (numRounds < 0) { throw new ArgumentOutOfRangeException(nameof(numRounds)); } if (maxCycleLength <= 2) { throw new ArgumentOutOfRangeException(nameof(maxCycleLength)); } // TODO: Might want to validate baseState more carefully here return(DoWork(baseState, numPeriods, numRounds, maxCycleLength)); }
private bool InternalExecute(RecipeExecutorState stateBefore, out RecipeExecutorState stateAfter, bool shouldThrow) { var newBoundary = new CircularList <(Arrow <int>, BoundaryArrowOrientation)>(stateBefore.Boundary); stateAfter = new RecipeExecutorState { Potential = stateBefore.Potential, NextVertex = stateBefore.NextVertex, Boundary = newBoundary, }; return(true); }
private IEnumerable <QuiverWithPotential <int> > DoWork(RecipeExecutorState state, int numPeriods, int numRounds, int maxCycleLength) { if (numRounds == 0) { yield return(new QuiverWithPotential <int>(state.Potential)); yield break; } int numArrowsInPeriod = state.Boundary.Count / numPeriods; foreach (int index in Enumerable.Range(0, numArrowsInPeriod)) { var firstArrowOrientation = state.Boundary[index].Orientation; for (int count = 1; count <= numArrowsInPeriod; count++) { var lastArrowOrientation = state.Boundary[index + count - 1].Orientation; if (firstArrowOrientation != lastArrowOrientation) { break; // The path has arrows of different orientation, so try a new startIndex } for (int cycleLength = Math.Max(3, count + 1); cycleLength <= maxCycleLength; cycleLength++) { var instruction = new PeriodicAtomicGlueInstruction(new AtomicGlueInstruction(index, count, cycleLength), numPeriods); if (!instruction.TryExecute(state, out var newState)) { continue; // Fails only on "problematic arrows"? Also on bad input with GenerateQPsFromGivenBase } foreach (var qp in DoWork(newState, numPeriods, numRounds - 1, maxCycleLength)) { yield return(qp); } } } } }
/// <inheritdoc/> public bool TryExecute(RecipeExecutorState stateBefore, out RecipeExecutorState stateAfter) { return(InternalExecute(stateBefore, out stateAfter, false)); }
/// <inheritdoc/> public RecipeExecutorState Execute(RecipeExecutorState state) { InternalExecute(state, out var newState, true); return(newState); }
/// <remarks>Like <see cref="TryExecute(RecipeExecutorState, out RecipeExecutorState)"/> /// but with a boolean parameter for throwing.</remarks> private bool InternalExecute(RecipeExecutorState state, out RecipeExecutorState stateAfter, bool shouldThrow) { var oldPathTuples = state.Boundary.GetRange(Index, Count); var oldPathArrows = oldPathTuples.Select(x => x.Arrow); var oldPathOrientations = oldPathTuples.Select(x => x.Orientation); if (!oldPathOrientations.AllAreEqual()) { if (shouldThrow) { throw new PotentialRecipeExecutionException("The path in the boundary has arrows of different orientation."); } stateAfter = null; return(false); } var oldPathOrientation = oldPathTuples.First().Orientation; if (oldPathOrientation == BoundaryArrowOrientation.Left) { oldPathArrows = oldPathArrows.Reverse(); } var oldPath = new Path <int>(oldPathArrows); var newPathLength = CycleLength - Count; var newPath = Utility.MakePath(oldPath.EndingPoint, oldPath.StartingPoint, newPathLength, state.NextVertex); if (newPathLength == 1) { var newArrow = newPath.Arrows.Single(); // Bad cancellation if (state.PotentiallyProblematicArrows.Contains(newArrow)) { if (shouldThrow) { throw new PotentialRecipeExecutionException("The arrow of the singleton new path is already present in the potential."); } stateAfter = null; return(false); } // 2-cycle (which is bad) if (state.PotentiallyProblematicArrows.Contains(new Arrow <int>(newArrow.Target, newArrow.Source))) { if (shouldThrow) { throw new PotentialRecipeExecutionException("The anti-parallel of the arrow of the singleton new path is present in the potential."); } stateAfter = null; return(false); } } var newPathOrientation = oldPathOrientation.Reverse(); IEnumerable <Arrow <int> > newPathArrows = newPath.Arrows; if (newPathOrientation == BoundaryArrowOrientation.Left) { newPathArrows = newPathArrows.Reverse(); } var newPathTuples = newPathArrows.Select(a => (a, newPathOrientation)); var newCycle = new SimpleCycle <int>(oldPath.AppendPath(newPath)); int coefficient = newPathOrientation == BoundaryArrowOrientation.Right ? +1 : -1; var newBoundary = new CircularList <(Arrow <int>, BoundaryArrowOrientation)>(state.Boundary); newBoundary.ReplaceRange(Index, Count, newPathTuples); var newPotentiallyProblematicArrows = new HashSet <Arrow <int> >(state.PotentiallyProblematicArrows); if (Count == 1) { newPotentiallyProblematicArrows.Add(oldPath.Arrows.Single()); } stateAfter = new RecipeExecutorState { Potential = state.Potential.AddCycle(newCycle, coefficient), NextVertex = state.NextVertex + (newPath.LengthInVertices - 2), Boundary = newBoundary, PotentiallyProblematicArrows = newPotentiallyProblematicArrows }; return(true); }
private bool InternalExecute(RecipeExecutorState stateBefore, out RecipeExecutorState stateAfter, bool shouldThrow) { stateAfter = null; // Normalize the index of every instruction for the current boundary var instructions = atomicInstructions.Select(ai => new AtomicGlueInstruction(ai.Index.Modulo(stateBefore.Boundary.Count), ai.Count, ai.CycleLength)).ToList(); instructions.Sort((i1, i2) => { int cmp = i1.Index.CompareTo(i2.Index); if (cmp != 0) { return(cmp); } else { return(i1.Count.CompareTo(i2.Count)); } }); if (!InstructionsHaveDisjointSupport()) { if (shouldThrow) { throw new PotentialRecipeExecutionException("The atomic instructions do not have disjoint support."); } else { return(false); } } // Execute the instructions from last to first in order to mostly avoid having to update the indices of the instructions instructions.Reverse(); // If the support of last instruction wraps around, its execution will affect the indices of the previous instructions // This can be solved either (1) by shifting the indices of all the previous instructions accordingly or // (2) by rotating the boundary and shifting the indices of *all* instructions accordingly (including the last) // (not that no inverse rotation is necessary). // The former approach is used below if (instructions.Count > 0) { var lastInstruction = instructions.First(); // Last instruction with the original order ("increasing") int numWrappedAroundArrows = Math.Max(0, (lastInstruction.Index + lastInstruction.Count) - stateBefore.Boundary.Count); if (numWrappedAroundArrows > 0) { instructions = instructions.Select((instr, index) => index == 0 ? instr : new AtomicGlueInstruction(instr.Index - numWrappedAroundArrows, instr.Count, instr.CycleLength)).ToList(); } } var state = stateBefore; // Might be better to sort the list descendingly from the beginning, but reversing it here is easier foreach (var instruction in instructions) { if (shouldThrow) { state = instruction.Execute(state); } else if (!instruction.TryExecute(state, out state)) { return(false); } } stateAfter = state; return(true); bool InstructionsHaveDisjointSupport() { int previousInstructionEnd = 0; // exclusive foreach (var instruction in instructions) { if (instruction.Index < previousInstructionEnd) { return(false); } previousInstructionEnd = instruction.Index + instruction.Count; } return(true); } }