-
Notifications
You must be signed in to change notification settings - Fork 46
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
Adds MaximalCommonSubdigraph and MinimalCommonSupergraph #334
Changes from all 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 | ||||
---|---|---|---|---|---|---|
|
@@ -678,3 +678,135 @@ function(D, t) | |||||
n := DigraphNrVertices(D); | ||||||
return IsDigraphColouring(D, ImageListOfTransformation(t, n)); | ||||||
end); | ||||||
|
||||||
InstallMethod(MaximalCommonSubdigraph, "for a pair of digraphs", | ||||||
[IsDigraph, IsDigraph], | ||||||
function(A, B) | ||||||
local D1, D2, MPG, Clqus, M, i, j, l, vertices, edges, n, | ||||||
m, adj, embedding1, embedding2, iso, A1, A2, intto4tuple, t; | ||||||
|
||||||
D1 := DigraphImmutableCopy(A); | ||||||
D2 := DigraphImmutableCopy(B); | ||||||
|
||||||
# If the digraphs are isomorphic then we return the first one as the answer | ||||||
iso := IsomorphismDigraphs(D1, D2); | ||||||
if iso <> fail then | ||||||
return [D1, IdentityTransformation, AsTransformation(iso)]; | ||||||
fi; | ||||||
|
||||||
n := DigraphNrVertices(D1); | ||||||
m := DigraphNrVertices(D2); | ||||||
A1 := AdjacencyMatrix(D1); | ||||||
A2 := AdjacencyMatrix(D2); | ||||||
|
||||||
# The algorithm works as follows: We construct the modular product digraph | ||||||
# MPG (see https://en.wikipedia.org/wiki/Modular_product_of_graphs for the | ||||||
# undirected version) a maximal partial isomorphism between D1 and D2 is | ||||||
# equal to a maximal clique this digraph. We then search for cliques using the | ||||||
# DigraphMaximalCliquesReps function. | ||||||
|
||||||
# we represent an element of V(D1)xV(D2)xV(D1)xV(D2) as an element of | ||||||
# [0 .. (n x m x n x m) - 1] | ||||||
intto4tuple := function(i) | ||||||
local t, tempi; | ||||||
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 could be more compact as:
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. But with things properly aligned... |
||||||
t := [0, 0, 0, QuoInt(i, n * m * n)]; | ||||||
tempi := RemInt(i, n * m * n); | ||||||
t[3] := QuoInt(tempi, n * m); | ||||||
tempi := RemInt(i, n * m); | ||||||
t[2] := QuoInt(tempi, n); | ||||||
t[1] := RemInt(i, n); | ||||||
return t; | ||||||
end; | ||||||
|
||||||
# As we are only concerned with cliques we don't need to consider the isolated | ||||||
# vertices of MPG so we constrict it without them | ||||||
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
|
||||||
|
||||||
edges := List([1 .. n * m], x -> []); | ||||||
for i in [0 .. (n * m * n * m - 1)] do | ||||||
t := intto4tuple(i); | ||||||
# not that we are only concerned with cliques so we can ignore edges | ||||||
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
|
||||||
# if we don't have their corresponding reverse edges | ||||||
if t[1] <> t[3] and t[2] <> t[4] and | ||||||
A1[t[1] + 1][t[1] + 1] = A2[t[2] + 1][t[2] + 1] and | ||||||
A1[t[3] + 1][t[3] + 1] = A2[t[4] + 1][t[4] + 1] and | ||||||
A1[t[1] + 1][t[3] + 1] = A2[t[2] + 1][t[4] + 1] and | ||||||
A1[t[3] + 1][t[1] + 1] = A2[t[4] + 1][t[2] + 1] then | ||||||
Add(edges[t[1] + t[2] * n + 1], t[3] + t[4] * n); | ||||||
fi; | ||||||
od; | ||||||
|
||||||
vertices := Filtered([0 .. n * m - 1], x -> edges[1 + x] <> []); | ||||||
|
||||||
# In the case that the modular product digraph has no edges, we attempt | ||||||
# to find a vertex in the second digraph with the same number of loops | ||||||
# as one in the first | ||||||
if vertices = [] then | ||||||
for i in DigraphVertices(D1) do | ||||||
for j in DigraphVertices(D2) do | ||||||
if AdjacencyMatrix(D1)[i][i] = AdjacencyMatrix(D2)[j][j] then | ||||||
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 assign the variables |
||||||
return [Digraph([ListWithIdenticalEntries(AdjacencyMatrix(D1)[i][i], | ||||||
1)]), | ||||||
Transformation([1], [i]), Transformation([1], [j])]; | ||||||
fi; | ||||||
od; | ||||||
od; | ||||||
return [Digraph([]), IdentityTransformation, IdentityTransformation]; | ||||||
fi; | ||||||
|
||||||
# We now build the modular product graph | ||||||
adj := function(v, w); | ||||||
return w in edges[v + 1]; | ||||||
end; | ||||||
MPG := Digraph(vertices, adj); | ||||||
|
||||||
# We find a big clique | ||||||
Clqus := DigraphMaximalCliquesReps(MPG); | ||||||
M := 1; | ||||||
for l in [1 .. Size(Clqus)] do | ||||||
if Size(Clqus[l]) > Size(Clqus[M]) then | ||||||
M := l; | ||||||
fi; | ||||||
od; | ||||||
|
||||||
embedding1 := List(Clqus[M], x -> intto4tuple(vertices[x])[1] + 1); | ||||||
embedding2 := List(Clqus[M], x -> intto4tuple(vertices[x])[2] + 1); | ||||||
return [InducedSubdigraph(D1, embedding1), | ||||||
Transformation([1 .. Size(embedding1)], embedding1), | ||||||
Transformation([1 .. Size(embedding2)], embedding2)]; | ||||||
|
||||||
end); | ||||||
|
||||||
InstallMethod(MinimalCommonSuperdigraph, "for a pair of digraphs", | ||||||
[IsDigraph, IsDigraph], | ||||||
function(D1, D2) | ||||||
local out, L, v, e, embfunc, embedding1, embedding2, newvertices; | ||||||
L := MaximalCommonSubdigraph(D1, D2); | ||||||
L[2] := List([1 .. DigraphNrVertices(L[1])], x -> x ^ L[2]); | ||||||
L[3] := List([1 .. DigraphNrVertices(L[1])], x -> x ^ L[3]); | ||||||
out := List(OutNeighbours(D1), x -> ShallowCopy(x)); | ||||||
newvertices := Filtered(DigraphVertices(D2), x -> not x in L[3]); | ||||||
embedding1 := [1 .. DigraphNrVertices(D1)]; | ||||||
|
||||||
embfunc := function(v) | ||||||
if v in L[3] then | ||||||
return L[2][Position(L[3], v)]; | ||||||
fi; | ||||||
return Position(newvertices, v) + DigraphNrVertices(D1); | ||||||
end; | ||||||
embedding2 := List(DigraphVertices(D2), embfunc); | ||||||
|
||||||
for v in newvertices do | ||||||
Add(out, []); | ||||||
od; | ||||||
|
||||||
for e in DigraphEdges(D2) do | ||||||
if (not e[1] in L[3]) or (not e[2] in L[3]) then | ||||||
Add(out[embedding2[e[1]]], embedding2[e[2]]); | ||||||
fi; | ||||||
od; | ||||||
|
||||||
return [Digraph(out), Transformation([1 .. Size(embedding1)], embedding1), | ||||||
Transformation([1 .. Size(embedding2)], embedding2)]; | ||||||
|
||||||
end); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2453,6 +2453,48 @@ false | |
gap> IsDigraphEmbedding(ran, src, (), [2, 1], [1, 1, 2]); | ||
false | ||
|
||
# MaximalCommSubdigraph and MinimalCommonSuperDigraph | ||
gap> MaximalCommonSubdigraph(NullDigraph(0), CompleteDigraph(10)); | ||
[ <immutable empty digraph with 0 vertices>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(NullDigraph(0), CompleteDigraph(10)); | ||
[ <immutable digraph with 10 vertices, 90 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MaximalCommonSubdigraph(PetersenGraph(), CompleteDigraph(10)); | ||
[ <immutable digraph with 2 vertices, 2 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(PetersenGraph(), CompleteDigraph(10)); | ||
[ <immutable digraph with 18 vertices, 118 edges>, IdentityTransformation, | ||
Transformation( [ 1, 2, 11, 12, 13, 14, 15, 16, 17, 18, 11, 12, 13, 14, 15, | ||
16, 17, 18 ] ) ] | ||
gap> MaximalCommonSubdigraph(NullDigraph(10), CompleteDigraph(10)); | ||
[ <immutable empty digraph with 1 vertex>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(NullDigraph(10), CompleteDigraph(10)); | ||
[ <immutable digraph with 19 vertices, 90 edges>, IdentityTransformation, | ||
Transformation( [ 1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 11, 12, 13, 14, 15, | ||
16, 17, 18, 19 ] ) ] | ||
gap> MaximalCommonSubdigraph(CompleteDigraph(100), CompleteDigraph(100)); | ||
[ <immutable digraph with 100 vertices, 9900 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(CompleteDigraph(100), CompleteDigraph(100)); | ||
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. To be even more certain that these functions continue working in the future, perhaps there should be some tests that check not only equality of display, but also equality of objects, here? For example: gap> comm := MinimalCommonSuperdigraph(CompleteDigraph(100), CompleteDigraph(100));;
gap> comm[1] = CompleteDigraph(100);
true I'm not sure to what extent this is implemented in other test files, it may be overkill :) 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. good idea |
||
[ <immutable digraph with 100 vertices, 9900 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MaximalCommonSubdigraph(PetersenGraph(), | ||
> DigraphSymmetricClosure(CycleDigraph(5))); | ||
[ <immutable digraph with 5 vertices, 10 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(PetersenGraph(), | ||
> DigraphSymmetricClosure(CycleDigraph(5))); | ||
[ <immutable digraph with 10 vertices, 30 edges>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MaximalCommonSubdigraph(Digraph([[1, 1]]), Digraph([[1]])); | ||
[ <immutable empty digraph with 0 vertices>, IdentityTransformation, | ||
IdentityTransformation ] | ||
gap> MinimalCommonSuperdigraph(Digraph([[1, 1]]), Digraph([[1]])); | ||
[ <immutable multidigraph with 2 vertices, 3 edges>, IdentityTransformation, | ||
Transformation( [ 2, 2 ] ) ] | ||
|
||
# DIGRAPHS_UnbindVariables | ||
gap> Unbind(edges); | ||
gap> Unbind(epis); | ||
|
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.
Shouldn't the isomorphism check be done before
A
andB
are copied? After all what's the point of copying them if it's not necessary.