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

Add floyd_warshall_predecessor_and_distance #43

Merged

Conversation

eriknw
Copy link
Member

@eriknw eriknw commented Feb 7, 2023

This follows up on #42 to also compute predecessors with floyd_warshall.

CC @jim22k @SultanOrazbayev @LuisFelipeRamos

I came up with the algorithm for computing predecessors via trial and error--i.e., a lot of guessing :). The outcome seems reasonable/plausible, and it passes tests.

As the comments indicate, by adding the use of Mask, we increase memory usage, but it computes faster. I don't have a great feel for when we should trade memory for speed or vice versa. So, let's go with the simple option, which happens to be the faster option.

Also, instead of converting the output Matrix to a dict of dicts, I convert it to a new object NodeNodeMap that behaves similarly and is backed by the matrix. I updated other uses of fill_value with NodeMap to keep the output sparse (i.e., don't fill with fill_value).

I wonder how we can/should optimize for undirected graphs (i.e., symmetric adjacency matrix). Perhaps we can only compute the lower or upper triangular portion of the results while iterating.

Our original floyd_warshall was nice, because it looked a lot more like a typical floyd-warshall implementation. The new version that can also compute predecessors is nice b/c it's more capable without duplicating a bunch of code, but less nice b/c it's more complicated. Please let me know if anything in floyd_warshall_predecessor_and_distance can be more clear.

@eriknw eriknw requested a review from jim22k February 7, 2023 05:00
@codecov-commenter
Copy link

codecov-commenter commented Feb 7, 2023

Codecov Report

Base: 72.25% // Head: 69.57% // Decreases project coverage by -2.69% ⚠️

Coverage data is based on head (9388982) compared to base (6dd93bd).
Patch coverage: 32.03% of modified lines in pull request are covered.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #43      +/-   ##
==========================================
- Coverage   72.25%   69.57%   -2.69%     
==========================================
  Files          72       72              
  Lines        2617     2777     +160     
  Branches      479      516      +37     
==========================================
+ Hits         1891     1932      +41     
- Misses        557      675     +118     
- Partials      169      170       +1     
Impacted Files Coverage Δ
...blas_algorithms/algorithms/shortest_paths/dense.py 7.93% <8.51%> (-8.20%) ⬇️
graphblas_algorithms/classes/nodemap.py 42.12% <28.44%> (-7.61%) ⬇️
graphblas_algorithms/nxapi/shortest_paths/dense.py 45.45% <33.33%> (-11.69%) ⬇️
graphblas_algorithms/classes/_utils.py 63.63% <56.52%> (-1.52%) ⬇️
graphblas_algorithms/classes/digraph.py 40.00% <100.00%> (+0.29%) ⬆️
graphblas_algorithms/classes/graph.py 59.54% <100.00%> (+0.37%) ⬆️
graphblas_algorithms/interface.py 97.50% <100.00%> (+0.03%) ⬆️
...raphblas_algorithms/nxapi/centrality/degree_alg.py 100.00% <100.00%> (ø)
graphblas_algorithms/nxapi/cluster.py 77.55% <100.00%> (-0.67%) ⬇️
...aphblas_algorithms/nxapi/link_analysis/hits_alg.py 100.00% <100.00%> (ø)
... and 2 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Comment on lines 63 to 68
if is_directed:
Mask << indexunary.offdiag(Outer)
else:
Mask << indexunary.triu(Outer, 1)
Mask(binary.second) << binary.lt(Outer & D)
Outer(Mask.V, replace) << Outer
Copy link
Member Author

Choose a reason for hiding this comment

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

An alternative is

        Mask << binary.ge(Outer & D)
        if is_directed:
            Outer(~Mask.V, replace) << select.offdiag(Outer)
        else:
            Outer(~Mask.V, replace) << select.triu(Outer, 1)

which may be preferred, because this keeps Mask smaller. However, I think complemented masks with replace is potentially confusing.

@eriknw
Copy link
Member Author

eriknw commented Feb 7, 2023

I wonder how we can/should optimize for undirected graphs (i.e., symmetric adjacency matrix). Perhaps we can only compute the lower or upper triangular portion of the results while iterating.

Implemented (via upper triangles)! This wasn't too hard or invasive to do, but it does make the implementation a little more complicated. We still symmetrize the results at the end--maybe someday we'll be able to avoid this step.

Comment on lines +59 to +65
if not compute_predecessors:
# It is faster (approx 10%-30%) to use a mask as is done below when computing
# predecessors, but we choose to use less memory here by not using a mask.
if is_directed:
D(binary.min) << select.offdiag(Outer)
else:
D(binary.min) << select.triu(Outer, 1)
Copy link
Member Author

Choose a reason for hiding this comment

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

I added the core of the original implementation back in for when we only compute distances. Even though this is consistently slower, it should use less memory, which I think is worth it, b/c floyd_warshall is memory-intensive.

@eriknw eriknw merged commit 0b649b2 into python-graphblas:main Feb 8, 2023
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.

2 participants