-
Notifications
You must be signed in to change notification settings - Fork 59
Add VertexConnectivity
#94
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?
Conversation
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.
A few more fairly minor comments. I've still not studied the algorithm in detail, to see whether you're implementing it correctly. Not sure whether I will. Maybe @james-d-mitchell knows more about the algorithm and could weigh in?
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.
This looks quite reasonable, apart from some design decisions that are likely rather costly (in terms of time and space). I'd like to see the code in this PR refactored to remove these things.
@james-d-mitchell Did @ffloresbrito address your changes? Is @ffloresbrito still involved in any GAP-related activity? |
de81d16
to
b27af13
Compare
@james-d-mitchell I have squashed and rebased this PR on the latest master branch, and fixed the CI errors, and pushed. Assuming that @ffloresbrito is no longer involved with this, we can now decide how to proceed with this PR (or whether just to just close it). On the whole, I'd say it's better to have a slow implementation than no implementation - it could always be a student project to improve or rewrite it. But that assumes that this implementation is correct, and it's been long enough that I don't remember if it is. So I'll review it again sometime. |
7671666
to
f26545c
Compare
5b55e83
to
7aa5e9d
Compare
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.
I accidentally marked a comment from James as resolved, but it's not. It basically said:
You should add a citation to the documentation, stating where this algorithm is taken from.
Looks like all of the specific changes that James asked for were made.
7aa5e9d
to
7e1f0d7
Compare
7e1f0d7
to
1f0981b
Compare
e21ba66
to
da6864e
Compare
da6864e
to
059d12b
Compare
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.
For us this method would be very helpful. I looked at the algorithm and the implementation and to me it looks like a useful implementation of vertex connectivity.
In my opinion two parts are written a bit awkwardly as you can see below. Is there anything else that needs to be done apart from adding a citation to the documentation?
gap/attr.gi
Outdated
mindeg := degs[i]; | ||
mindegv := i; | ||
fi; | ||
od; |
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.
I think mindegv:=PositionMinimum(degs)
andmindeg:=degs[mindegv]
should do the same and is an easier computation and easier to read.
degs[j] := degs[j] + 1; | ||
fi; | ||
od; | ||
od; |
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.
It should be also possible to compute degs with OutDegrees
and InDegrees
such that it is maybe shorter and easier to read.
059d12b
to
29a624d
Compare
Thanks for your review last year @MeikeWeiss. I'll have a look at reviewing and hopefully merging this, once and for all. For now I'll rebase on |
29a624d
to
6911bc7
Compare
6911bc7
to
45b3aeb
Compare
45b3aeb
to
f2118f0
Compare
The method currently seems to have a bug: testing: /tmp/gaproot/pkg/Digraphs/tst/standard/attr.tst
########> Diff in /tmp/gaproot/pkg/Digraphs/tst/standard/attr.tst:
3152
# Input is:
VertexConnectivity(D);
# Expected output:
4
# But found:
3
########
4903 ms (366 ms GC) and 1.16GB allocated for attr.tst The correct answer is indeed 4 (as proved by brute force, according to some additional lines that I've since added to the test). |
To add my 2 cents:
If the original digraph It follows from this theorem that, instead of computing the max-flow with source I'll have a crack at making these changes if thats ok with you @wilfwilson [1] https://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf |
Please do, @reiniscirpons! Be my guest. Thank you very much. What you describe sounds smart. |
Ok I think I fixed the bug. The new There are still a few questions that should be resolved (currently down as TODOs in the source), namely:
@wilfwilson could you give the code a review, when you have the chance? Thanks! |
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.
This is looking good, thank you for working on it.
Perhaps to avoid any weirdness we could only define vertex connectivity for digraphs with at least two vertices? (Or whatever the relevant number would be to avoid the weird cases).
I'm happy with the file having lengthy comments, they're harmless and potentially helpful!
I've got a few suggestions that I've commented directly on the code.
<A>digraph</A>. | ||
<P/> | ||
|
||
The weak vertex connectivity for a weakly connected digraph is the largest |
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.
The weak vertex connectivity for a weakly connected digraph is the largest | |
The weak vertex connectivity of a weakly connected digraph is the largest |
The weak vertex connectivity for a weakly connected digraph is the largest | ||
number <M>k</M> such that: | ||
<List> | ||
<Item> the digraph has at least <M>k + 1</M> vertices and</Item> |
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.
<Item> the digraph has at least <M>k + 1</M> vertices and</Item> | |
<Item> the digraph has at least <M>k + 1</M> vertices, and</Item> |
|
||
DeclareAttribute("CharacteristicPolynomial", IsDigraph); | ||
DeclareAttribute("NrSpanningTrees", IsDigraph); | ||
# TODO: Should we change the name to DigraphVertexConnectivity? |
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.
Yeah, I say so.
<A>digraph</A>. | ||
<P/> | ||
|
||
The weak vertex connectivity for a weakly connected digraph is the largest |
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.
If I remember correctly, we don't really use/prioritise the term "weakly connected" in the Digraphs package, we rather tend to just use the term "connected". (Please let me know if I'm wrong). So I'd suggest that you change it to this:
The weak vertex connectivity for a weakly connected digraph is the largest | |
The weak vertex connectivity of a connected digraph (in the sense of `<Ref Prop="IsConnectedDigraph"/>) is the largest |
<#Include Label="LaplacianMatrix"> | ||
</Section> | ||
|
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.
👍
digraph := DigraphRemoveAllMultipleEdges(digraph); | ||
digraph := DigraphRemoveLoops(digraph); | ||
digraph := DigraphSymmetricClosure(digraph); | ||
digraph := MakeImmutable(digraph); |
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.
digraph := DigraphRemoveAllMultipleEdges(digraph); | |
digraph := DigraphRemoveLoops(digraph); | |
digraph := DigraphSymmetricClosure(digraph); | |
digraph := MakeImmutable(digraph); | |
DigraphRemoveAllMultipleEdges(digraph); | |
DigraphRemoveLoops(digraph); | |
DigraphSymmetricClosure(digraph); | |
MakeImmutable(digraph); |
Save some bits 😉
# TODO: Do each check separately? Or work this into the doubled digraph | ||
# computation? |
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.
I think it's fine to keep this separate from the doubled digraph computation, for the purposes of keeping the code for that computation as understandable as possible.
But if you think merging would give a significant performance benefit, then I wouldn't object.
end); | ||
|
||
InstallMethod(VertexConnectivity, "for a digraph", [IsDigraph], | ||
function(digraph) |
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.
Please use D
as the variable name, it's what we (at one point) have tried to standardise upon in the library of this the package. (See also #868 where I've made the package more consistent again).
function(digraph) | |
function(D) |
adjacency_function := DigraphAdjacencyFunction(digraph); | ||
kappa_min := fail; | ||
for u in DigraphVertices(digraph) do | ||
if u <> v and not adjacency_function(v, u) then |
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.
Does the u <> v
check here mean that you can avoid removing all loops above? I haven't checked.
Also, I think the most efficient thing you can do in general is simply:
if u <> v and not adjacency_function(v, u) then | |
if u <> v and not u in neighbours_v then |
for j in [i + 1 .. Length(neighbours_v)] do | ||
u := neighbours_v[i]; | ||
v := neighbours_v[j]; | ||
if not adjacency_function(v, u) then |
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.
Probably the best you can do here is:
if not adjacency_function(v, u) then | |
if not IsDigraphEdge(D, v, u) then |
Although I'm not totally sure.
But this change would let you get rid of the line
adjacency_function := DigraphAdjacencyFunction(digraph);
above.
@reiniscirpons In response to your specific questions above, here are my answers:
multi := IsMultiDigraph(D);
loops := DigraphHasLoops(D);
nsymm := not IsSymmetricDigraph(D);
if multi or loops or nsymm then
D := DigraphMutableCopy(D);
if multi then
DigraphRemoveAllMultipleEdges(D);
fi;
if loops then
DigraphRemoveLoops(D);
fi;
if nsymm then
DigraphSymmetricClosure(D);
fi;
MakeImmutable(D);
fi;
|
Edit: ( @reiniscirpons ): the current implementation seems have some bug and it fails on
DigraphFromGraph6String("HoStIv{")
(this is Graph 33668 on house of graphs). The expected output is 4, currently the method gives 3. My hunch is that the bug may be in the Edmonds-Karp method. It seems PR #584 implements a max-flow method, so once this is merged, we may be able to fix this PR as well.Hopefully within the next 7 years we will be able to merge this PR!
Original comment:
This pull request adds an attribute called
VertexConnectivity
which for a graph G = (V, E) computes the least cardinality |S| of a subset S of V, such that G - S is either disconnected, is trivial, or has no vertices. Documentation and tests are included.