{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# Search Proofs\n", "\n", "This notebook contains proofs of certain lemmas needed to ascertain that the search algorithm works correctly.\n", "They are referenced from the article that explains the algorithm:\n", "\n", "[Search Design](https://github.com/ETCBC/text-fabric/wiki/Search-design)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Algorithm\n", "\n", "Our search algorithm consists of several parts, the main ones of which are\n", "\n", "* spinning edges\n", "* stitching results\n", "\n", "We will make these terms precise now, and prove essential properties, leading to insight\n", "in the correctness of our algorithm, and the relative merits (and demerits) of it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Terminology\n", "Let us define all these textile, woolly, fluffy concepts into hard formal language.\n", "\n", "## Setting\n", "\n", "We have a dataset which is a graph consisting of text nodes and text edges, in short, nodes and edges.\n", "Nodes are textual objects, edges are relations between them.\n", "\n", "Searching is: looking for patterns of nodes and edges.\n", "The pattern is a template, describing nodes to be instantiated, constrained by relations that\n", "correspond to the edges in the graph.\n", "\n", "## Query graph\n", "A query is a graph of query nodes and query edges.\n", "We refer to the query nodes as\n", "$$q_1, ... , q_n$$\n", "and to the query edges as\n", "$$e_1, ... , e_k$$\n", "where every\n", "$$e_j = (R_j, q_f, q_t)$$\n", "for some $f$ and $t$ in $1, ..., n$, \n", "where $R_j$ is a relationship between text nodes.\n", "That means, if $s$ and $u$ are text nodes, $sR_ju$ is either *true* or *false*.\n", "\n", "### Query node\n", "A query node $q_i$ consists of a\n", "[node type](https://etcbc.github.io/text-fabric-data/features/hebrew/etcbc4c/otype)\n", "of text nodes and a \n", "[features](https://etcbc.github.io/text-fabric-data/features/hebrew/etcbc4c/0_overview.html)\n", "dict.\n", "The features dict specifies any number of data features, with an allowed value or set of allowed values for each of them.\n", "\n", "## Fleece and yarn\n", "The set of all text nodes of a\n", "[node type](https://etcbc.github.io/text-fabric-data/features/hebrew/etcbc4c/otype)\n", "is called the *fleece* of that node type.\n", "\n", "The fleece of a query node, is the fleece of the node type that is associated with that query\n", "node.\n", "\n", "If we filter the fleece of a node by some process, we call the result set a *yarn*\n", "of that query node.\n", "\n", "The *thick* yarn of a query node consists of the fleece of the query node,\n", "filtered by the features dict of the query node.\n", "\n", "## Spinning and proper yarns\n", "If we have an edge $e_j = (R_j, q_f, q_t)$ and if we have yarns $y_f, y_t$ for\n", "$q_f$, $q_t$, then we *turn* the edge $e_j$ \n", "to *spin* the yarns $y_f$, $y_t$ further.\n", "\n", "The result of this spinning are two new yarns, $z_f, z_t$, obtained as follows:\n", "\n", "* we iterate through the text nodes $s$ in yarn $y_i$:\n", " * we compute the set of text nodes $u$ in $y_j$ such that $sRu$ holds;\n", " * if this set is non-empty, we:\n", " * add those $u$ text nodes to $z_j$\n", " * add the $s$ text node to $z_i$\n", " \n", " Otherwise we do not add nodes to $z_i$ nor to $z_j$.\n", "\n", "So spinning means thinning out the yarns of two query nodes that are connected to a\n", "query edge, in such a way, that afterwards the relationship that is associated\n", "with the edge, can be realized for every text node in both yarns.\n", "In other words: after spinning, every text node in a yarn involved, has at least one\n", "counterpart text node in the other yarn such that both are in relationship which each other.\n", "That is, the relationship belonging to the edge.\n", "\n", "So, every *spinning* action is caused by a *turn* of the edge, which acts as the spinning\n", "wheel.\n", "\n", "A *proper yarn* of a query node is either its *principal yarn* or any yarn that results from\n", "turning edges between proper yarns.\n", "So if we start with the principal yarn of a query node, and start spinning, we produce\n", "proper yarns for that query node.\n", "\n", "## Stitching results\n", "\n", "A *result* of a query is a list of text nodes\n", "$$s_1, ... , s_n$$\n", "satisfying the following conditions:\n", "\n", "* for every $i$ in $1, ... , n$: $s_i$ is in the principal yarn of $q_i$\n", "* for every $e_j = (R_j, q_f, q_t)$: $s_fRs_t$\n", "\n", "If this is the case, we call text node $s_i$ a *stitch* for query node $q_i$. \n", "\n", "A result can be seen as a stitching together of the principal yarns of the query nodes,\n", "in such a way, that all constraints specified by the query edges are satisfied.\n", "The stitches are those nodes in the text, one for each yarn, that together constitute\n", "a result.\n", "\n", "## Overspun and underspun yards\n", "\n", "If a yarn of a query node has become so thin, that it fails to contain stitches of\n", "of the result of the query,\n", "we call the yarn *overspun*.\n", "\n", "If a yarn of a query node contains nodes that are not stitches of any result of the query,\n", "we call the yarn *underspun*.\n", "\n", "## Employ the terminology\n", "\n", "We can now use this fabric language to formulate our query algorithm and prove essential properties of it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The recipe\n", "\n", "Here we describe the starting condition for our algorithm, what happens at each iteration,\n", "and the conditions at termination.\n", "\n", "## Initial\n", "\n", "Here is the preparation for running a sequence of iterations.\n", "In those iterations we *turn* the edges to *spin* yarns.\n", "\n", "First of all, for every query node, we collect its fleece, and spin its principal yarn.\n", "\n", "> we compute edge constraints and filter the result sets of text nodes.\n", "\n", "> we collect all text nodes of the node type associated with the query node\n", "(the fleece), and we apply the criteria defined by the features of the query node\n", "(from fleece to principal yarn).\n", "\n", "We give every edge a the status: *not up-to-date*.\n", "The intention is to give an edge the status *up-to-date* if it has just been turned,\n", "and no turnings of other edges have since interfered with the yarns involved,\n", "so the yarns still reflect the effects of turning the edge.\n", "\n", "Initially, no edge has been turned, so every edge starts out as not up-to-date.\n", "\n", "## Iterations\n", "When query edges are computed, yarns are spun,\n", "some edges become up-to-date, others get *not* up-to-date.\n", "\n", "* we select a query edge by means of some strategy (see later on),\n", "* we turn the edge,\n", "* we set the status of the edge to up-to-date,\n", "* for each of both query nodes of the edge:\n", " * if its yarn has changed:\n", " * for each query edge leading to or from such a query node:\n", " * we set its status to not up-to-date\n", "\n", "## Final\n", "Here is when we stop turning the edges.\n", "We stop when continuing does not make sense anymore, and that happens if one\n", "of the following conditions occur. \n", "Whether we have correct and complete results if we stop, is something that\n", "remains to be seen.\n", "We'll prove it later.\n", "\n", "### Stop on an empty yarn\n", "If a yarn becomes empty, we can stop: there are no results at all. \n", "The combined turning of the edges has spun the yard into nothing: the thread has broken.\n", "Every yarn must be part of the result, so if a yarn breaks, the result is gone.\n", "\n", "> The constraints expressed by the query edges are such, that one of the query nodes\n", "cannot be instantiated by a text node. Since every query result must instantiate all\n", "query nodes by a text node, we do not have a result.\n", "\n", "### Stop on no change\n", "If all edges are up-to-date, it does not make sense to turn edges anymore.\n", "So we stop.\n", "\n", "> Turning an up-to-date edge means recomputing the constraints posed by that edge\n", "on node sets on which it has already been computed. There will be no effect." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Correctness and completeness\n", "\n", "The main questions are: \n", "* does the computation stop at all in all cases\n", "* and when it stops, have we got what we want?\n", "\n", "This fundamental question can be split into lesser ones.\n", "\n", "## Note about strategy\n", "The validity of the results delivered should not dependent on\n", "the strategy of selecting edges for computation. \n", "But it is easy to come up with a stupid strategy that will not terminate\n", "or not arrive at desired results.\n", "For example, if we always select the first edge,\n", "it is clear that this is in general a very unreliable strategy.\n", "\n", "So we suppose that our strategy of edge selection for computation satisfies a minimal\n", "sanity requirement. \n", "We can formulate that exactly:\n", "\n", "A strategy is *thorough* if it always selects a non-up-to-date edge if there is one.\n", "\n", "This is really a minimal criterion, since selecting an up-to-date edge is always a waste\n", "of time.\n", "And if there are no up-to-date edges, it is time to stop anyway.\n", "\n", "## Note on results\n", "The algorithm as defined above does not deliver a list of *results*, \n", "results being *stitches*. \n", "It only delivers the yarns to be stitched.\n", "\n", "> The algorithm yields a list of text node sets\n", "instantiations of all query nodes by text nodes in such a way that all individual query edges\n", "are satisfied. Which combinations of these text nodes constitute results, remains to \n", "be established.\n", "\n", "Instead, we assert that after the process, under some conditions, every yarn is *fully spun*, so\n", "none of them is *underspun* and none of them is *overspun*.\n", "We also prove that in general none of the yards are *overspun*, but that we cannot rule\n", "out *underspun* yarns in general.\n", "\n", "> The claim is that every node set has only nodes that occur in the real results, and that\n", "all nodes that occur in the real results, do occur in the proper node sets (the latter only under\n", "some conditions)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lemma 1: Termination\n", "**Under every *thorough* strategy, the iteration always terminates.**\n", "\n", "**Proof:**\n", "\n", "If an edge is turned, no yarn becomes longer. It is possible, however, that no yarn\n", "shrinks.\n", "\n", "For every turn of an edge there are two cases to consider:\n", "\n", "a. No yarn becomes shorter:\n", " Then the amount of up-to-date edges increases by one.\n", " This is so, because the recently turned edge becomes up-to-date, and no other edges \n", " become non-up-to-date.\n", "b. One or more yarns become shorter.\n", "\n", "Now suppose that that the sequence of edge turnings never stops.\n", "Mark every step as `a` if case a. applies and as `b` if case b. applies.\n", "Our infinite turning is then an infinite sequence of `a` and `b`.\n", "\n", "In that sequence there cannot be infinitely many `b`, because every `b` corresponds to\n", "a shortening of the total amount of yarn, and the total length of yarn is finite.\n", "\n", "So there must be infinitely many `a`.\n", "That means that from some point onward, we see only `a` and never a `b` anymore.\n", "But every `a` decreases the number of non-up-to-date edges, so a sequence of `a` is\n", "bound to stop.\n", "\n", "Ergo: the sequence cannot be infinite and the computation terminates." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lemma 2: No overspinning\n", "**Turning of edges does not cause overspinning: no yarn gets overspun.**\n", "\n", "**Proof:**\n", "\n", "Suppose none of the yarns is overspun yet, and we turn an edge\n", "$e = (R_j, q_f, q_t)$ on yarns $y_f, y_t$ resulting in yarns $z_f, z_t$.\n", "\n", "We assume $y_f$ and $y_t$ are not overspun.\n", "\n", "**Case a: Suppose $z_f$ is overspun.**\n", "\n", "(i) Then there must be a result text node $s$ of query node $q_f$ in $y_f$ \n", "that does not reappear in $z_f$.\n", "\n", "Because $s$ is in the result, there must be a text node $u$ in the result\n", "of query node $q_t$, such that $sR_ju$.\n", "\n", "That means that $u$ belongs to every yarn of $q_t$ that is not overspun.\n", "By hypothesis, $y_t$ is not overspun, so $u$ is member of $y_t$.\n", "\n", "By following the definition of turning an edge,\n", "we see that the existence of this $u$ will cause $s$ to be added to $z_f$\n", "which conflicts with (i).\n", "\n", "So case a. does not happen.\n", "\n", "**Case b: Suppose $z_t$ is overspun.**\n", "\n", "(ii) Then there must be a result text node $u$ of query node $q_t$ in $y_t$ \n", "that does not reappear in $z_t$.\n", "\n", "Because $u$ is in the result, there must be a text node $s$ in the result\n", "of query node $q_f$, such that $sR_ju$.\n", "\n", "That means that $s$ belongs to every yarn of $q_f$ that is not overspun.\n", "By hypothesis, $y_f$ is not overspun, so $s$ is member of $y_f$.\n", "\n", "By following the definition of turning an edge,\n", "we see that the existence of this $s$ will cause $u$ to be added to $z_t$\n", "which conflicts with (ii).\n", "\n", "**No more cases: $z_f$ and $z_t$ are not overspun. QED**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Corollary 3: Empty yarn means no results\n", "**If, after some turning, a yarn becomes empty, the query as a whole has no results.**\n", "\n", "**Proof:**\n", "\n", "If at some point a yarn, say $y_i$ becomes empty, we know by lemma 2, that it is\n", "still not overspun.\n", "That means that all nodes that occur in a result and that correspond with query node\n", "$q_i$, are contained in this empty yarn $y_i$. \n", "\n", "Hence there are no results, QED." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Definition: Connected components\n", "A connected component of a graph is a subgraph satisfying two conditions:\n", "* **connectedness:** \n", " there is a bridge of edges between each pair of nodes, meaning that for each pair of\n", " nodes $q_0$ and $q_{n+1}$ there is a set of nodes\n", " $q_1, ... , q_n$ in the subgraph such that for each $i$ in $0, ..., n$:\n", " * there is either an edge from $q_i$ to $q_{i+1}$ or the other way round;\n", "* **maximal:**\n", " every other subgraph that properly contains this subgraph, does not have the\n", " **connectedness** property." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " \n", "## Lemma 3: Decomposition\n", "Every graph can be divided into a number of disjunct connected components.\n", "\n", "**Proof:**\n", "\n", "See the literature on graph theory." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lemma 4: Decomposing queries\n", "**The results of a query is essentially the set consisting of the cartesian product of the \n", "query results of its connected component queries.**\n", "\n", "**Proof:**\n", "\n", "We prove first:\n", "\n", "### Lemma 4a: \n", "**If a query can be split into two subparts between which there are no edges,\n", "then the results of the whole query can be seen as the cartesian product of \n", "the results of the two subparts.**\n", "\n", "**Proof:**\n", "\n", "Say the query is a graph $G$ consisting of\n", "$G_1 = (Q_1, E_1)$ and $G_2 = (Q_2, E_2)$,\n", "with no edges between $Q_1$ and $Q_2$ and\n", "with result sets $R_1$ and $R_2$.\n", "\n", "Say there are $m_1$ nodes in $Q_1$ and $m_2$ in $Q_2$.\n", "\n", "Then $R_1$ is a set of $m_1$ tuples of text nodes,\n", "and $R_2$ is a set of $m_2$ tuples of text nodes.\n", "\n", "Every $r_1$ in $R_1$ is a $m_1$-tuple of text nodes satisfying $G_1$.\n", "\n", "Every $r_2$ in $R_2$ is a $m_2$-tuple of text nodes satisfying $G_2$.\n", "\n", "Then, for every combination of such an $r_1$ and $r_2$,\n", "the concatenation of $r_1$ and $r_2$ a $m_1+m_2$-tuple of text nodes.\n", "\n", "The first $m_1$ text nodes instantiate the query nodes of $G_1$\n", "and satisfy all edge constraints of $G_1$. \n", "\n", "They do not have to satisfy the query nodes in $G_2$\n", "in order to be valid for $G$ as a whole,\n", "because they do not correspond to them.\n", "\n", "They are not influenced by the constraints of the edges in $G_2$,\n", "because these edges do not reach the nodes of $G_1$.\n", "\n", "Analogous for the last $m_2$ nodes in such a combination.\n", "\n", "Hence the combination is a result of the whole graph.\n", "\n", "Conversely, every result of the whole graph is a tuple that can be decomposed in an $r_1$\n", "which is a result of $G_1$ and an $r_2$ which is a result of $G_2$.\n", "\n", "**QED (lemma 4a)**\n", "\n", "Now we can prove by induction on the number of connected components of a query\n", "that its results can be seen as the cartesian product \n", "of the results of its connected components.\n", "\n", "**Case a:**\n", "\n", "The query consists of just one connected component: the lemma is trivially true.\n", "\n", "**Case b:**\n", "The query consists of $n+2$ connected components, $n > 0$.\n", "\n", "We assume by way of induction hypothesis\n", "that the lemma holds for all cases up to $n+1$.\n", "\n", "Say $G = (G_1 + ... + G_{n+2}, E_1, ... , E_{n+2})$\n", "with result set $R$, \n", "where each \n", "$(G_i, E_i)$ is a connected component with result set $R_i$.\n", "\n", "Say $H = (G_1 + ... + G_{n+1}, E_1, ... , E_{n+1})$\n", "then we know by induction hypothesis that the results of H are\n", "$R_1 \\times ... \\times R_{n+1}$.\n", "\n", "Now $H$ and $G_{n+2}$ satisfy the conditions of lemma 4a, to the results of\n", "\n", "$G = H + (G_{n+2}, E_{n+2})$\n", "\n", "is $(R_1 \\times ... \\times R_{n+1}) \\times R_{n+2}$\n", "\n", "is $R_1 \\times ... \\times R_{n+1} \\times R_{n+2}$\n", "\n", "**QED (lemma 4)**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Concept: Cyclic queries\n", "A *cycle* in a query is a list of query nodes connected by edges, where the first node is equal to\n", "the last node. \n", "Here the edges are taken in the directional sense.\n", "\n", "### Examples\n", "Here is a graph with a cycle:\n", "$$G = (\\{q_1, q_2\\}, \\{(R, q_1, q_2), (S, q_2, q_1)\\})$$\n", "But this is not a cycle:\n", "$$G = (\\{q_1, q_2\\}, \\{(R, q_1, q_2), (S, q_1, q_2)\\})$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lemma 5: Eventual full-spinning, under conditions\n", "**For queries consisting of just one connected component:**\n", "\n", "**if the query does not contain cycles:**\n", "\n", "**if, after some spinning, all edges are up-to-date, then all yarns are fully spun.**\n", "\n", "**Proof:**\n", "Because the yarns are never overspun by lemma 2,\n", "we must prove that when all edges are up-to-date,\n", "none of the yarns are *underspun*.\n", "\n", "Suppose we are in the situation that all edges are up-to-date.\n", "Suppose that in this situation there is an underspun yarn $y_i$, associated\n", "with query node $q_i$.\n", "That means that $y_i$ contains a text node $s$ that does not belong to any final result.\n", "\n", "Let us have a closer look at how $q_i$ lies hooked up in the query graph.\n", "Because the query is a connected component, we have only these possibilities:\n", "\n", "**Case a: $q_i$ does not belong to an edge.**\n", "\n", "In this case, $q_i$ must the only node, because if there were other nodes,\n", "there would have been bridges from $q_i$ to those other nodes, and hence\n", "$q_i$ would be involved in edges.\n", "\n", "So, the query is just one node without edges, hence $y_i$ is the primary yarn,\n", "and hence all nodes in it are results. So this $s$ cannot exist.\n", "\n", "**Case b: $q_i$ has an out-going edge or an incoming edge.**\n", "\n", "For all $q_i$-outgoing edges $(R_j, q_i, q_t)$ it holds that there\n", "is a $u$ in the yarn $y_t$ of $q_t$ such that $sR_ju$.\n", "This is because all edges are up-to-date.\n", "\n", "Like wise, for all $q_i$-incoming edges $(R_k, q_f, q_i)$ it holds that\n", "there is a $u$ in the yarn $y_f$ of $q_f$ such that $uR_ks$.\n", "\n", "If all $u$s found in this way belonged to the result, then $s$ would also belong\n", "to the result, because all its edge constraints would be satisfied.\n", "\n", "So, if, as we assumed, $s$ is not in the result, then at least one of those $u$s\n", "does not belong to the result.\n", "\n", "Then we can repeat the same argument for this $u$, and find a $v$ that does not belong\n", "to the result.\n", "\n", "Since there are finitely many nodes, we find a cycle $s_1, ... , s_k$ of text\n", "nodes in yarns $y_1, ... , y_k$ that do not belong to the result.\n", "\n", "This contradicts the assumption. QED." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 5b: Cycles can prevent full spinning.\n", "In order to show that the condition of *no cycles* in Lemma 5 cannot be missed,\n", "we show a small example of a graph with a cycle that will not become fully spun\n", "by the algorithm.\n", "\n", "$$G = (\\{q_1, q_2\\}, \\{(R, a, c), (R, b, d), S(c, b), S(d, a)\\})$$\n", "\n", "with primary yarns $y_1, y_2$ for $q_1, q_2$ as follows:\n", "\n", "$$y_1 = \\{a, b\\}$$\n", "$$y_2 = \\{c, d\\}$$\n", "\n", "If we spin the edges $R$ and $S$, nothing happens, because\n", "the elements $a, b \\in y_1$ are happily in relationship $R$ with elements $c, d$ in $y_2$,\n", "and\n", "the elements $c, d \\in y_2$ are happily in relationship $S$ with elements $a, b$ in $y_2$.\n", "\n", "Yet, none of them are part of any result, because there are no results, because the equation\n", "\n", "$$xRySx$$\n", "\n", "does not have any solutions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analysis\n", "The trouble with a cycle is, that it creates a long distance dependency in the final results\n", "that cannot be \"felt\" by spinning.\n", "\n", "You can also have long distance dependencies by confluence, but these will be eventually solved by just spinning.\n", "\n", "For example: if we have to solve both of the following\n", "\n", "$$uRvSwTxUk$$\n", "$$aGbHcKmUk$$\n", "\n", "then we could solve both chains separately, and if both have a solution, we now that the combined solution is a complete solution.\n", "Hence, if there is no solution, one of the two will have no solution.\n", "\n", "In case of the cycle, we are essentially trying to solve\n", "\n", "$$xRySz$$\n", "$$x = z$$\n", "\n", "where $x = z$ is the long distance dependency." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Result fetching\n", "\n", "Spinning is just part of the solution.\n", "It thins out the space in which solutions exist, and does a better job for graphs without\n", "cycles than for graphs with cycles.\n", "\n", "But after spinning, we have to find the valid stitches between the yarns.\n", "\n", "That is a separate search process. \n", "The challenge is to wade through all possibilities in an efficient way.\n", "\n", "For graphs without cycles we know that if we start with any text node in any yarn, it will\n", "be part of a result.\n", "And if we start stitching that node to a node in the next yarn, we are sure that the\n", "pair is also part of a result. And so on. So in this case we can stitch arbitrarily (note\n", "that stitching implies that we hop from one yarn to the other by following the appropriate\n", "relationships between text nodes), and always generate results.\n", "\n", "In this case, it does not matter much in what order we stitch, because we need to make all stitchings anyway to generate results.\n", "\n", "If the graph does have cycles, then we will discover that certain partial stitchings will\n", "lead to no valid results.\n", "Because of that strategy becomes important, because if we are not smart, we could spend a lot\n", "of time in rejecting millions of stitchings, before arriving at the first valid stitching.\n", "\n", "Here is an example, and let us become a bit more concrete.\n", "\n", "Suppose our graph has nodes: $Sentence$, $Word1$, $Word2$, all without feature restrictions.\n", "So the primary yarns are all sentences, all words, all words, respectively.\n", "Suppose we have 100,000 sentences, all 10 words, so 1 million words in total.\n", "\n", "Suppose our graph has edges:\n", "\n", "* `word1` is in `sentence`\n", "* `word2` is in `sentence`\n", "* `word1` comes before `word2`\n", "\n", "A solution is a tuple of text nodes $s$, $w_1$, $w_2$, such that $w_1$ is a word\n", "in sentence $s$, $w_2$ is a word in the same sentence $s$, and $w_1$ comes before $w_2$.\n", "\n", "Let's start stitching by first picking `word1`, then `word2`, then `sentence`.\n", "\n", "We start to pick the first word $w_1$ in the yarn of `word1`, which is all words>\n", "\n", "Then we pick a word $w_2$ in the yarn of `word2`, such that $w_2$ comes after $w_1$.\n", "\n", "Note that we have 999,999 possibilities for $w_2$, of which only the first 10 are part of\n", "a result. All others are not in the same sentence.\n", "\n", "So if we try out all these $w_2$s, we get 10 results fairly quickly, and then 999,990 spurious tries, before we try a new node in the yarn of $Word1$ and get new results.\n", "\n", "By the time we have collected all results,\n", "we have visited a million times on average half a million words, or\n", "$1,000,000 \\times 500,000 = 500,000,000,000$ words.\n", "\n", "We could have done it in an other way: after picking $w_1$ from the yarn of $Word1$,\n", "we pick a sentence $s_1$ from the yarn of *Sentence* (only one possibility), and from\n", "there we pick a $w_2$ from the yarn of $Word2$ such that $w_2$ is in $s_1$ (only 10 possibilities). Of those 10 possibilities, one will be rejected, (where $w_2 = w_1$).\n", "\n", "After this, we move to the next $w_1$ in the yarn of $Word1$, which is the second word in the same sentence, pick the same $s_1$ in the sentence yarn, pick the same words in the $Word2$\n", "yarn, reject 2 of them, and deliver 8 results.\n", "And so on.\n", "When we have gone through the whole of $s_1$, we have tried 100 words, and rejected 50 of them. \n", "And so it goes for all sentences.\n", "In the end we find all 100,000 * 50 = 5,000,000 solutions by visiting 10,000,000 words.\n", "\n", "This is a 50,000 fold improvement!\n", "\n", "So how can we build a strategy from this observation?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Spin strategy\n", "\n", "The challenge is now to run the edges in an optimal sequence.\n", "\n", "The basic intuition is this.\n", "\n", "* some query nodes filter strongly, others hardly, i.e. some atom results are small compared to the total\n", " number of nodes in their node type, other atom results are nearly as big as the total node type.\n", "* if an edge connects a strongly filtering node with a weakly filtering node, we expect a big reduction\n", "* if we work within the strongest filtering query nodes, we do not have to do much work and when we reach the\n", " weaker filters, they will decrease rapidly\n", "* so we postpone to touch the bigger sets as long as possible, and when we touch them, they are expected to decrease\n", " quickly\n", "\n", "We are going to rank query nodes by how strong they have filtered their node type so far.\n", "\n", "* let $o_n$ be the node type associated with query node $n$\n", "* let $r_n$ be the current result set associated with query node $n$\n", "\n", "\n", "Then the **query fraction** $q(n)$ is the a proportion between\n", "the number of text nodes in the current result:\n", "and\n", "the total number of text nodes in the node type:\n", "\n", "$$q(n) = {{|r_n|}\\over {|o_n|}}$$\n", "\n", "Then we rank the edges by combined query fraction of the nodes:\n", "\n", "$$ r(f,t) = q(f)^2 + q(t)^2$$\n", "\n", "By squaring both query fractions, we strongly give precedence to edges involving few results." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Stitching strategy\n", "\n", "Consider the relationships by which we hop from one yarn to another, while stitching.\n", "Some of them are functional, in the sense that coming from node, they leave only one possibility for the next node in the stitching.\n", "\n", "Take for example the relation: *sentence of*. Coming from a word, this relationship leaves\n", "us but one choice: the one sentence of which that word is a part.\n", "\n", "Other relationships are virtually the opposite of functional: they leave very many options.\n", "\n", "Take for example the relation: *comes after* between words. For any word you have on average the choice of half the total amount of words.\n", "\n", "Some relationships that are not functional are still functional in the opposite direction.\n", "\n", "Take for example: *contains* between sentences and words. A sentence contains multiple words,\n", "so *contains* is not functional. But going into the other direction, is the precisely the\n", "relation: *sentence of*, which is functional.\n", "\n", "Between functional and non-functional relations there is a spectrum of *functionalness*.\n", "Let us call this notion the *spread* of a relation.\n", "\n", "### Definition: spread\n", "\n", "**The spread of a relation $R$ is the average number of $t$ that satisfies the\n", "equation $fRt$ for each $f$.**\n", "\n", "In other words, for every *from* node, compute to how many *to* nodes $R$ brings you.\n", "Take the average, and that is your spread.\n", "\n", "## Back to stitching\n", "\n", "When stitching, we want to follow the query graph in such a way that we hop from yarn to\n", "yarn by edges with relations with lesser spread first.\n", "\n", "This could be a way:\n", "\n", "1. compute the spread of all relations in the query graph, and all their converses too\n", "2. for every relation, consider whether it or its converse has the lesser spread\n", " and if so, replace the relation by its converse in the opposite direction\n", "3. for each node, define the its spread as the sum of the spreads of its outgoing\n", " relations\n", "4. start with a node that has minimal spread among nodes with outgoing edges\n", "5. go from that node via outgoing edge to a node with minimum spread" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 1 }