Skip to content
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

Algorithm: Greedy feedback arc set #386

Merged
merged 16 commits into from
May 12, 2021
Merged

Algorithm: Greedy feedback arc set #386

merged 16 commits into from
May 12, 2021

Conversation

tobz1000
Copy link
Contributor

@tobz1000 tobz1000 commented Dec 31, 2020

Obtains a feedback arc set of a graph which is both decently small and has good time complexity.

This procedure involves the creation of a 64-bit-indexed graph mirroring the input graph, which may somewhat negate the memory advantages of 32-bit for huge graphs (I couldn't find a better way to support but 32-bit- and 64-bit-indexed graphs).

Credit for finding the algorithm used here goes to @steffahn in this discussion.

Copy link
Member

@ABorgna ABorgna left a comment

Choose a reason for hiding this comment

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

Hi, thanks for this. Can you rebase, so that it runs the tests with the new CI?

The paper uses a vector of doubly linked lists to find node candidates, so it avoids the linear search in your implementation.
It would be nice to implement that, but if you prefer we can merge this PR as-is and optimize it afterwards.

On that vein, we could use a small benchmark to test future changes.

src/feedback_arc_set.rs Outdated Show resolved Hide resolved
tests/quickcheck.rs Outdated Show resolved Hide resolved
src/feedback_arc_set.rs Outdated Show resolved Hide resolved
@tobz1000
Copy link
Contributor Author

tobz1000 commented May 3, 2021

Hi, thanks for this. Can you rebase, so that it runs the tests with the new CI?

Rebase was conflicting so I've merged master; I hope that's okay.

The paper uses a vector of doubly linked lists to find node candidates, so it avoids the linear search in your implementation.
It would be nice to implement that, but if you prefer we can merge this PR as-is and optimize it afterwards.

Yeah, I would like to try this, good spot. I didn't read the paper thoroughly at the time. I still don't understand how the bin sort makes it "trivial" to find the next candidate node when constructing the node sequence 😅. But I'll have a play and hopefully figure it out.

@ABorgna
Copy link
Member

ABorgna commented May 3, 2021

They store a cursor into the corresponding linked list for each node so they can delete the nodes in constant time.

I think not even the experimental cursor API for std's linked lists supports doing that, you'll have to roll your own.
You can pack the nodes directly into a Vec<LLNode> indexed by the SeqGraphIxs to avoid the cursor indirection.

Replaced the stable graph clone with a bespoke model which sorts nodes into linked list buckets by their delta degree. This improves time complexity.

Removced stable_graph feature dependency.
@tobz1000
Copy link
Contributor Author

tobz1000 commented May 9, 2021

The new changeset is a lot more to review than the original, sorry 😁. This is mostly due to the linked list implementation.

I've added benchmarks and tested them against the original - the improvement is mostly good. It's a bit slower than before when there aren't many edges, but the time complexity is clearly better for many edges.

Tournament |E| = O(|V|²)

nodes old new
10 3us 8us
50 0.16ms 0.16ms
100 1.8ms 0.6ms
200 15 ms 2.1ms
300 58ms 4.5ms

Fan Graph |E| = O(|V|)

nodes old new
10 2us 4us
50 9us 20us
200 54us 75us
500 0.24ms 0.18ms
1000 1ms 0.37ms

Copy link
Member

@ABorgna ABorgna left a comment

Choose a reason for hiding this comment

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

This is great :)
Seems that time constants are a bit higher, but times are much better for non-tiny inputs.

I just have a nitpick, but it can be merged as-is.

src/feedback_arc_set.rs Outdated Show resolved Hide resolved
src/feedback_arc_set.rs Show resolved Hide resolved
@tobz1000
Copy link
Contributor Author

I've tried trimming the bucket lists (hopefully this is similar to what you tried): tobz1000@266f979

The benchmark changes are within a margin of error to me it seems - but I've observed that it occasionally trims 5 or so empty lists off at once with the test graphs. I suppose for some topologies these trims might be even larger, and it might cause a non-negligible performance improvement. Up to you if you want to merge that change as well.

Thank you for reviewing this and giving some great input :)

@ABorgna
Copy link
Member

ABorgna commented May 12, 2021

It doesn't seem to add a cost and may be useful for some graphs as you said, so lets add it to this branch and I'll merge the PR.
Thanks again !

@ABorgna ABorgna merged commit 9dc94b6 into petgraph:master May 12, 2021
teuron pushed a commit to teuron/petgraph that referenced this pull request Oct 9, 2022
* Initial implementation & single test

* Add test for feedback arc set upper bound on "tournament" graph

* Add constraint for directed graphs only, refactor some types, update docs

* Allows 64-bit or 32-bit input graph index size

The sequence source graph is now always 64-bit however, negating the memory size advantages of 32-bit input graph index.

* Update doc, add example

* Hide behind "stable_graph" feature

* Minor PR suggestions

* Add benches

* "degree delta" buckets refactor

Replaced the stable graph clone with a bespoke model which sorts nodes into linked list buckets by their delta degree. This improves time complexity.

Removced stable_graph feature dependency.

* Add back feature gate for doctest

* adjust some benchmark data sizes

* Use Vec::resize_with

* trim bucket lists after removing bidirectional node
teuron pushed a commit to teuron/petgraph that referenced this pull request Oct 9, 2022
* Initial implementation & single test

* Add test for feedback arc set upper bound on "tournament" graph

* Add constraint for directed graphs only, refactor some types, update docs

* Allows 64-bit or 32-bit input graph index size

The sequence source graph is now always 64-bit however, negating the memory size advantages of 32-bit input graph index.

* Update doc, add example

* Hide behind "stable_graph" feature

* Minor PR suggestions

* Add benches

* "degree delta" buckets refactor

Replaced the stable graph clone with a bespoke model which sorts nodes into linked list buckets by their delta degree. This improves time complexity.

Removced stable_graph feature dependency.

* Add back feature gate for doctest

* adjust some benchmark data sizes

* Use Vec::resize_with

* trim bucket lists after removing bidirectional node
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants