/// <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
        }
Example #2
0
 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;
 }
Example #3
0
        public MCTSNodeStoreContext(MCTSNodeStore store)
        {
            NodeStore    = store;
            PriorContext = curContext;

            // Update statics
            curStore    = store;
            curNodes    = store.Nodes.nodes;
            curChildren = store.Children.childIndices;
            curContext  = this;
        }
Example #4
0
        /// <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
        }
Example #5
0
        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;
        }
Example #6
0
 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++;
                    }
                }
            }