-
Notifications
You must be signed in to change notification settings - Fork 66
Map Graph Recognition #922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -464,3 +464,39 @@ true | |||||
| </ManSection> | ||||||
|
|
||||||
| <#/GAPDoc> | ||||||
|
|
||||||
| <#GAPDoc Label="IsMapGraph"> | ||||||
| <ManSection> | ||||||
| <Attr Name="IsMapGraph" Arg="D"/> | ||||||
| <Returns><K>true</K> or <K>false</K>.</Returns> | ||||||
| <Description> | ||||||
| A map graph is a graph whose vertices can be represented as disjoint | ||||||
| regions in the plane, where two vertices are adjacent if the | ||||||
| corresponding regions share at least one point on their boundaries. | ||||||
| This is a generalized notion of planar graphs by allowing any number | ||||||
| of regions to meet at a single point rather than just along | ||||||
| borders of non-zero length. | ||||||
| <P/> | ||||||
| A graph <A>D</A> is a map graph if and only if it is the | ||||||
| <E>half-square</E> of some planar bipartite graph <M>H</M>. That is, | ||||||
| <A>D</A> is the graph formed by one part of the bipartition of <M>H</M>, | ||||||
| where two vertices in <A>D</A> are connected if they are at distance 2 in | ||||||
| <M>H</M>. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| Note that every planar graph is a map graph, but map graphs may contain | ||||||
| arbitrarily large cliques making them a strictly larger class than planar graphs. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be good to indicate something about the complexity of the implementation, it looks fairly high (unavoidably) so be good to indicate this. |
||||||
|
|
||||||
| <Example><![CDATA[ | ||||||
| gap> D := CompleteDigraph(5); | ||||||
| <immutable complete digraph with 5 vertices> | ||||||
| gap> IsPlanarDigraph(D); | ||||||
| false | ||||||
| gap> IsMapGraph(D); | ||||||
| true | ||||||
| gap> D := CompleteBipartiteDigraph(3, 7);; | ||||||
| gap> IsMapGraph(D); | ||||||
| false | ||||||
| ]]></Example> | ||||||
| </Description> | ||||||
| </ManSection> | ||||||
| <#/GAPDoc> | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -114,6 +114,231 @@ function(D) | |||||
| return DigraphByEdges(dualEdges); | ||||||
| end); | ||||||
|
|
||||||
| # A graph is a map graph if we can find a "witness" that is planar | ||||||
| # and bipartite that the original graph is a half-square of | ||||||
|
|
||||||
| # for all vertices v find all ways to cover its edges using a set of cliques | ||||||
| BindGlobal("DIGRAPHS_NbrCliqueCovers", | ||||||
| function(D, v) | ||||||
| local nbrs, sub, subEdges, maxCliques, mapping, cliqueCoverage, | ||||||
| covers, i, j, e, temp, EdgesCoveredBy, Backtrack; | ||||||
| nbrs := ShallowCopy(OutNeighboursOfVertex(D, v)); | ||||||
| nbrs := Filtered(nbrs, x -> x <> v); | ||||||
| Sort(nbrs); | ||||||
|
|
||||||
| if IsEmpty(nbrs) then | ||||||
| return [[]]; | ||||||
| fi; | ||||||
| sub := InducedSubdigraph(D, nbrs); | ||||||
|
|
||||||
| # gets the edges in the new graph | ||||||
| subEdges := []; | ||||||
| for i in DigraphVertices(sub) do | ||||||
| for e in OutNeighboursOfVertex(sub, i) do | ||||||
| if e > i then | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm struggling to understand why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intent is so that we don't double count edges([1,2] and [2,1]) ordering doesn't matter but edges are undirected(as a cant neighbor b without b also neighboring a)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, I'd still like to understand what algorithm your implementing but we can discuss it tomorrow, looks like there's great stuff in here! |
||||||
| Add(subEdges, [i, e]); | ||||||
| fi; | ||||||
| od; | ||||||
| od; | ||||||
|
|
||||||
| if IsEmpty(subEdges) then | ||||||
| return [List(nbrs, u -> [u])]; | ||||||
| fi; | ||||||
|
|
||||||
| maxCliques := List(DigraphMaximalCliques(sub), | ||||||
| cl -> List(cl, idx -> nbrs[idx])); | ||||||
|
|
||||||
| mapping := []; | ||||||
| for i in [1 .. Length(nbrs)] do | ||||||
| mapping[nbrs[i]] := i; | ||||||
| od; | ||||||
|
|
||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local functions that depend on the state of "global" variables, like |
||||||
| EdgesCoveredBy := function(clique) | ||||||
| local covered, a, b, ia, ib, edgeIdx; | ||||||
| covered := BlistList([1 .. Length(subEdges)], []); | ||||||
|
|
||||||
| for a in [1 .. Length(clique)] do | ||||||
| for b in [a + 1 .. Length(clique)] do | ||||||
| ia := mapping[clique[a]]; | ||||||
| ib := mapping[clique[b]]; | ||||||
|
|
||||||
| if ia > ib then | ||||||
| temp := ia; | ||||||
| ia := ib; | ||||||
| ib := temp; | ||||||
| fi; | ||||||
|
|
||||||
| edgeIdx := PositionSorted(subEdges, [ia, ib]); | ||||||
| if edgeIdx <= Length(subEdges) and subEdges[edgeIdx] = [ia, ib] then | ||||||
| covered[edgeIdx] := true; | ||||||
| fi; | ||||||
| od; | ||||||
| od; | ||||||
| return covered; | ||||||
| end; | ||||||
|
|
||||||
| cliqueCoverage := List(maxCliques, EdgesCoveredBy); | ||||||
|
|
||||||
| covers := []; | ||||||
|
|
||||||
| # find all edge covering clique sets | ||||||
| Backtrack := function(cover, uncovered, start) | ||||||
| local newUncovered, cov, touchedVerts, u, jj; | ||||||
|
|
||||||
| if not ForAny(uncovered, x -> x) then | ||||||
| touchedVerts := Union(cover); | ||||||
| cov := ShallowCopy(cover); | ||||||
|
|
||||||
| for u in nbrs do | ||||||
| if not u in touchedVerts then | ||||||
| Add(cov, [u]); | ||||||
| fi; | ||||||
| od; | ||||||
|
|
||||||
| Add(covers, cov); | ||||||
| return; | ||||||
| fi; | ||||||
|
|
||||||
| for i in [start .. Length(maxCliques)] do | ||||||
|
|
||||||
| if ForAny([1 .. Length(subEdges)], | ||||||
| jj -> uncovered[jj] and cliqueCoverage[i][jj]) then | ||||||
|
|
||||||
| newUncovered := ShallowCopy(uncovered); | ||||||
| for j in [1 .. Length(subEdges)] do | ||||||
| if cliqueCoverage[i][j] then | ||||||
| newUncovered[j] := false; | ||||||
| fi; | ||||||
| od; | ||||||
|
|
||||||
| Add(cover, maxCliques[i]); | ||||||
| Backtrack(cover, newUncovered, i + 1); | ||||||
| Remove(cover); | ||||||
| fi; | ||||||
| od; | ||||||
| end; | ||||||
|
|
||||||
| Backtrack([], BlistList([1 .. Length(subEdges)], | ||||||
| [1 .. Length(subEdges)]), 1); | ||||||
|
|
||||||
| if IsEmpty(covers) then | ||||||
| return [List(nbrs, u -> [u])]; | ||||||
| fi; | ||||||
|
|
||||||
| return covers; | ||||||
| end); | ||||||
|
|
||||||
| BindGlobal("DIGRAPHS_BuildWitness", | ||||||
| function(n, covers) | ||||||
| local witnessMap, nextWitness, outNeighb, v, clique, canon, key, w, u; | ||||||
|
|
||||||
| witnessMap := rec(); | ||||||
| nextWitness := n + 1; | ||||||
| outNeighb := List([1 .. n], i -> []); | ||||||
|
|
||||||
| for v in [1 .. n] do | ||||||
| for clique in covers[v] do | ||||||
| canon := ShallowCopy(clique); | ||||||
| if not v in canon then | ||||||
| Add(canon, v); | ||||||
| fi; | ||||||
| Sort(canon); | ||||||
| key := String(canon); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this will work in general, how is the key "123" interpreted? It can be generated by |
||||||
|
|
||||||
| if not IsBound(witnessMap.(key)) then | ||||||
| witnessMap.(key) := nextWitness; | ||||||
| Add(outNeighb, []); | ||||||
| nextWitness := nextWitness + 1; | ||||||
| fi; | ||||||
| w := witnessMap.(key); | ||||||
|
|
||||||
| for u in canon do | ||||||
| if not w in outNeighb[u] then | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless there's a reason to have the elements in |
||||||
| Add(outNeighb[u], w); | ||||||
| fi; | ||||||
| if not u in outNeighb[w] then | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same coment here |
||||||
| Add(outNeighb[w], u); | ||||||
| fi; | ||||||
| od; | ||||||
| od; | ||||||
| od; | ||||||
|
|
||||||
| for v in [1 .. Length(outNeighb)] do | ||||||
| Sort(outNeighb[v]); | ||||||
| od; | ||||||
|
|
||||||
| return DigraphImmutableCopy(Digraph(outNeighb)); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also an unnecessary copy |
||||||
| end); | ||||||
|
|
||||||
| # reconstruct graph from the planar bipartite graph | ||||||
| # for the inputted H we have U = [1...n] and V is the rest(all junctions) | ||||||
| BindGlobal("DIGRAPHS_HalfSquare", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be an attribute (or maybe an operation), for a bipartite graph, that has independent documentation and tests. It should probably take which set of vertices to use (instead of |
||||||
| function(H, n) | ||||||
| local adj, u, w, v, outgoing; | ||||||
|
|
||||||
| adj := List([1 .. n], i -> BlistList([1 .. n], [])); | ||||||
|
|
||||||
| for u in [1 .. n] do | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could replace this whole loop with |
||||||
| for w in OutNeighboursOfVertex(H, u) do | ||||||
| if w > n then | ||||||
| for v in OutNeighboursOfVertex(H, w) do | ||||||
| if v <= n and v <> u then | ||||||
| adj[u][v] := true; | ||||||
| fi; | ||||||
| od; | ||||||
| fi; | ||||||
| od; | ||||||
| od; | ||||||
|
|
||||||
| outgoing := List([1 .. n], u -> ListBlist([1 .. n], adj[u])); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also don't really see any advantage of using blists here, rather than just constructing |
||||||
| return DigraphImmutableCopy(Digraph(outgoing)); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is immutable by default and there's no sense in taking an additional copy here. |
||||||
| end); | ||||||
|
|
||||||
| BindGlobal("DIGRAPHS_IsMapGraphSearch", | ||||||
| function(G, n) | ||||||
| local allCovers, coverCounts, indices, assignment, H, | ||||||
| halfsq, i, v, targetEdges; | ||||||
|
|
||||||
| targetEdges := Set(DigraphEdges(G)); | ||||||
|
|
||||||
| allCovers := List([1 .. n], v -> DIGRAPHS_NbrCliqueCovers(G, v)); | ||||||
| coverCounts := List(allCovers, Length); | ||||||
|
|
||||||
| if ForAny(allCovers, IsEmpty) then | ||||||
| return false; | ||||||
| fi; | ||||||
|
|
||||||
| indices := List([1 .. n], i -> 1); | ||||||
|
|
||||||
| while true do | ||||||
| assignment := List([1 .. n], v -> allCovers[v][indices[v]]); | ||||||
| H := DIGRAPHS_BuildWitness(n, assignment); | ||||||
|
|
||||||
| if IsPlanarDigraph(H) then | ||||||
| halfsq := DIGRAPHS_HalfSquare(H, n); | ||||||
|
|
||||||
| if Set(DigraphEdges(halfsq)) = targetEdges then | ||||||
| return true; | ||||||
| fi; | ||||||
| fi; | ||||||
| i := n; | ||||||
| while i >= 1 do | ||||||
| if indices[i] < coverCounts[i] then | ||||||
| indices[i] := indices[i] + 1; | ||||||
| break; | ||||||
| else | ||||||
| indices[i] := 1; | ||||||
| i := i - 1; | ||||||
| fi; | ||||||
| od; | ||||||
| if i = 0 then | ||||||
| break; | ||||||
| fi; | ||||||
| od; | ||||||
|
|
||||||
| return false; | ||||||
| end); | ||||||
|
|
||||||
| ######################################################################## | ||||||
| # 2. Properties | ||||||
| ######################################################################## | ||||||
|
|
@@ -151,3 +376,31 @@ function(D) | |||||
| fi; | ||||||
| return IS_OUTER_PLANAR(D); | ||||||
| end); | ||||||
|
|
||||||
| InstallMethod(IsMapGraph, "for a digraph", [IsDigraph], | ||||||
| function(D) | ||||||
| local G, n, m; | ||||||
|
|
||||||
| if IsMultiDigraph(D) then | ||||||
| ErrorNoReturn("expected a digraph with no multiple edges"); | ||||||
| fi; | ||||||
|
|
||||||
| G := MaximalSymmetricSubdigraphWithoutLoops(D); | ||||||
|
|
||||||
| n := DigraphNrVertices(G); | ||||||
| m := DigraphNrAdjacenciesWithoutLoops(G); | ||||||
|
|
||||||
| if n < 5 or m < 9 then | ||||||
| return true; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. be good to combine these |
||||||
| fi; | ||||||
|
|
||||||
| if m > 2 * (6 * n - 12) then | ||||||
| return false; | ||||||
| fi; | ||||||
|
|
||||||
| if IsPlanarDigraph(G) then | ||||||
| return true; | ||||||
| fi; | ||||||
|
|
||||||
| return DIGRAPHS_IsMapGraphSearch(G, n); | ||||||
| end); | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.