{"id":1233,"date":"2019-09-26T15:59:10","date_gmt":"2019-09-26T14:59:10","guid":{"rendered":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/?p=1233"},"modified":"2019-10-08T23:58:19","modified_gmt":"2019-10-08T22:58:19","slug":"connected-components","status":"publish","type":"post","link":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/connected-components\/","title":{"rendered":"Connected Components, Concurrently"},"content":{"rendered":"<p>Computing\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Component_(graph_theory)\">connected components<\/a> in an undirected graph is one of the most basic graph problems. Given a graph with <span style=\"color: #000080\">n<\/span> vertices and <span style=\"color: #000080\">m<\/span> edges, you can find its components in linear time <span style=\"color: #000080\">O(n + m)<\/span> using\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Depth-first_search\">depth-first<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/Breadth-first_search\">breadth-first<\/a> search. But what if you need to go faster? In this blog post, I will describe a cool new\u00a0<em>concurrent<\/em> algorithm for this problem, which I learned this week at\u00a0the <a href=\"https:\/\/www.heidelberg-laureate-forum.org\/forum\/7th-hlf-2019.html\">Heidelberg Laureate Forum<\/a>\u00a0from Robert Tarjan himself. The algorithm distributes the work among\u00a0<span style=\"color: #000080\">n + m<\/span>\u00a0tiny processors that work concurrently most of the time and requires\u00a0<span style=\"color: #000080\">O(log n)<\/span>\u00a0global synchronisation rounds. The algorithm is remarkably simple but it&#8217;s far from obvious that it works correctly and efficiently. Happily, Tarjan and his co-author S. Cliff Liu have done all the hard proofs in <a href=\"https:\/\/arxiv.org\/abs\/1812.06177\">their recent paper<\/a>, so we can simply take the algorithm and use it.<\/p>\n<p><!--more--><\/p>\n<p>First, let&#8217;s recap the classic solution based on the depth-first search. I&#8217;ll use my favourite graph library <a href=\"https:\/\/github.com\/snowleopard\/alga\">Alga<\/a>, so my examples will be in Haskell. Below I create an <span style=\"color: #000080\">example<\/span> undirected graph and compute the number of connected components by counting trees in the depth-first search forest. The graph and the forest are shown in the figure; the edges that belong to the forest are directed to illustrate the order of graph traversal.<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\">\u03bb<span style=\"color: #666666\">&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">import<\/span> <span style=\"color: #0000ff;font-weight: bold\">Algebra.Graph.Undirected<\/span>\r\n\u03bb<span style=\"color: #666666\">&gt;<\/span> example <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> edges [ (<span style=\"color: #666666\">1<\/span>,<span style=\"color: #666666\">6<\/span>), (<span style=\"color: #666666\">2<\/span>,<span style=\"color: #666666\">6<\/span>), (<span style=\"color: #666666\">3<\/span>,<span style=\"color: #666666\">7<\/span>), <span style=\"color: #666666\">...<\/span> ]\r\n\u03bb<span style=\"color: #666666\">&gt;<\/span> length <span style=\"color: #666666\">$<\/span> dfsForest <span style=\"color: #666666\">$<\/span> fromUndirected example\r\n<span style=\"color: #666666\">3<\/span>\r\n<\/pre>\n<\/div>\n<p><a href=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/network-dfs-forest.png\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/network-dfs-forest.png\" alt=\"Example depth-first search forest\" width=\"630\" \/><\/a><\/p>\n<p>This approach is very simple and you should definitely use it, provided that the linear complexity <span style=\"color: #000080\">O(n + m)<\/span> is fast enough for your application. But what if you need to go faster? Some time ago my colleagues and I wrote a <a href=\"https:\/\/www.staff.ncl.ac.uk\/andrey.mokhov\/graphs-on-fpga.pdf\">paper<\/a> where we showed how to take advantage of concurrency by implementing the breadth-first search on an FPGA, resulting in better time complexity <span style=\"color: #000080\">O(d)<\/span> where <span style=\"color: #000080\">d<\/span> is the diameter of the graph. This was an excellent result for our application where graphs had a small diameter, but in general, the diameter can be as large as <span style=\"color: #000080\">n<\/span>. So can we do better?<\/p>\n<p>Yes! Tarjan&#8217;s paper presents several faster algorithms. It also describes the algorithms in a very nice compositional manner:<\/p>\n<p><a href=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/algorithms.png\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/algorithms.png\" alt=\"Concurrent connected components algorithms\" width=\"630\" \/><\/a><\/p>\n<p>To play with and understand these algorithms, let&#8217;s translate the above definitions to Haskell, trying to preserve their clarity and conciseness while also being explicit about details. Jumping ahead a little, here is how Algorithm P will look like in the end:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">algorithmP<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> repeat (parentConnect <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> update <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> shortcut)\r\n<\/pre>\n<\/div>\n<p>Pretty close! Now, let&#8217;s define the primitives\u00a0<span style=\"color: #000080\">parentConnect<\/span>\u00a0etc. in terms of the underlying computational model where a lot of tiny processing threads communicate via short messages, in rounds. In a round, threads concurrently receive some messages, then update their local states, and possibly send out new messages for the next round. We can capture the <em>local view<\/em> of a computation round by the following type:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #008000;font-weight: bold\">type<\/span> <span style=\"color: #b00040\">Local<\/span> s t i o <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> s <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> t <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> [i] <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, [(t, o)])\r\n<\/pre>\n<\/div>\n<p>Here <span style=\"color: #000080\">s<\/span> is a type of <em>states<\/em>, <span style=\"color: #000080\">t<\/span> is a type of <em>threads<\/em>, and <span style=\"color: #000080\">i<\/span> and <span style=\"color: #000080\">o<\/span> are types of <em>incoming and outgoing messages<\/em> in a round of computation. In words, given the current state of a thread and a list of incoming messages, a <span style=\"color: #000080\">Local<\/span>\u00a0function returns an updated state and a list of outgoing messages, each tagged with a target thread identifier &#8212; these messages will be delivered in the next computation round.<\/p>\n<p>We can upload\u00a0<span style=\"color: #000080\">Local<\/span> functions to our tiny processors, say on an FPGA, inject input messages into the communication network, and extract the outputs after a computation round, i.e. when all threads complete the computation. This requires specialised hardware\u00a0and non-trivial setup, so let&#8217;s find a way to simulate such computations on a big sequential machine that has enough memory to have the <em>global view<\/em>\u00a0of a round:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #008000;font-weight: bold\">type<\/span> <span style=\"color: #b00040\">Global<\/span> s t i o <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> (<span style=\"color: #b00040\">Map<\/span> t s, [(t, i)]) <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (<span style=\"color: #b00040\">Map<\/span> t s, [(t, o)])\r\n<\/pre>\n<\/div>\n<p>The <span style=\"color: #000080\">Global<\/span> function takes a map from threads to their current states and a list of all input messages in the network and returns the resulting global state: a map of new thread states and a list of newly generated output messages. Note that such global computations can be composed using function composition and form a category; the operator <span style=\"color: #000080\">&gt;&gt;&gt;<\/span> in the above code snippet for\u00a0<span style=\"color: #000080\">Algorithm P<\/span> is just the left-to-right composition defined in the standard module\u00a0<span style=\"color: #000080\">Control.Category<\/span>.<\/p>\n<p>Converting from a <span style=\"color: #000080\">Local<\/span> to a <span style=\"color: #000080\">Global<\/span> view is relatively straightforward:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">round<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Ord<\/span> t <span style=\"color: #aa22ff;font-weight: bold\">=&gt;<\/span> <span style=\"color: #b00040\">Local<\/span> s t i o <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #b00040\">Global<\/span> s t i o\r\n<span style=\"color: #0000ff\">round<\/span> local (states, messages) <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> collect (<span style=\"color: #b00040\">Map<\/span><span style=\"color: #666666\">.<\/span>mapWithKey update states)\r\n  <span style=\"color: #008000;font-weight: bold\">where<\/span>\r\n    deliveries <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">Map<\/span><span style=\"color: #666666\">.<\/span>fromAscList (groupSort messages)\r\n    update t s <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> local s t (<span style=\"color: #b00040\">Map<\/span><span style=\"color: #666666\">.<\/span>findWithDefault <span style=\"color: #b00040\">[]<\/span> t deliveries)\r\n    collect    <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> runWriter <span style=\"color: #666666\">.<\/span> traverse writer\r\n<\/pre>\n<\/div>\n<p>We first find all the\u00a0<span style=\"color: #000080\">deliveries<\/span>, i.e. lists of incoming messages that should be delivered to each thread, then execute the local <span style=\"color: #000080\">update<\/span> functions of each thread, sequentially (note that this is OK since threads interact only between\u00a0rounds), and finally,\u00a0<span style=\"color: #000080\">collect<\/span>\u00a0outgoing messages of all threads.<\/p>\n<p>To express Tarjan&#8217;s algorithms, we will need the generic\u00a0<span style=\"color: #000080\">repeat<\/span> function that executes a given <span style=\"color: #000080\">Global<\/span> computation repeatedly until thread states stop changing.<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">repeat<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> (<span style=\"color: #b00040\">Eq<\/span> s, <span style=\"color: #b00040\">Eq<\/span> t) <span style=\"color: #aa22ff;font-weight: bold\">=&gt;<\/span> <span style=\"color: #b00040\">Global<\/span> s t <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #b00040\">Global<\/span> s t <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #b00040\">Void<\/span>\r\n<span style=\"color: #0000ff\">repeat<\/span> g (states, messages)\r\n    <span style=\"color: #666666\">|<\/span> states <span style=\"color: #666666\">==<\/span> newStates <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> (states, messages)\r\n    <span style=\"color: #666666\">|<\/span> otherwise           <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> repeat g (newStates, newMessages)\r\n  <span style=\"color: #008000;font-weight: bold\">where<\/span>\r\n    (newStates, newMessages) <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> g (states, messages)\r\n<\/pre>\n<\/div>\n<p>Note that we require that the network is <em>quiescent<\/em> before and after a given computation, as indicated by the type of incoming and outgoing messages\u00a0<span style=\"color: #000080\">i = o = Void<\/span>. Of course, the computation may be comprised of multiple rounds, which can exchange messages between each other!<\/p>\n<p>Now let&#8217;s use the above definitions to express Algorithms P and R from Tarjan&#8217;s paper. We&#8217;ll have <span style=\"color: #000080\">n + m<\/span> threads corresponding to vertices and edges, whose states will be very minimalistic: edges will have no state at all, and every vertex will store its current <em>parent<\/em>\u00a0i.e. the minimum vertex of the connected component to which the vertex is currently assigned.<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #008000;font-weight: bold\">type<\/span> <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">Int<\/span>\r\n\r\n<span style=\"color: #008000;font-weight: bold\">data<\/span> <span style=\"color: #b00040\">Thread<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">VertexThread<\/span> <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #666666\">|<\/span> <span style=\"color: #b00040\">EdgeThread<\/span> <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #b00040\">Vertex<\/span>\r\n<span style=\"color: #008000;font-weight: bold\">data<\/span> <span style=\"color: #b00040\">State<\/span>  <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">VertexState<\/span>  <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #666666\">|<\/span> <span style=\"color: #b00040\">EdgeState<\/span>\r\n<\/pre>\n<\/div>\n<p>A vertex that is its own parent is called\u00a0<em>root<\/em>; all vertices are initially roots. If this reminds you of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Disjoint-set_data_structure\">disjoint-set data structure<\/a> you are on the right track!\u00a0All algorithms follow the same general idea: we start by assigning each vertex to a separate component and then use edges to inform their neighbouring vertices about other reachable vertices in the component, maintaining the invariant that the root of a component is its smallest vertex. As rounds progress, we grow a parent forest, not dissimilar to the forest produced by the depth-first search for our earlier example graph, but now taking advantage of concurrency.<\/p>\n<p>Let&#8217;s also create a convenient type synonym for denoting global views of computation rounds involving <span style=\"color: #000080\">s = State<\/span> and <span style=\"color: #000080\">t = Thread<\/span>:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #008000;font-weight: bold\">type<\/span> (<span style=\"color: #666666\">~&gt;<\/span>) i o <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">Global<\/span> <span style=\"color: #b00040\">State<\/span> <span style=\"color: #b00040\">Thread<\/span> i o\r\n<\/pre>\n<\/div>\n<p>We can now express computation primitives, such as\u00a0<span style=\"color: #000080\">connect<\/span>, where edges inform their neighbours about each other:<\/p>\n<p>&nbsp;<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">connect<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Vertex<\/span>\r\n<span style=\"color: #0000ff\">connect<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s t <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> t <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n    <span style=\"color: #b00040\">VertexThread<\/span> <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n    <span style=\"color: #b00040\">EdgeThread<\/span> x y <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, [(<span style=\"color: #b00040\">VertexThread<\/span> (max x y), min x y)])\r\n<\/pre>\n<\/div>\n<p>The type <span style=\"color: #000080\">Void\u00a0~&gt; Vertex<\/span>\u00a0says that the round starts with no messages in the network and ends with messages carrying vertices. Vertex threads are dormant in the round, whereas edge threads generate one message each, sending a smaller vertex to the thread corresponding to the larger vertex so that the latter could update its parent. Note that we express the behaviour locally, and then use the function\u00a0<span style=\"color: #000080\">round<\/span>\u00a0defined above to obtain its <span style=\"color: #000080\">Global<\/span> semantics.<\/p>\n<p>This <span style=\"color: #000080\">connect<\/span> round can be followed by the <span style=\"color: #000080\">update<\/span> round, where vertex threads process incoming messages, updating their parents accordingly:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">update<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Void<\/span>\r\n<span style=\"color: #0000ff\">update<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s <span style=\"color: #008000;font-weight: bold\">_<\/span> i <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> s <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n    <span style=\"color: #b00040\">VertexState<\/span> p <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (<span style=\"color: #b00040\">VertexState<\/span> <span style=\"color: #666666\">$<\/span> minimum (p <span style=\"color: #b00040\">:<\/span> i), <span style=\"color: #b00040\">[]<\/span>)\r\n    <span style=\"color: #b00040\">EdgeState<\/span>     <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n<\/pre>\n<\/div>\n<p>The primitives\u00a0<span style=\"color: #000080\">connect<\/span> and\u00a0<span style=\"color: #000080\">update<\/span>\u00a0are not sufficient on their own; we need another crucial ingredient &#8212; the <span style=\"color: #000080\">shortcut<\/span> primitive that halves the depths of trees in the parent forest, similarly to the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Disjoint-set_data_structure#Path_compression\">path compression technique<\/a> used in the disjoint-set data structure.<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">shortcut<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Void<\/span>\r\n<span style=\"color: #0000ff\">shortcut<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> request <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> respondParent <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> update\r\n  <span style=\"color: #008000;font-weight: bold\">where<\/span>\r\n    request <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Thread<\/span>\r\n    request <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s t <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> s <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n        <span style=\"color: #b00040\">VertexState<\/span> p <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, [(<span style=\"color: #b00040\">VertexThread<\/span> p, t)])\r\n        <span style=\"color: #b00040\">EdgeState<\/span>     <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n\r\n<span style=\"color: #0000ff\">respondParent<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Thread<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Vertex<\/span>\r\n<span style=\"color: #0000ff\">respondParent<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s <span style=\"color: #008000;font-weight: bold\">_<\/span> i <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> s <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n    <span style=\"color: #b00040\">VertexState<\/span> p <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, map (,p) i)\r\n    <span style=\"color: #b00040\">EdgeState<\/span>     <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n<\/pre>\n<\/div>\n<p>In <span style=\"color: #000080\">shortcut<\/span>, every vertex <span style=\"color: #000080\">request<\/span>s the parent of its parent sending itself as a &#8220;respond-to&#8221; address. In the subsequent round, each vertex thread <span style=\"color: #000080\">respond<\/span>s by sending its parent. The process completes by running the <span style=\"color: #000080\">update<\/span> primitive defined above. Note how we can compose simple primitives together in a type-safe way, obtaining a non-trivial <span style=\"color: #000080\">shortcut<\/span>.<\/p>\n<p>The last primitive that I&#8217;ll cover is <span style=\"color: #000080\">parentConnect<\/span>, a variation of <span style=\"color: #000080\">connect<\/span> that informs the parents of two edge vertices about each other:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">parentConnect<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Vertex<\/span>\r\n<span style=\"color: #0000ff\">parentConnect<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> request <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> respondParent <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> receive\r\n  <span style=\"color: #008000;font-weight: bold\">where<\/span>\r\n    request <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Thread<\/span>\r\n    request <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s t <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> t <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n        <span style=\"color: #b00040\">VertexThread<\/span> <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n        <span style=\"color: #b00040\">EdgeThread<\/span> x y <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, [(<span style=\"color: #b00040\">VertexThread<\/span> x, t), (<span style=\"color: #b00040\">VertexThread<\/span> y, t)])\r\n\r\n    receive <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Vertex<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Vertex<\/span>\r\n    receive <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> round <span style=\"color: #666666\">$<\/span> <span style=\"color: #0000ff\">\\<\/span>s <span style=\"color: #008000;font-weight: bold\">_<\/span> i <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> s <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n        <span style=\"color: #b00040\">VertexState<\/span> <span style=\"color: #008000;font-weight: bold\">_<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, <span style=\"color: #b00040\">[]<\/span>)\r\n        <span style=\"color: #b00040\">EdgeState<\/span>     <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #008000;font-weight: bold\">case<\/span> i <span style=\"color: #008000;font-weight: bold\">of<\/span>\r\n            [x, y] <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> (s, [(<span style=\"color: #b00040\">VertexThread<\/span> (max x y), min x y)])\r\n            <span style=\"color: #008000;font-weight: bold\">_<\/span>      <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #d2413a;font-weight: bold\">error<\/span> <span style=\"color: #ba2121\">\"Unexpected number of responses\"<\/span>\r\n<\/pre>\n<\/div>\n<p>We start by <span style=\"color: #000080\">request<\/span>ing parents of edge vertices, continue by sending the responses using the\u00a0<span style=\"color: #000080\">respondParent<\/span>\u00a0primitive defined above, and finally <span style=\"color: #000080\">receive<\/span> and handle the responses in a way similar to <span style=\"color: #000080\">connect<\/span>.<\/p>\n<p>All the ingredients required for expressing Algorithm P are now in place:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">algorithmP<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Void<\/span>\r\n<span style=\"color: #0000ff\">algorithmP<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> repeat (parentConnect <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> update <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> shortcut)\r\n<\/pre>\n<\/div>\n<p>Great! My favourite algorithm from the paper is Algorithm R, which is a slight variation of Algorithm P:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">algorithmR<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #666666\">~&gt;<\/span> <span style=\"color: #b00040\">Void<\/span>\r\n<span style=\"color: #0000ff\">algorithmR<\/span> <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> repeat (parentConnect <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> rootUpdate <span style=\"color: #666666\">&gt;&gt;&gt;<\/span> shortcut)\r\n<\/pre>\n<\/div>\n<p>Here we use <span style=\"color: #000080\">rootUpdate<\/span> instead of <span style=\"color: #000080\">update<\/span>, whose only difference is that updates to non-root vertices are ignored. This makes the growth of the parent forest <em>monotonic<\/em>: we never exchange subtrees between trees and only graft whole trees to other trees. This greatly simplifies the\u00a0analysis of the performance of Algorithm R compared to Algorithm P. In fact, according to Tarjan, analysis of Algorithm P is still an open problem! I encourage you to read <a href=\"https:\/\/arxiv.org\/abs\/1812.06177\">the paper<\/a>, which presents five (!) algorithms for computing connected components concurrently, and also to implement the two remaining primitives, <span style=\"color: #000080\">extendedConnect<\/span> and <span style=\"color: #000080\">alter<\/span>, required for expressing Algorithms E, A and RA using our little modelling framework.<\/p>\n<p>Let&#8217;s check if our implementation of Algorithm P works correctly on the example graph:<\/p>\n<div style=\"background: #f8f8f8;overflow: auto;width: auto;padding: 0em;margin: 0em 0em 1em\">\n<pre style=\"margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">initialise<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Graph<\/span> <span style=\"color: #b00040\">Int<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #b00040\">Map<\/span> <span style=\"color: #b00040\">Thread<\/span> <span style=\"color: #b00040\">State<\/span>\r\n<span style=\"color: #0000ff\">initialise<\/span> g <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> <span style=\"color: #b00040\">Map<\/span><span style=\"color: #666666\">.<\/span>fromList (vs <span style=\"color: #666666\">++<\/span> es)\r\n  <span style=\"color: #008000;font-weight: bold\">where<\/span>\r\n    vs <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> [ (<span style=\"color: #b00040\">VertexThread<\/span> x, <span style=\"color: #b00040\">VertexState<\/span> x) <span style=\"color: #666666\">|<\/span> x      <span style=\"color: #aa22ff;font-weight: bold\">&lt;-<\/span> vertexList g ]\r\n    es <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> [ (<span style=\"color: #b00040\">EdgeThread<\/span> x y, <span style=\"color: #b00040\">EdgeState<\/span>    ) <span style=\"color: #666666\">|<\/span> (x, y) <span style=\"color: #aa22ff;font-weight: bold\">&lt;-<\/span> edgeList   g ]\r\n\r\n<span style=\"color: #0000ff\">run<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Global<\/span> s t <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #b00040\">Void<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #b00040\">Map<\/span> t s <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> <span style=\"color: #b00040\">Map<\/span> t s\r\n<span style=\"color: #0000ff\">run<\/span> g m <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> fst <span style=\"color: #666666\">$<\/span> g (m, <span style=\"color: #b00040\">[]<\/span>)\r\n\r\n<span style=\"color: #0000ff\">components<\/span> <span style=\"color: #aa22ff;font-weight: bold\">::<\/span> <span style=\"color: #b00040\">Map<\/span> <span style=\"color: #b00040\">Thread<\/span> <span style=\"color: #b00040\">State<\/span> <span style=\"color: #aa22ff;font-weight: bold\">-&gt;<\/span> [(<span style=\"color: #b00040\">Int<\/span>, [<span style=\"color: #b00040\">Int<\/span>])]\r\n<span style=\"color: #0000ff\">components<\/span> m <span style=\"color: #aa22ff;font-weight: bold\">=<\/span> groupSort\r\n    [ (p, x) <span style=\"color: #666666\">|<\/span> (<span style=\"color: #b00040\">VertexThread<\/span> x, <span style=\"color: #b00040\">VertexState<\/span> p) <span style=\"color: #aa22ff;font-weight: bold\">&lt;-<\/span> <span style=\"color: #b00040\">Map<\/span><span style=\"color: #666666\">.<\/span>toList m ]\r\n\r\n\u03bb<span style=\"color: #666666\">&gt;<\/span> mapM_ print <span style=\"color: #666666\">$<\/span> components <span style=\"color: #666666\">$<\/span> run algorithmP <span style=\"color: #666666\">$<\/span> initialise example\r\n(<span style=\"color: #666666\">1<\/span>,[<span style=\"color: #666666\">1<\/span>,<span style=\"color: #666666\">2<\/span>,<span style=\"color: #666666\">3<\/span>,<span style=\"color: #666666\">4<\/span>,<span style=\"color: #666666\">5<\/span>,<span style=\"color: #666666\">6<\/span>,<span style=\"color: #666666\">7<\/span>,<span style=\"color: #666666\">8<\/span>,<span style=\"color: #666666\">9<\/span>,<span style=\"color: #666666\">10<\/span>,<span style=\"color: #666666\">11<\/span>,<span style=\"color: #666666\">13<\/span>,<span style=\"color: #666666\">15<\/span>,<span style=\"color: #666666\">18<\/span>,<span style=\"color: #666666\">22<\/span>,<span style=\"color: #666666\">23<\/span>,<span style=\"color: #666666\">24<\/span>,<span style=\"color: #666666\">25<\/span>,<span style=\"color: #666666\">27<\/span>,<span style=\"color: #666666\">29<\/span>,<span style=\"color: #666666\">31<\/span>,<span style=\"color: #666666\">32<\/span>,<span style=\"color: #666666\">35<\/span>,<span style=\"color: #666666\">38<\/span>])\r\n(<span style=\"color: #666666\">12<\/span>,[<span style=\"color: #666666\">12<\/span>,<span style=\"color: #666666\">14<\/span>,<span style=\"color: #666666\">16<\/span>,<span style=\"color: #666666\">17<\/span>])\r\n(<span style=\"color: #666666\">19<\/span>,[<span style=\"color: #666666\">19<\/span>,<span style=\"color: #666666\">20<\/span>,<span style=\"color: #666666\">21<\/span>,<span style=\"color: #666666\">26<\/span>,<span style=\"color: #666666\">28<\/span>,<span style=\"color: #666666\">30<\/span>,<span style=\"color: #666666\">33<\/span>,<span style=\"color: #666666\">34<\/span>,<span style=\"color: #666666\">36<\/span>,<span style=\"color: #666666\">37<\/span>])\r\n<\/pre>\n<\/div>\n<p>As expected, there are three components with roots <span style=\"color: #000080\">1<\/span>, <span style=\"color: #000080\">12<\/span> and <span style=\"color: #000080\">19<\/span>, which matches the figure at the top of the blog post.<\/p>\n<p>And here is an animation of how Algorithm R builds up the parent forest in a monotonic manner. Note that the edges in the parent forest are now shown as undirected (unlike in the earlier depth-first search figure) since they may change their logical direction during the execution of the algorithm (e.g. see the edge <span style=\"color: #000080\">4-9<\/span>).<\/p>\n<p><a href=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/ccc-forest.gif\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/files\/2019\/09\/ccc-forest.gif\" alt=\"Parent forest built by Algorithm R\" width=\"630\" \/><\/a><\/p>\n<p>That&#8217;s all for now! I hope to find time soon to try these algorithms on <a href=\"https:\/\/github.com\/POETSII\/tinsel\">Tinsel<\/a>, a multi-FPGA hardware platform with thousands of processors connected by a fast network. Tinsel is being developed at the University of Cambridge as part of the <a href=\"https:\/\/poets-project.org\/about\/\">POETS research project<\/a>, which I worked on while at Newcastle University. Tinsel is very cool! Have a look at it if you are into FPGAs and unconventional computing architectures.<\/p>\n<p>P.S.: If you haven&#8217;t heard about the Heidelberg Laureate Forum before, you can read about it <a href=\"https:\/\/www.heidelberg-laureate-forum.org\/about-us.html\">here<\/a>. If you are a young researcher in computer science or mathematics, you should consider applying &#8212; it&#8217;s an amazing event where you get a chance to talk to the leaders of the two fields, as well as to young researchers from all over the world. This year I&#8217;m here as part of the HLF blogging team, writing about people I met and things I learned here, and it&#8217;s been a lot of fun too &#8212; if you&#8217;d like to help cover and promote this event, get in touch with the <a href=\"mailto:media@heidelberg-laureate-forum.org\">HLF media team<\/a>.<\/p>\n<p>P.P.S.: And here are a couple of links: (i) a <a href=\"https:\/\/www.youtube.com\/watch?v=jKtLiqb1JvI\">video recording<\/a> of Tarjan&#8217;s HLF lecture and (ii) the complete <a href=\"https:\/\/gist.github.com\/snowleopard\/bad0f1591a2978354336ecb8631024a1\">source code<\/a> from this blog post.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Computing\u00a0connected components in an undirected graph is one of the most basic graph problems. Given a graph with n vertices and m edges, you can find its components in linear time O(n + m) using\u00a0depth-first or breadth-first search. But what if you need to go faster? In this blog post, I will describe a cool &hellip; <a href=\"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/connected-components\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Connected Components, Concurrently<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1174,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,6,12],"tags":[16,4],"class_list":["post-1233","post","type-post","status-publish","format-standard","hentry","category-algorithms","category-coding","category-research","tag-graphs","tag-haskell"],"_links":{"self":[{"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/posts\/1233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/users\/1174"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/comments?post=1233"}],"version-history":[{"count":39,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/posts\/1233\/revisions"}],"predecessor-version":[{"id":1278,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/posts\/1233\/revisions\/1278"}],"wp:attachment":[{"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/media?parent=1233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/categories?post=1233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.ncl.ac.uk\/andreymokhov\/wp-json\/wp\/v2\/tags?post=1233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}