feat: New k_shortest_paths() to compute the first k shortest paths between two vertices#1028
feat: New k_shortest_paths() to compute the first k shortest paths between two vertices#1028aviator-app[bot] merged 3 commits intomainfrom
k_shortest_paths() to compute the first k shortest paths between two vertices#1028Conversation
Current Aviator status
This PR was merged using Aviator. See the real-time status of this PR on the Aviator webapp. Use the Aviator Chrome Extension to see the status of your PR within GitHub.
|
|
@krlmlr This does not currently work and I need some advice on how to fix it. The reason for wrapping > k_shortest_paths(make_ring(5), 1, 2, k=3)
Error in match.arg(arg = arg, choices = choices, several.ok = several.ok) :
'arg' must be of length 1
> traceback()
5: stop("'arg' must be of length 1")
4: match.arg(arg = arg, choices = choices, several.ok = several.ok)
3: igraph.match.arg(mode)
2: get_k_shortest_paths_impl(graph = graph, weights = weights, k = k,
from = from, to = to, mode = mode)
1: k_shortest_paths(make_ring(5), 1, 2, k = 3)@ntamas Is there a stimulus feature to reorder parameters? Can this be handled in |
f8db3ad to
afe79f9
Compare
|
@ntamas OK, I found |
|
Never mind, I just realized that The R code is now fully auto-generated and it works. |
|
@ntamas Is there a way to set default parameter values just in R when they are not set in the C core? What if I'd like to set a default of |
No, not at the moment, apart from copying the entire parameter specification of the function from the C core and adding the default value there. We can implement something like this in Stimulus, although the ideal solution would be to define the default value in the C core. |
krlmlr
left a comment
There was a problem hiding this comment.
Thanks. What would a test look like here?
| @@ -819,14 +819,14 @@ get_k_shortest_paths_impl <- function(graph, weights=NULL, k, from, to, mode=c(" | |||
| res <- .Call(R_igraph_get_k_shortest_paths, graph, weights, k, from-1, to-1, mode) | |||
There was a problem hiding this comment.
I wonder why we have -1 here but no inverse correction for the output. Is this expected?
There was a problem hiding this comment.
I don't have the code in front of me right now, but I remember that there aren't any clear guidelines on where to do the +1/-1 adjustments for vertex/edge indices and sometimes it's done in the output conversion of the result. For instance, there is R_igraph_vector_int_to_SEXP to convert an igraph_vector_int_t back to R without adjustment and R_igraph_vector_int_to_SEXPp1 to convert it back with adjustments. (The p1 suffix stands for "plus one").
Ideally, there should be guidelines on where the adjustment is done. If it is easier to do in R and there are no performance implications (like an unnecessary copying of the vector), it should probably be done in R and we could get rid of the p1 functions in rinterface_extra.c.
There was a problem hiding this comment.
There are performance implications, ideally we'd convert everything in C.
There was a problem hiding this comment.
Dreaming big: If we could get views that cast to int and perform the "minus one" on the fly, in the C core...
R/aaa-auto.R
Outdated
| } | ||
|
|
||
| get_k_shortest_paths_impl <- function(graph, weights=NULL, k, from, to, mode=c("out", "in", "all", "total")) { | ||
| get_k_shortest_paths_impl <- function(graph, from, to, k, weights=NULL, mode=c("out", "in", "all", "total")) { |
There was a problem hiding this comment.
The following would be safer:
| get_k_shortest_paths_impl <- function(graph, from, to, k, weights=NULL, mode=c("out", "in", "all", "total")) { | |
| get_k_shortest_paths_impl <- function(graph, from, to, ..., k, weights=NULL, mode=c("out", "in", "all", "total")) { | |
| check_dots_empty() |
This would require users to supply names for k and the other optional arguments. Positional matching makes it harder:
- for users to skim the code (what does that fifth argument mean again?),
- for us to add new arguments (all new ones must go at the end to maintain full compatibility, which is not always desired).
I wonder if this could be part of Stimulus, or if we create a wrapper here (and in many other places).
There was a problem hiding this comment.
Stimulus does not have a way to indicate a preference for keyword arguments vs positional arguments, but I guess it could be added. Python does it by adding a lone * in the argument list (e.g., def compare(a, b, *, key=None): makes key a keyword argument only while a and b could be defined either as positional or as keyword arguments). We could adapt something similar in Stimulus if this is a concept that seems to apply to multiple languages. Languages without kwarg support could ignore the asterisk in the argument list.
Another alternative would be to add a Stimulus keyword that specifies the name of the first argument that should be treated as keyword-only (it would be k here), but this could cause problems if a higher level interface decides to re-order arguments.
There was a problem hiding this comment.
I like the star notation. Can you please propose something, @ntamas?
There was a problem hiding this comment.
I'll come up with a patch in Stimulus and we'll see how the idea works out.
There was a problem hiding this comment.
See the Stimulus revision tagged with 0.21.0.
There are now three alternative ways to indicate keyword-only arguments:
- You can add
KWin front of the type of the argument in thePARAMSkey to mark an argument as keyword-only. - You can add
*in the middle of the argument list at the point where the keyword-only arguments start. - You can add a key named
FIRST_KW_PARAMnext toPARAMSand it must contain the name of the first keyword-only argument. This is primarily meant for backward compatibility with Stimulus 0.20 and earlier as the other two options would fail on earlier versions of Stimulus.
I tried to update the RR code generator to respect keyword-only arguments, but I haven't tested it. If it does not work, look in RRCodeGenerator, this is where the magic happens.
Next up would be to update functions.yaml in the C core to indicate which arguments should be keyword-only in generated interfaces.
There was a problem hiding this comment.
If you want to make k a mandatory keyword argument, I would rather specify this as a custom parameter list for the R interface, and also give a default of k=1. In this specific case, I wouldn't mark it as mandatory keyword in the C core. Shall I go ahead?
While this feels natural in R, it wouldn't be so in Mathematica. Not sure about Python. Note that when using this function, people would always want to specify k.
There was a problem hiding this comment.
@ntamas How does the * work with parameter reordering? Is it allowed to be used in PARAM_ORDER? One difficulty is that in C, weights usually comes second, yet it should probably always be keyword-only in R.
There was a problem hiding this comment.
Is it allowed to be used in PARAM_ORDER?
No, only in PARAMS. It's a neat idea, though. I'll see what can be done.
How does the
*work with parameter reordering?
In the parsed parameter specification, each parameter has a property named is_keyword_only that specifies whether the parameter is to be used as a keyword argument only if the language supports keyword arguments. The * is essentially a shortcut for specifying this for an argument and all subsequent arguments automatically. This comes before the parameter reordering.
Note that in theory, one could end up in a situation where keyword-only and non-keyword-only arguments are intermixed in the parameter ordering. It is up to the language-specific code generator to deal with such a situation. The RR code generator collects non-keyword-only args first and keyword-only args second (but respects PARAM_ORDER within each sublist). The Python code generator throws an error because it was easier to do it that way.
There was a problem hiding this comment.
PARAM_ORDER now supports * from Stimulus 0.21.2. FWIW it also supports ..., which means "all the parameters that have not been listed explicitly so far, in their original order".
|
I'm massaging the |
|
After #1043, there should be no more conflicts for now. |
Yes, I will add tests. |
|
A good test would be: @krlmlr How do I check that |
45565fb to
59d7b03
Compare
|
Conflicts resolved. |
|
I would do something like: expect_length(res$vpaths, 2)
expect_equal(as.numeric(res$vpaths[[1]]), c(1, 2))
expect_equal(as.numeric(res$vpaths[[2]]), c(1, 5, 4, 3, 2))
... |
4b05202 to
9b034b3
Compare
k_shortest_paths() to compute the first k shortest paths between two vertices
| PP: get.all.simple.paths.pp | ||
|
|
||
| igraph_get_k_shortest_paths: | ||
| PARAM_ORDER: graph, from, to, *, k, ... |
There was a problem hiding this comment.
I wonder why we need to override this here. No strong opinion.
There was a problem hiding this comment.
I did this to be able to make weights optional. This function will often be used on unweighted graphs. Is there a better way to do this?
There was a problem hiding this comment.
It's fine, but is this order something to be considered upstream too? To me, this order seems reasonable for other interfaces too.
There was a problem hiding this comment.
Well, yes, but probably too late for that. If we ever reorder arguments in all functions in the C core, this would make sense, but it's unlikely that'll happen. When we settled on the argument ordering, we forgot about the consequences for auto-generation. CC @ntamas
https://github.com/igraph/igraph/wiki/Guidelines-for-function-argument-ordering
There was a problem hiding this comment.
Interesting.
In R land, we have the attribute. R objects are immutable, it's easy to remove weights, or to set new weights, without changing the graph object. I wonder if we even need the weights argument.
9b034b3 to
58377eb
Compare
Fixes #972 by adding
k_shortest_paths().This is a draft, but it should be reviewed now. Keep in mind that I don't actually know much R! In fact I wouldn't mind if someone took this over or helped out.
The reason why I did not add this to the doc page of
shortest_paths()is that thetoparameter is different: it must be a single vertex.@maelle:
@inheritParamshere.@krlmlr:
I did not attempt to add an
outputparameter likeshortest_pathshas. Both edge and vertex paths are returned unconditionally.I hope to have this much-requested feature in 2.0.0.
CC @luukvdmeer