/// <summary> /// Checks to see if <paramref name="currentSymbolInTarget"/> matches the symbol at <paramref name="currentIndexInMatch"/> in the <paramref name="seriesMatch"/> string. /// And also if the parent at <paramref name="currentParentIndexInTarget"/> maps to the parent of <paramref name="currentIndexInMatch"/> /// </summary> /// <param name="seriesMatch"></param> /// <param name="targetIndexesToMatchIndexes"></param> /// <param name="currentParentIndexInTarget"></param> /// <param name="currentSymbolInTarget"></param> /// <param name="currentIndexInMatch"></param> /// <returns></returns> private bool TargetSymbolMatchesAndParentMatches( SymbolSeriesSuffixMatcher seriesMatch, NativeHashMap <int, int> targetIndexesToMatchIndexes, int currentParentIndexInTarget, int currentIndexInTarget, int currentIndexInMatch, SymbolString <float> symbolString ) { var symbolInMatch = nativeRuleData.suffixMatcherGraphNodeData[currentIndexInMatch + seriesMatch.graphNodeMemSpace.index].mySymbol; if ( symbolInMatch.targetSymbol == symbolString[currentIndexInTarget] && symbolInMatch.parameterLength == symbolString.ParameterSize(currentIndexInTarget)) { var parentIndexInMatch = nativeRuleData.suffixMatcherGraphNodeData[currentIndexInMatch + seriesMatch.graphNodeMemSpace.index].parentIndex; if (parentIndexInMatch == -1) { // if the parent is the origin, always match. return(true); } var parentIndexInTarget = currentParentIndexInTarget; var indexInMatchOfTargetParentMapped = targetIndexesToMatchIndexes[parentIndexInTarget]; // check to ensure the parent of this node in the target graph // is already mapped to the parent of the node in the if (parentIndexInMatch == indexInMatchOfTargetParentMapped) { return(true); } } return(false); }
/// <summary> /// partial, semi-ordered tree matching algorithm. /// /// iterate over target string, no backtracking /// maintain data about matches in series match: /// indexes which are valid branched matches for the current index in target /// record of which leaves in the matcher have been matched /// </summary> /// <param name="indexInSymbolTarget"></param> /// <param name="seriesMatch"></param> /// <returns>a mapping from all symbols in seriesMatch back into the target string</returns> public bool MatchesForward( NativeMultipleHashSets.HashSetSlice includeSymbolsSet, int indexInSymbolTarget, SymbolSeriesSuffixMatcher seriesMatch, SymbolString <float> symbolString, int firstParameterCopyIndex, NativeArray <float> parameterCopyMemory, out byte paramsCopiedToMem, TmpNativeStack <BranchEventData> helperStack ) { if (!seriesMatch.HasGraphIndexes) { // this should be done in the parsing/compiling phase. should only have to happen once for the whole system, per matching rule. throw new System.Exception("graph indexes should be precomputed"); //seriesMatch.ComputeGraphIndexes(branchOpenSymbol, branchCloseSymbol); } // keep count of how many more matches are required at each level of the tree. // starts out as a copy of the child count array. each leaf will be at 0, and will go negative when matched. //var remainingMatchesAtIndexes = seriesMatch.childrenCounts.Clone() as int[]; return(MatchesForwardsAtIndexOrderingInvariant( includeSymbolsSet, indexInSymbolTarget, seriesMatch, symbolString, firstParameterCopyIndex, parameterCopyMemory, out paramsCopiedToMem, helperStack)); }
public SymbolSeriesSuffixMatcher BuildIntoManagedMemory( SystemLevelRuleNativeData nativeData, SymbolSeriesMatcherNativeDataWriter dataWriter, Allocator allocator = Allocator.Persistent) { var matcher = new SymbolSeriesSuffixMatcher(); matcher.graphNodeMemSpace = new JaggedIndexing { index = dataWriter.indexInSuffixNodes, length = (ushort)RequiredGraphNodeMemSpace }; dataWriter.indexInSuffixNodes += RequiredGraphNodeMemSpace; for (int i = 0; i < nodes.Count; i++) { var sourceNode = nodes[i]; nativeData.suffixMatcherGraphNodeData[i + matcher.graphNodeMemSpace.index] = new SymbolMatcherGraphNode { parentIndex = sourceNode.parentIndex, myIndexInParentChildren = sourceNode.myIndexInParentChildren, mySymbol = targetSymbolSeries[i].AsBlittable() }; } matcher.childrenOfRoot = new JaggedIndexing { index = dataWriter.indexInSuffixChildren, length = (ushort)rootChildren.Count }; var childrenAsArray = nodes.Select(x => x.childrenIndexes.ToArray()).ToArray(); var tmpIndexInChildren = 0; foreach (var rootChild in rootChildren) { nativeData.suffixMatcherChildrenDataArray[tmpIndexInChildren + dataWriter.indexInSuffixChildren] = rootChild; tmpIndexInChildren++; } JaggedNativeArray <int> .WriteJaggedIndexing( (indexInJagged, jaggedIndexing) => { var node = nativeData.suffixMatcherGraphNodeData[indexInJagged + matcher.graphNodeMemSpace.index]; node.childrenIndexing = jaggedIndexing; nativeData.suffixMatcherGraphNodeData[indexInJagged + matcher.graphNodeMemSpace.index] = node; }, childrenAsArray, nativeData.suffixMatcherChildrenDataArray, dataWriter.indexInSuffixChildren + tmpIndexInChildren ); dataWriter.indexInSuffixChildren += RequiredChildrenMemSpace; matcher.HasGraphIndexes = true; matcher.IsCreated = true; return(matcher); }
public DepthFirstSearchState( SymbolSeriesSuffixMatcher source, int currentIndex, SystemLevelRuleNativeData nativeDataPointer) { this.source = source; this.currentIndex = currentIndex; nativeData = nativeDataPointer; }
/// <summary> /// check for a match, enforcing the same ordering in the target match as defined in the matching pattern. /// </summary> /// <param name="originIndexInTarget"></param> /// <param name="seriesMatch"></param> /// <param name="consumedTargetIndexes"></param> /// <returns></returns> private bool MatchesForwardsAtIndexOrderingInvariant( NativeMultipleHashSets.HashSetSlice includeSymbolSet, int originIndexInTarget, SymbolSeriesSuffixMatcher seriesMatch, SymbolString <float> symbolString, int firstParameterCopyIndex, NativeArray <float> parameterCopyMemory, out byte paramsCopiedToMem, TmpNativeStack <BranchEventData> helperStack ) { helperStack.Reset(); var targetParentIndexStack = helperStack;// new TmpNativeStack<BranchEventData>(5);// new Stack<BranchEventData>(); int currentParentIndexInTarget = originIndexInTarget; var targetIndexesToMatchIndexes = new NativeHashMap <int, int>(seriesMatch.graphNodeMemSpace.length, Allocator.Temp);// new Dictionary<int, int>(); paramsCopiedToMem = 0; var indexInMatchDFSState = seriesMatch.GetImmutableDepthFirstIterationState(nativeRuleData); targetIndexesToMatchIndexes.Add(originIndexInTarget, indexInMatchDFSState.currentIndex); if (!indexInMatchDFSState.Next(out indexInMatchDFSState)) { return(true); // if the match is empty, automatically matches. //return targetIndexesToMatchIndexes; } for (int indexInTarget = originIndexInTarget + 1; indexInTarget < symbolString.Length; indexInTarget++) { var targetSymbol = symbolString[indexInTarget]; if (!includeSymbolSet.Contains(targetSymbol)) { continue; } if (targetSymbol == branchOpenSymbol) { targetParentIndexStack.Push(new BranchEventData { currentParentIndex = currentParentIndexInTarget, openBranchSymbolIndex = indexInTarget, paramsCopiedAtThisPoint = paramsCopiedToMem }); } else if (targetSymbol == branchCloseSymbol) { // will encounter a close symbol in one of two cases: // 1. the branch in target has exactly matched the branch in the matcher, and we should just step down // 2. the branch in target has terminated early, meaning we must step down the branch chain and also // reverse the matcher DFS back to a common ancenstor if (targetParentIndexStack.Count <= 0) { // if we encounter the end of the branch which contains the origin index before full match, fail. return(false); } var lastBranch = targetParentIndexStack.Pop(); currentParentIndexInTarget = lastBranch.currentParentIndex; //paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; var parentInMatch = targetIndexesToMatchIndexes[currentParentIndexInTarget]; var parentOfSearchState = indexInMatchDFSState.GetParentIndex(); if (parentInMatch != parentOfSearchState) { // if the parents dont match, that means that the algo will be stepping backwards to the last branch sybmol. paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; } } else { // reverse the DFS in matcher, back to the last point which shares a parent with the current parent // this acts to ensure the entry to the match has a shared parent, if at all possible. // the reversal is necessary when a branching structure failed to match in the last step var parentInMatch = targetIndexesToMatchIndexes[currentParentIndexInTarget]; if (indexInMatchDFSState.FindPreviousWithParent(out var reversedMatchIndex, parentInMatch)) { indexInMatchDFSState = reversedMatchIndex; } var indexInMatch = indexInMatchDFSState.currentIndex; var currentTargetMatchesMatcher = TargetSymbolMatchesAndParentMatches( seriesMatch, targetIndexesToMatchIndexes, currentParentIndexInTarget, indexInTarget, indexInMatch, symbolString); if (currentTargetMatchesMatcher) { targetIndexesToMatchIndexes.Add(indexInTarget, indexInMatch); var paramsToCopy = symbolString.parameters[indexInTarget]; for (int paramIndex = 0; paramIndex < paramsToCopy.length; paramIndex++) { parameterCopyMemory[firstParameterCopyIndex + paramsCopiedToMem] = symbolString.parameters[paramsToCopy, paramIndex]; paramsCopiedToMem++; } currentParentIndexInTarget = indexInTarget; // series continuation includes implicit parenting if (!indexInMatchDFSState.Next(out indexInMatchDFSState)) { return(true); } } else { // symbol in target isn't a valid match, so no further symbols in the current target branching structure can match. // rewind back to the previous branching symbol, and skip this whole structure. // Or if we're not in a nested structure, fail. if (targetParentIndexStack.Count <= 0) { return(false); } var lastBranch = targetParentIndexStack.Pop(); currentParentIndexInTarget = lastBranch.currentParentIndex; //paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; indexInTarget = FindClosingBranchIndexReadonly(lastBranch.openBranchSymbolIndex); var parentInMatch1 = targetIndexesToMatchIndexes[currentParentIndexInTarget]; var parentOfSearchState = indexInMatchDFSState.GetParentIndex(); if (parentInMatch1 != parentOfSearchState) { // if the parents dont match, that means that the algo will be stepping backwards to the last branch sybmol on the next update. paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; } } } } return(false); }