Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Implement bridge #1105

Merged
merged 17 commits into from
Jan 18, 2019
Merged

Implement bridge #1105

merged 17 commits into from
Jan 18, 2019

Conversation

piever
Copy link
Contributor

@piever piever commented Jan 9, 2019

This implements a function to compute all bridges of a graph. My main doubt is what should be the output format of this (returning a subset of edges). I have opted for an array of CartesianIndex with the indices of the edges (which is what one generally gets when doing findall on a matrix), but maybe simply a list of Tuple or a tuple of arrays would make more sense.

I also plan to implement "edge-biconnected components" but I think that can be done in a separate PR.

@sbromberger
Copy link
Owner

What do you mean by "indices of the edges"? We don't have edge indices in LG.

@jpfairbanks
Copy link
Contributor

Based on the code, it looks like edge indices means the pair of vertices (u,v).

push!(state.bridges, (v, w)) I like idea of thinking of an edge as a Cartesian index, but we don't do it anywhere else so I think returning a Vector{Edge} is the right answer for now.

@codecov
Copy link

codecov bot commented Jan 10, 2019

Codecov Report

Merging #1105 into master will increase coverage by <.01%.
The diff coverage is 95.23%.

@@            Coverage Diff             @@
##           master    #1105      +/-   ##
==========================================
+ Coverage   93.86%   93.87%   +<.01%     
==========================================
  Files          90       91       +1     
  Lines        4354     4375      +21     
==========================================
+ Hits         4087     4107      +20     
- Misses        267      268       +1

@piever
Copy link
Contributor Author

piever commented Jan 10, 2019

Ok, I've replaced CartesianIndex(u, v) with Edge(u, v). I thought it was nice to be able to use the result directly as indices in a square matrix, but using Edge is definitely more consistent with the rest of the package.

One more question: if I use the Edge, should I just add Edge(i, j) with i<j? I'm currently adding both directions but I think that's against the convention in the package. EDIT: have updated the PR to only add one edge per bridge.

@piever
Copy link
Contributor Author

piever commented Jan 10, 2019

It should be good to go/review on my side: the reduction in test coverage seems to be an artefact as the untested line is between two lines that are tested.

"""
bridge(g)
Compute the [bridges](https://en.m.wikipedia.org/wiki/Bridge_(graph_theory))
of a connected graph `g` and return an array containing all bridges.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this can be done in a single sentence, I think we should describe here what a bridge is.

Edge 1 => 2
```
"""
function bridge(g::AbstractGraph)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this bridges instead.
g should be restricted to undirected graphs, unless we can define what the bridge of a directed graph is.
Also, we should restrict g to integer eltypes here, as the function will not work otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bridge is a bit ambiguous for directed graphs, better to restrict the dispatch.

Concerning the name, I agree bridges makes more sense. I wanted to be consistent with articulation (which should maybe be articulations instead?), but maybe it's better to call this bridges and deprecate articulation to articulations in the future.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think articulation is shorthand for articulation_points. But I agree that this should be bridges.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, yes this should definitely be bridges.

I'm a bit unsure what's the correct dispatch, because it is not clear to me that all graphs types defined in various packages are subtypes of SimpleGraphs.AbstractSimpleGraph. Maybe easier to accept all AbstractGraph{<:Integer} and throw an argument error if the graph is directed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use the package SimpleTraits.jl for dispatching on directed/undirected graphs. For an example on how to do that, you can have a look at the file LightGraphs/src/community/cliques.jl. The function maximal_cliques is restricted to undirected graphs there.

I wouldn't bother with vertices that are not integers if there is not a simple and performant way to do such a thing. As far as I know, there is no package that uses such a graph yet and if there was, that person would have to implement many other functions from LightGraphs anyway, as they will not work out of the box.


function Bridges(g::AbstractGraph)
n = nv(g)
T = typeof(n)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use eltype(g) here.

state.depth[v] = state.id
state.low[v] = state.depth[v]

for w in outneighbors(g, v)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put an @inbounds in front of to the loop.

@simonschoelly
Copy link
Contributor

This recursive algorithm is great, but in the future we might have to replace it with a iterative one, as this code already gives me a stack overflow: bridge(PathGraph(5 * 10^4)).

Don't worry about coverage, that is broken at the moment.

@piever
Copy link
Contributor Author

piever commented Jan 11, 2019

This recursive algorithm is great, but in the future we might have to replace it with a iterative one, as this code already gives me a stack overflow: bridge(PathGraph(5 * 10^4)).

I see, if the depth first search graph is very deep things just fail... Ideally we could have a DFS iterator that one would use to code this algorithms? Maybe even a "Tarjan" iterator.

@sbromberger
Copy link
Owner

sbromberger commented Jan 11, 2019

Ideally we could have a DFS iterator that one would use to code this algorithms?

Well, we've played with that in the past but the problem is that calling generic visiting / opening / closing functions makes things horribly slow so we specialze the DFS for each algorithm that needs it. See
https://github.com/JuliaGraphs/LightGraphs.jl/blob/master/src/traversals/dfs.jl for some examples.

Edited to add: we've made a conscious decision in the past to remove functions that don't scale to large graphs. I don't see us adding any functions that can't handle, say, 1 million nodes / 100 million edges without crashing.

@jpfairbanks
Copy link
Contributor

I think that code was slow because of pre v0.6 implementation stuff. I think you could write a generic visitor code now and it wouldn't be slower. The old way was to pass the hooks in as arguments. But I think the new Julia 1.0 way is to define a VisitorType struct and then overload these functions for the api.

prehook(t::VisitorType, g, v) 
onhook(t::VisitorType, g, v) 
posthook(t::VisitorType, g, v) 

@sbromberger
Copy link
Owner

@jpfairbanks I'd be really interested in bringing back the traversal code if we can show that it's as fast. I'll open up an issue and maybe one of the GSOC candidates can use it to get familiar w/ our codebase.

@piever
Copy link
Contributor Author

piever commented Jan 11, 2019

Should have implemented most of the feedback. I'm really curious about the VisitorType struct for iteration (from a link in Slack there was a TarjanVisitor that got deleted - I suppose for performance reasons in older Julia - but it would have been handy), but I feel that belongs to a separate PR and when we have an efficient implementation of that I can see if I can simplify the code here.

@simonschoelly
Copy link
Contributor

I think this could be merged. @abhinavmehndiratta is working on an iterative version of the similar articulation algorithm and could maybe later transfer his code to this.

add newlines between signature and start of doc
@sbromberger sbromberger merged commit ee0db34 into sbromberger:master Jan 18, 2019
SohamTamba pushed a commit to SohamTamba/LightGraphs.jl that referenced this pull request Mar 2, 2019
* implement bridge computation

* added tests

* fix reference

* fix docstring

* change to Edge

* simplify test

* fix tests

* fix tests for real

* just add one edge

* remove unnecessary lines

* implement suggestions

* rename to bridges

* added check for undirectedness

* use dispatch on trait IsDirected

* update docs

* Update bridge.jl

add newlines between signature and start of doc
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants