/// <summary> /// Makes node within the tree the root child, reorganizing the nodes and child arrays. /// /// Critically, we do this operation in situ to avoid having to transiently allocate /// extremely large memory objects. /// /// The operation consists of 3 stages: /// - traverse the subtree breadth-first starting at the new root, /// building a bitmap of which nodes are children. /// /// - traverse the node array sequentially, processing each node that is a member of this bitmap by /// moving it into the new position in the store (including modifying associated child and parent references) /// Also we build a table of all the new children that need to be moved. /// /// - using the child table built above, shift all children down. Because nodes may have children written /// out of order, we don't know for sure there is enough space available. Therefore we sort the table /// based on their new position in the table and then shift them down, insuring we don't overwrite /// children yet to be shifted. /// /// Additionally we may have to recreate the transposition roots dictionary because /// the set of roots is smaller (the retined subtree only) and the node indices will change. /// </summary> /// <param name="store"></param> /// <param name="newRootChild"></param> /// <param name="newPriorMoves"></param> /// <param name="transpositionRoots"></param> public static void MakeChildNewRoot(MCTSNodeStore store, float policySoftmax, ref MCTSNodeStruct newRootChild, PositionWithHistory newPriorMoves, PositionEvalCache cacheNonRetainedNodes, TranspositionRootsDict transpositionRoots) { #if DEBUG store.Validate(); #endif COUNT++; // Nothing to do if the requested node is already currently the root if (newRootChild.Index == store.RootNode.Index) { // Nothing changing in the tree, just flush the cache references store.ClearAllCacheIndices(); } else { DoMakeChildNewRoot(store, policySoftmax, ref newRootChild, newPriorMoves, cacheNonRetainedNodes, transpositionRoots); } #if DEBUG store.Validate(); #endif }
internal ChildEnumeratorImplState(MCTSNodeStore store, MCTSNodeStructIndex nodeIndex, int maxIndex) { Store = store; childSpan = store.Children.SpanForNode(nodeIndex); index = UInt32.MaxValue; endIndex = (int)maxIndex; nodes = (MCTSNodeStruct *)Store.Nodes.nodes.RawMemory; }
public MCTSNodeStoreContext(MCTSNodeStore store) { NodeStore = store; PriorContext = curContext; // Update statics curStore = store; curNodes = store.Nodes.nodes; curChildren = store.Children.childIndices; curContext = this; }
/// <summary> /// Constructor. /// </summary> /// <param name="parentStore"></param> /// <param name="maxChildren"></param> public MCTSNodeStructChildStorage(MCTSNodeStore parentStore, long maxChildren) { ParentStore = parentStore; #if SPAN MaxChildren = 1 + maxChildren; childIndices = new MemoryBufferOS <MCTSNodeStructChild>(MaxChildren, MCTSParamsFixed.STORAGE_LARGE_PAGES, SharedMemChildrenName, MCTSParamsFixed.STORAGE_USE_EXISTING_SHARED_MEM, MCTSParamsFixed.STORAGE_USE_INCREMENTAL_ALLOC); #else throw new NotImplementedException("Currently only SPAN mode is supported because otherwise GC may relocate nodes violating assumption"); childIndices = new MCTSNodeStructChild[1 + numChildren]; #endif }
public void Dispose() { if (curStore == null) { throw new Exception("Internal error: MCTSNodeStoreContext improperly nested"); } // Update statics if (PriorContext != null) { curStore = PriorContext.NodeStore; curNodes = PriorContext.NodeStore.Nodes.nodes; curChildren = PriorContext.NodeStore.Children.childIndices; } curContext = PriorContext; }
public ChildEnumeratorImpl(MCTSNodeStore store, MCTSNodeStructIndex nodeIndex, int overrideMaxIndex = int.MaxValue) { Store = store; NodeIndex = nodeIndex; MaxIndex = Math.Min(overrideMaxIndex, Store.Nodes.nodes[nodeIndex.Index].NumChildrenExpanded - 1); }
static void DoMakeChildNewRoot(MCTSNodeStore store, float policySoftmax, ref MCTSNodeStruct newRootChild, PositionWithHistory newPriorMoves, PositionEvalCache cacheNonRetainedNodes, TranspositionRootsDict transpositionRoots) { ChildStartIndexToNodeIndex[] childrenToNodes; uint numNodesUsed; uint numChildrenUsed; BitArray includedNodes; int newRootChildIndex = newRootChild.Index.Index; int newIndexOfNewParent = -1; int nextAvailableNodeIndex = 1; // Traverse this subtree, building a bit array of visited nodes includedNodes = MCTSNodeStructUtils.BitArrayNodesInSubtree(store, ref newRootChild, out numNodesUsed); //using (new TimingBlock("Build position cache ")) if (cacheNonRetainedNodes != null) { long estNumNodes = store.RootNode.N - numNodesUsed; cacheNonRetainedNodes.InitializeWithSize((int)estNumNodes); ExtractPositionCacheNonRetainedNodes(store, policySoftmax, includedNodes, in newRootChild, cacheNonRetainedNodes); } // We will constract a table indicating the starting index and length of // children associated with the nodes we are extracting childrenToNodes = GC.AllocateUninitializedArray <ChildStartIndexToNodeIndex>((int)numNodesUsed); void RewriteNodes() { // TODO: Consider that the above is possibly all we need to do in some case // Suppose the subtree is very large relative to the whole // This approach would be much faster, and orphan an only small part of the storage // Now scan all above nodes. // If they don't belong, ignore. // If they do belong, swap them down to the next available lower location // Note that this can't be parallelized, since we have to do it strictly in order of node index int numRewrittenNodesDone = 0; for (int i = 2; i < store.Nodes.nextFreeIndex; i++) { if (includedNodes.Get(i)) { ref MCTSNodeStruct thisNode = ref store.Nodes.nodes[i]; // Reset any cache entry thisNode.CacheIndex = 0; // Not possible to support transposition linked nodes, // since the root may be in a part of the tree that is not retained // and possibly already overwritten. // We expect them to have already been materialized by the time we reach this point. Debug.Assert(!thisNode.IsTranspositionLinked); Debug.Assert(thisNode.NumNodesTranspositionExtracted == 0); // Remember this location if this is the new parent if (i == newRootChildIndex) { newIndexOfNewParent = nextAvailableNodeIndex; } // Move the actual node MoveNodePosition(store, new MCTSNodeStructIndex(i), new MCTSNodeStructIndex(nextAvailableNodeIndex)); // Reset all transposition information thisNode.NextTranspositionLinked = 0; childrenToNodes[numRewrittenNodesDone] = new ChildStartIndexToNodeIndex(thisNode.childStartBlockIndex, nextAvailableNodeIndex, thisNode.NumPolicyMoves); // Re-insert this into the transpositionRoots (with the updated node index) if (transpositionRoots != null) { transpositionRoots.TryAdd(thisNode.ZobristHash, nextAvailableNodeIndex); } Debug.Assert(thisNode.NumNodesTranspositionExtracted == 0); numRewrittenNodesDone++; nextAvailableNodeIndex++; } } }