예제 #1
0
        /// <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);
        }
예제 #2
0
        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));
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
                        }
                    }
                }
            }
        }
예제 #5
0
 /// <inheritdoc/>
 public bool TryExecute(RecipeExecutorState stateBefore, out RecipeExecutorState stateAfter)
 {
     return(InternalExecute(stateBefore, out stateAfter, false));
 }
예제 #6
0
 /// <inheritdoc/>
 public RecipeExecutorState Execute(RecipeExecutorState state)
 {
     InternalExecute(state, out var newState, true);
     return(newState);
 }
예제 #7
0
        /// <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);
            }
        }