Skip to content

PNC k shortest simple path (for directed graphs) #40284

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

Merged
merged 10 commits into from
Jul 6, 2025

Conversation

kappybar
Copy link
Contributor

@kappybar kappybar commented Jun 22, 2025

Implement PNC k shortest simple path for directed graphs.
I implemented for only directed graphs for simplicity first.

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation and checked the documentation preview.

⌛ Dependencies

#40248
#40217

@dcoudert dcoudert added c: graph theory gsoc: 2025 Tag for GSoC2025 issues/PRs labels Jun 23, 2025
Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

Why is it necessary to define PathCandidate in a .h file ? can't it be done in file path_enumeration.pxd ?

@kappybar
Copy link
Contributor Author

Why is it necessary to define PathCandidate in a .h file ? can't it be done in file path_enumeration.pxd ?

Because I want to use priority_queue of C++ standard library, the type of PathCandidate must be struct of C++. Thus, I think I need definition of PathCandidate in .h file. However, There may be another way of doing so without .h file. I am looking for it.

@dcoudert
Copy link
Contributor

isn't it possible to use a tuple instead of PathCandidate ? or may be cdef priority_queue[pair[pair[double,bint], pair[int, int]]] heap_sorted_paths.
Priority should go to the path with smallest length and in case several paths have the same length, we want first the simple paths.

Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

also, the key to the paper should be ACN2023 (Al Zoobi, Coudert, Nisse)

@dcoudert
Copy link
Contributor

dcoudert commented Jun 24, 2025

something goes wrong in the algorithm. Paths are not reported in increasing order of weight

sage: g = DiGraph(graphs.Grid2dGraph(2, 6).relabel(inplace=False))
sage: for u, v in g.edge_iterator(labels=False):
....:     g.set_edge_label(u, v, 1)
sage: %time X = [w for w, P in pnc_k_shortest_simple_paths(g, 5, 1, by_weight=True, report_weight=True, labels=True, report_edges=True)]
CPU times: user 1.39 ms, sys: 0 ns, total: 1.39 ms
Wall time: 1.4 ms
sage: print(X)
[4.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 10.0, 10.0, 10.0, 10.0, 8.0]

@dcoudert
Copy link
Contributor

You should rebase the branch on the last beta.

If you do another commit, you can add the doi to the paper :doi:`10.1145/3626567`.

@dcoudert
Copy link
Contributor

It's not fully fixed

sage: from sage.graphs.path_enumeration import yen_k_shortest_simple_paths as yen
sage: from sage.graphs.path_enumeration import feng_k_shortest_simple_paths as feng
sage: from sage.graphs.path_enumeration import pnc_k_shortest_simple_paths as pnc
sage: D = graphs.Grid2dGraph(5, 5).relabel(inplace=False).to_directed()
sage: %time len(list(yen(D, 0, 24)))
CPU times: user 14 s, sys: 408 µs, total: 14 s
Wall time: 14.1 s
8512
sage: %time len(list(feng(D, 0, 24)))
CPU times: user 1.23 s, sys: 988 µs, total: 1.23 s
Wall time: 1.24 s
8512
sage: %time len(list(pnc(D, 0, 24)))
CPU times: user 427 ms, sys: 995 µs, total: 428 ms
Wall time: 428 ms
8530

Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

Something goes wrong. The method can yield non simple paths.

sage: from sage.graphs.path_enumeration import pnc_k_shortest_simple_paths as pnc
sage: D = graphs.Grid2dGraph(5, 5).relabel(inplace=False).to_directed()
sage: for P in pnc(D, 0, 24):
....:     if len(P) != len(set(P)):
....:         print(P)
....:         break
....: 
[0, 5, 10, 15, 16, 17, 18, 13, 12, 11, 6, 1, 2, 7, 8, 13, 14, 19, 24]

vertex 13 is repeated.

a path is returned. Otherwise a tuple of path length and path is
returned.

OUTPUT: iterator
Copy link
Contributor

Choose a reason for hiding this comment

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

