{"id":17262574,"url":"https://github.com/patmorin/lhp","last_synced_at":"2025-03-26T10:24:37.394Z","repository":{"id":139814989,"uuid":"281830409","full_name":"patmorin/lhp","owner":"patmorin","description":"An implementation of layered H-partitions","archived":false,"fork":false,"pushed_at":"2021-01-06T11:14:43.000Z","size":1152,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-31T11:50:00.933Z","etag":null,"topics":["layered-h-partition","planar-graph","product-structure-theorem","tripod-partition"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patmorin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-23T02:28:22.000Z","updated_at":"2021-08-26T15:45:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"3d1779bd-f2ef-44fc-959a-8a386ae6f178","html_url":"https://github.com/patmorin/lhp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patmorin%2Flhp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patmorin%2Flhp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patmorin%2Flhp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patmorin%2Flhp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patmorin","download_url":"https://codeload.github.com/patmorin/lhp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245633804,"owners_count":20647458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["layered-h-partition","planar-graph","product-structure-theorem","tripod-partition"],"created_at":"2024-10-15T07:54:10.384Z","updated_at":"2025-03-26T10:24:37.364Z","avatar_url":"https://github.com/patmorin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# An Implementation of the Product Structure Theorem for Planar Graphs\n\nAn implementation of layered H-partitions, a.k.a, the Product Structure Theorem for planar graphs.  This implements the algorithm described in [arXiv:2004.02530](https://arxiv.org/abs/2004.02530).\n\n# lhp.py\n\n`lhp.py` is a module that includes the `tripod_partition` class and is also a standalone command-line program.\n\n## The `tripod_partition` class\n\nThe useful thing for programmers is the `tripod_partition` class, whose constructor requires an embedding of a planar triangulation *G* with vertex set \\{0,..,*n*-1\\} and a list of three vertices `outer_face` that lists the vertices of some face in counterlockwise order.\n\nThe triangulation *G* must be described as a list `succ` of length *n*. The list entry `succ[i]` is a dictionary that maps each neighbour *j* of *i* onto the neighbour *k* of *i* that appears immediately after *j* when ordering the neighbours of *i* in counterclockwise order around *i*.  Specifically, `(i,j,k)` is a triangular face of *G*.  For any directed edge `ij`, `succ[i][j]` is the third vertex of the face to the left of `ij`.\n\nIf you have a standard adjacency list representation of *G* you can use the following function to convert it to the formatted needed by the `tripod_partition` constructor:\n\n    def al2succ(al):\n        succ = list()\n        for neighbours in al:\n            succ.append(dict())\n            for i in range(len(neighbours)):\n                succ[-1][neighbours[i]] = neighbours[(i+1)%len(neighbours)]\n        return succ\n\n\nAfter constructing it, the tripod partition has several parts:\n\n- `t`: This is a BFS tree rooted at `roots`, stored in an adjacency list representation.  This tree is represented as a list of length *n*. For each `i` in \\{0,...,`n`-1\\}, `t[i]` is the list of nodes adjacent to `i` beginning with the parent node, so `t[i][0]` is the parent of `i` in the BFS tree.  The parent of each outer face node is `-1`, so `t[j][0] = -1` for each j in `outer_face`.\n- `tripods`: This is a list of *closed tripods*.  Each tripod `tripods[t]` is a list of 3 vertical paths in the BFS tree T called the *legs* of `t`.  Each leg `tripods[t][i]` of each tripod contains a *foot* `tripods[t][i][-1]`.  For each `t\u003e0` each each `i` in \\{1,2,3\\}, the foot `tripods[t][i][-1]` appears\u0026mdash;not as a foot\u0026mdash;in exactly one other tripod, so  `tripods[t][i][-1]` is contained in `tripods[p][j][:-1]` for some `(p,j)`.  When this happens, `p` is called the *parent tripod* of `t`.  The only exception is the *root tripod* `tripods[0]`, for which `tripods[0][i][-1]=-1` for each `i` in \\{0,1,2\\}.  The *open* tripod `t` consists of the tripod `t` minus its three feet. The open tripods form a partition of the vertices of *G*, as do the legs of the open tripods; each vertex of *G* appears in exactly one leg of one open tripod.\n- `tripod_map`: This is a list of length *n* that maps each *v* vertex of *G* onto a triple `(ti,l,j)` where `ti` is the tripod that contains *v*, `l` is the leg that contains *v* and `j` is the location of *v* in this leg.  So, if `(ti,l,j) = tripod_map[v]` then `tripods[ti][l][j]=v`.\n- `tripod_tree`: This is a list of length `len(tripods)` that encodes a 3-ary tree whose nodes are tripods.  This tree has the property that `tripods[i][j][:-1]` (a vertical path in `t`) has no vertex adjacent to any open tripod in the subtree `tripod_tree[i][j]`.  (Leg *j* of the tripod is separated from all tripods contained in subtree *j*.)  A useful property of these tripods is that they are ordered by a preorder traversal of the tripod tree.  If tripod `a` is an ancestor of tripod `t`, then this makes it possible to know, in constant-time, which of the three subtrees of `a` contains `t`.\n\n## Tree decompositions of quotient graphs\n\nA `tripod_partition` induces two quotient graphs: The graph h3 is the graph obtained by contracting each open tripod. The graph h8 is the graph obtained by contracting each leg of each open tripod. The data members `tripod_tree`, `tripods`, and `tripod_map` can be used to obtain a width-3 tree-decomposition of h3 and a width-8 tree decomposition of h8. The `tripod_partition` class includes members functions for doing this:\n\n- `h3parents(t)`: returns a list of (at most 3) tripods that are the parents of t in a width-3 tree decomposition of h3.\n- `h8parents(t, i)`: returns a lsit of (at most 8) tripods that are the parents of leg i of tripod t in a width-8 tree decomposition of h8.\n\nThe numbering of tripods is such that `p\u003ct` for each `p` in `h3parents(t)` and `(p,j)\u003c(t,i)` for each `(p,j)` in `h3parents(t,i)`.  (Remember that Python does lexicographic comparison of tuples.) \n\nEach of these functions runs in constant time, but if you intend to do a lot of work with these decompositions, it is worthwhile saving them using something like:\n\n    h3p = [ h3parents(t) for t in range(len(tp.tripods) ]\n    h8p = [ h8parents(t,i) for (t,i) in itertools.product(range(len(tp.tripods),range(3)) ]\n\n(In the second case, `h8parents(t,i)==h8p[3*t+i]`.)\n\n## Standalone program\n\nThe lhp.py module can also be used as a standalone program that reads a triangulation from stdin and outputs a list of tripods to stdout.\n\nThe input represents a graph with vertex set 0,..,.n-1.\n- Line 0 of the input is f = 2*n - 4\n- Each of lines 1,...,f in the input is a triangular face\nThe vertices of each triangular face must be listed in counterclockwise order\n\nThe output represents the closed tripods in a tripod partition.\n- Line 0 is the number k of tripods\n- Lines 3i-2, 3i-1, 3i are the legs of tripod i (for each i in {1,...,k})\nEach leg of the tripod begins with a vertex of the Sperner triangle and ends at a vertex in one of the three parent tripods.\n\nIf f is of the form 2*n - 5, instead of 2*n - 4, then the program assumes that there is an additional face [0,1,2] or [2,1,0] that is missing and adds it. This makes it compatible with `qhull`. In particular, the following command line works:\n\n    rbox y 100 D2 | qhull d Qt i | python3 lhp.py\n\n# lhp_demo.py\n\nUnfortunately, this demo requires `scipy.spatial` (which uses `qhull`) for generating random Delaunay triangulations\n\n    ./lhp_demo.py -h\n    Computes a tripod decomposition of a Delaunay triangulation\n    Usage: ./lhp_demo.py [-h] [-c] [-r] [-y] [-w] [-b] [-nv] \u003cn\u003e\n      -h show this message\n      -c use collinear points\n      -y use random points in triangle\n      -r use random points in disk (default)\n      -w use O(n log n) time algorithm (default)\n      -b use O(n^2) time algorithm (usually faster)\n      -nv don't verify correctness of results\n      \u003cn\u003e the number of points to use (default = 10)\n\nIf *n* \u0026lt; 500 then this program will show the result in a matplotlib window, producing pictures that look like this:\n\n![tripod decomposition](figs/figure.png \"Tripod decomposition\")\n![tripod decomposition](figs/figure2.png \"Tripod decomposition\")\n![tripod decomposition](figs/figure3.png \"Tripod decomposition\")\n\nRun `lhp_demo.py -h` for a list of options\n\n# References\n\nFor more information on the Product Structure Theorem and the algorithm described here, see the following references:\n\n- [arxiv:1904.04791](https://arxiv.org/abs/1904.04791) introduces the tripod decomposition and uses it to solve an old problem on planar graphs.\n- [arxiv:1807.03683](https://arxiv.org/abs/1807.03683) introduces a slightly different tripod decomposition, on which the one described in the previous reference  is based.\n- [arxiv:2004.02530](https://arxiv.org/abs/2004.02530) describes the algorithm used in this implementation\n\nThe product structure theorem has been used to solve a number of problems on planar graphs, including [queue number](https://arxiv.org/abs/1904.04791), [nonrepetitive colouring](https://arxiv.org/abs/1904.05269), [adjacency labelling](https://arxiv.org/abs/2003.04280), [universal graphs](https://arxiv.org/abs/2010.05779), [vertex ranking](https://arxiv.org/abs/2007.06455) and [p-centered colouring](https://arxiv.org/abs/1907.04586).  You can probably find more on [Google Scholar](https://scholar.google.com/scholar?cites=16964377059594834981).\n\nThere are product structure theorems for generalizations of planar graphs, including [bounded-genus and apex-minor-free graphs](https://arxiv.org/abs/1904.04791) and [k-planar graphs](https://arxiv.org/abs/1907.05168).  Most of these ultimately rely on a subroutine computing the product structure of planar graphs like the one given in this implementation.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatmorin%2Flhp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatmorin%2Flhp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatmorin%2Flhp/lists"}