private double getRank(NodeReference answerCandidate, MultiTraceLog2 pattern)
        {
            var commonTraceNodes = getCommonTraceNodes(answerCandidate, pattern);
            var rank             = 0.0;
            var initialNodeCount = pattern.InitialNodes.Count();

            foreach (var traceNode in commonTraceNodes)
            {
                var compatibleNodes = traceNode.CompatibleInitialNodes.ToArray();
                var edgeImportance  = 1.0 * compatibleNodes.Length / initialNodeCount;
                rank += edgeImportance;
            }

            //TODO here could be more precise formula
            return(rank / commonTraceNodes.Count());
        }
        private IEnumerable <PathSubstitution> getSubstitutedPaths(MultiTraceLog2 pattern, NodeReference substitutionNode, ComposedGraph graph)
        {
            var substitution = new SubstitutionValidator(substitutionNode, graph);

            var filteredNodes = getFilteredTraces(pattern);

            foreach (var traceNode in filteredNodes.Reverse())
            {
                if (traceNode.CurrentEdge == null)
                {
                    //we skip substitution to answer
                    continue;
                }

                if (substitution.IsCompatible(traceNode))
                {
                    yield return(new PathSubstitution(substitutionNode, traceNode));
                }
            }
        }
        private IEnumerable <TraceNode2> getFilteredTraces(MultiTraceLog2 pattern)
        {
            TraceNode2[] result;
            if (!_cachedFilteredTraces.TryGetValue(pattern, out result))
            {
                var start = DateTime.Now;

                result = pattern.TraceNodes.Where(n =>
                {
                    //TODO this is workaround how to filter out irrelevant paths
                    var pathLen         = n.Path.Count();
                    var distinctPathLen = n.Path.Distinct().Count();

                    return(pathLen == distinctPathLen);
                }).OrderByDescending(t => t.CompatibleInitialNodes.Count()).Take(100).ToArray();

                _cachedFilteredTraces[pattern] = result;
                // Console.WriteLine("GetFilteredTraces {0}s", (DateTime.Now - start).TotalSeconds);
            }

            return(result);
        }
        internal PatternSubstitutionMatch PatternMatchProbability(MultiTraceLog2 pattern, string questionSignature, IEnumerable <NodeReference> questionEnities, ComposedGraph graph)
        {
            var bestSubstitutions = new List <PathSubstitution>();

            foreach (var node in questionEnities)
            {
                var currentBestConfidence        = 0.0;
                PathSubstitution currentBestPath = null;
                foreach (var path in getSubstitutedPaths(pattern, node, graph))
                {
                    var currentPath   = path;
                    var originalNodes = currentPath.OriginalTrace.CurrentNodes;
                    var confidence    = currentPath.Rank / pattern.InitialNodes.Count();
                    //TODO consider context
                    confidence *= SubstitutionProbability(currentPath.Substitution, originalNodes);
                    currentPath = currentPath.Reranked(confidence);
                    if (currentBestConfidence < confidence)
                    {
                        currentBestPath       = currentPath;
                        currentBestConfidence = confidence;
                    }
                }

                if (currentBestPath == null)
                {
                    //substitution was not found
                    return(null);
                }

                bestSubstitutions.Add(currentBestPath);
            }


            //TODO we would like to have match information in the output
            return(new PatternSubstitutionMatch(bestSubstitutions));
        }
        private IEnumerable <TraceNode2> getCommonTraceNodes(NodeReference answerCandidate, MultiTraceLog2 pattern)
        {
            var compatibleTraces = new List <TraceNode2>();
            var filteredTraces   = getFilteredTraces(pattern);

            foreach (var node in filteredTraces)
            {
                if (node.PreviousNode == null)
                {
                    if (node.CurrentNodes.Contains(answerCandidate))
                    {
                        //candidate matches directly as an answer
                        compatibleTraces.Add(node);
                    }
                }
                else
                {
                    var tracePath = node.Path;
                    if (_graph.GetForwardTargets(new[] { answerCandidate }, tracePath).Any())
                    {
                        compatibleTraces.Add(node);
                    }
                }
            }

            return(compatibleTraces);
        }
        private IEnumerable <Ranked <NodeReference> > rankCandidates(IEnumerable <NodeReference> answerCandidates, MultiTraceLog2 pattern)
        {
            var candidates = new List <Ranked <NodeReference> >();

            foreach (var answerCandidate in answerCandidates)
            {
                var rank = getRank(answerCandidate, pattern);
                candidates.Add(new Ranked <NodeReference>(answerCandidate, rank));
            }
            return(candidates);
        }