this line is not useful. It can be removed.


ALGORITHM:

This algorithm is based on the `feng_k_shortest_simple_paths` algorithm
Copy link
Contributor

Choose a reason for hiding this comment

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

use the form the ``feng_k_shortest_simple_paths`` . A single ` is for math mode.

[4.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 8.0, 8.0,
8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 10.0, 10.0, 10.0, 10.0]

Same tests as yen_k_shortest_simple_paths::
Copy link
Contributor

Choose a reason for hiding this comment

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

Here as well, use ``yen_k_shortest_simple_paths``::


Same tests as yen_k_shortest_simple_paths::

sage: g = DiGraph([(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30)])
Copy link
Contributor

Choose a reason for hiding this comment

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

this first graph is not used.

Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

Let's try to make the doc build. It's currently failing.

deviations occur. See Postponed Node Classification algorithm in [ACN2023]_
for the algorithm description.

EXAMPLES:
Copy link
Contributor

Choose a reason for hiding this comment

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

for the EXAMPLES and TESTS blocks, use EXAMPLES:: and TESTS::, unless it is followed by sub blocks, which is not the case here.

G.delete_edges(G.incoming_edges(source, labels=False))
G.delete_edges(G.outgoing_edges(target, labels=False))

by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
Copy link
Contributor

Choose a reason for hiding this comment

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

Here you can use G. _get_weight_function.

# target vertex is not reachable from v
return -1
if v in ancestor_idx_dict:
if ancestor_idx_dict[v] <= t or ancestor_idx_dict[v] == len(path) - 1:
Copy link
Contributor

Choose a reason for hiding this comment

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

len(path) - 1 could be a parameter of the method. I know it's a minor optimization.

if is_simple:
# output
if report_edges and labels:
P = [edge_labels[e] for e in zip(path[:-1], path[1:])]
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI, you could simply use zip(path, path[1:]). the zip iterator ends as soon as one of the list is exhausted. No need to change.

Copy link

github-actions bot commented Jun 29, 2025

Documentation preview for this PR (built with commit 211ae0c; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

The following improvements can be done. Let me know if you agree.

  • vertices in unnecessary_vertices could be removed from G, and so not considered when calling shortest_path_func
  • method ancestor_idx_func could be defined outside the while loop. It suffices to add it parameter ancestor_idx_dict
  • dictionary ancestor_idx_dict should be initialized only if is_simple is True.

Finally, this new method can be exposed in shortest_simple_paths and become the default for directed graphs. I'm not sure it's faster for small digraphs than Yen, but for large ones, it's clearly faster.

If you prefer, we can do these changes in a follow up PR.

@kappybar
Copy link
Contributor Author

Yes I agree with you. This implementation can be improved in these ways or more. I will make another follow up PR.
Thank you for your valuable comments to this PR!

Copy link
Contributor

@dcoudert dcoudert left a comment

Choose a reason for hiding this comment

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

LGTM.

@kappybar kappybar mentioned this pull request Jul 2, 2025
5 tasks
@vbraun vbraun merged commit 1a1d0c4 into sagemath:develop Jul 6, 2025
26 checks passed
vbraun pushed a commit to vbraun/sage that referenced this pull request Jul 26, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
vbraun pushed a commit to vbraun/sage that referenced this pull request Jul 26, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
vbraun pushed a commit to vbraun/sage that referenced this pull request Jul 27, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
vbraun pushed a commit to vbraun/sage that referenced this pull request Jul 28, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
vbraun pushed a commit to vbraun/sage that referenced this pull request Jul 29, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
vbraun pushed a commit to vbraun/sage that referenced this pull request Aug 1, 2025
sagemathgh-40364: Improve PNC algorithm
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->

Improve the implementation of PNC algorithm.
See comments in sagemath#40284.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->

sagemath#40284
    
URL: sagemath#40364
Reported by: Yuta Inoue
Reviewer(s): David Coudert
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: graph theory gsoc: 2025 Tag for GSoC2025 issues/PRs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants