private HashSet <BlockSet> ExploreExistingCosetsByNewGenerator(CubeAction newGenerator) { var newStates = new HashSet <BlockSet>(); int walkedCount = 0; var needWalkStates = new HashSet <BlockSet>(OrbitToCoset.Keys); foreach (var startState in needWalkStates) { walkedCount++; var newState = ExploreNewCoset(startState, newGenerator); if (newState != null) { newStates.Add(newState); Console.WriteLine( $"Stablized[{Stablized.Indexes.Count}] " + $"ExploreNewGeneratorOnExistingCosets: foundCount/needWalk/total=" + $"{newStates.Count}/{needWalkStates.Count - walkedCount}/{OrbitToCoset.Count} " + $"newState=[{newState}] newCoset=[{OrbitToCoset[newState].Count()}] " + $"startState=[{startState}] generator=[{newGenerator.Count()}]"); } } return(newStates); }
/// <summary> /// Stablizer chain algorithm templated from https://www.jaapsch.net/puzzles/schreier.htm. /// /// __Basic stablizer chain algorithm__ /// /// Each GStep in the stablizer chain corresponds to gradually more blocks were rotated to /// the ideal cube position. Since the next GStep stablizes more blocks, it's the subgroup /// of the previous GStep. /// /// To obtain the next GStep, we obtain its generators. Subgroup generators can be obtained /// by Schreier subgroup lemma. It needs the set of cosets, i.e. coset representatives, and /// the parent group generators. /// /// Coset representatives can be obtained by repeating permutation of parent group generators. /// We know coset representatives are 1-on-1 mapping to block states we are trying to stablize. /// So we can find whether coset representatives are equal, or whether we walked all of them. /// /// So, giving parent group generators, we can obtain subgroup generators. Recursively, we /// walk along the stablizer chain of GSteps, until we solved all blocks. /// /// __Incremental stablizer chain algorithm__ /// /// The problem is, the count of generators grow exponentially along the stablizer chain. /// We want to know whether a generator is not necessary, i.e. this generator and all descendants /// generators discovered by it, won't help us find any new cosets. /// /// The cosets at each GStep stablizer chain are the final results we want. Because given a cube /// state, we use 1-on-1 mapping to know its coset representative, We use the reverse of the coset /// representative to rotate the cube to ideal position. We do it along the stablizer chain, we /// then solve the cube. /// /// That's why, if a generator discovers no new coset, the generator is not necessary. We can then /// rewrite the algorithm in the new way, to incrementally add generators one by one. If a generator /// is found not necessary, we then discard it, so that it won't further exponentially increase our /// computation overhead. /// </summary> public int AddGeneratorIncrementally(CubeAction newGenerator, List <ProgressInfo> progressInfoList) { if (null == Generators) { Generators = new HashSet <CubeAction>(); } if (null == RejectedGenerators) { RejectedGenerators = new HashSet <CubeAction>(); } if (RejectedGenerators.Contains(newGenerator)) { return(0); } if (PrintProgress) { foreach (var p in progressInfoList) { Console.Write($"{p.StablizedCount}:{p.CompletedWork}/{p.TotalWork} "); } Console.WriteLine(); Console.WriteLine( $"{new string(' ', Stablized.Indexes.Count)}" + $"{Stablized.Indexes.Count} - G:{newGenerator.Count()} " + $"FC:{GeneratorFilter.JumpCount} GC:{Generators.Count} " + $"CC:{(OrbitToCoset != null ? OrbitToCoset.Count : 0)} RJ:{RejectedGenerators.Count}"); } var filteredGenerator = GeneratorFilter.FilterGeneratorIncrementally(newGenerator); if (filteredGenerator != null) { newGenerator = filteredGenerator; } else { RejectedGenerators.Add(newGenerator); return(0); } if (Generators.Contains(newGenerator)) { Utils.DebugAssert(false); return(0); } ProgressInfo progressInfo = null; int foundStateCount = 0; { var newStates = ExploreOrbitToCosetIncrementally(newGenerator); foundStateCount += newStates.Count; var newSubgroupGenerators = ObtainGeneratorsOfStablizerSubgroupIncrementally( newGenerator, newStates); if (Next != null) { progressInfo = new ProgressInfo(Stablized.Indexes.Count, newSubgroupGenerators.Count); progressInfoList.Add(progressInfo); foreach (var subgroupGenerator in newSubgroupGenerators) { foundStateCount += Next.AddGeneratorIncrementally(subgroupGenerator, progressInfoList); progressInfo.CompletedWork++; } } } Utils.DebugAssert(GeneratorFilter.AcceptedGeneratorCount == Generators.Count); Console.WriteLine( $"Stablized[{Stablized.Indexes.Count}] " + $"AddGeneratorIncrementally: Accepted new generator: " + $"foundStateCount={foundStateCount} Generators={Generators.Count} " + $"Cosets={OrbitToCoset.Count} FilterCount={GeneratorFilter.JumpCount} " + $"newGenerator=[{newGenerator.Count()}]"); if (progressInfo != null) { progressInfoList.RemoveAt(progressInfoList.Count - 1); } return(foundStateCount); }