Tom Janson
8 years ago
4 changed files with 8 additions and 122 deletions
-
0src/storm/utility/shortestPaths.cpp
-
4src/storm/utility/shortestPaths.h
-
12src/test/utility/KSPTest.cpp
-
114src/utility/shortestPaths.md
@ -1,12 +1,12 @@ |
|||
#include "gtest/gtest.h"
|
|||
#include "storm-config.h"
|
|||
|
|||
#include "src/builder/ExplicitModelBuilder.h"
|
|||
#include "src/models/sparse/Dtmc.h"
|
|||
#include "src/parser/PrismParser.h"
|
|||
#include "src/storage/SymbolicModelDescription.h"
|
|||
#include "src/utility/graph.h"
|
|||
#include "src/utility/shortestPaths.cpp"
|
|||
#include "storm/builder/ExplicitModelBuilder.h"
|
|||
#include "storm/models/sparse/Dtmc.h"
|
|||
#include "storm/parser/PrismParser.h"
|
|||
#include "storm/storage/SymbolicModelDescription.h"
|
|||
#include "storm/utility/graph.h"
|
|||
#include "storm/utility/shortestPaths.cpp"
|
|||
|
|||
// NOTE: The KSPs / distances of these tests were generated by the
|
|||
// KSP-Generator itself and checked for gross implausibility, but no
|
@ -1,114 +0,0 @@ |
|||
# k-shortest Path Generator |
|||
[This is a collection of random notes for now, to help me remember |
|||
the design decisions and the rationale behind them.] |
|||
|
|||
## Differences from REA algorithm |
|||
This class closely follows the Jimenez-Marzal REA algorithm. |
|||
However, there are some notable deviations in the way targets and shortest |
|||
paths are defined. |
|||
|
|||
### Target groups |
|||
Firstly, instead of a single target state, a group of target states is |
|||
considered. It is clear that this can achieved by removing all outgoing |
|||
transitions (other than self-loops, but this is moot due to not allowing |
|||
non-minimal paths -- see below) and instead introducing edges to a new sink |
|||
state that serves as a meta-target. |
|||
|
|||
<!-- |
|||
In terms of implementation, there are two possible routes (that I can think |
|||
of): |
|||
|
|||
- Simply (but destructively) modifying the precomputation results (in |
|||
particular the predecessor list). This is straightforward and has no |
|||
performance penalty, but implies that each instance of the SP-Generator |
|||
is restricted to a single group of targets. |
|||
(Whereas the original algorithm can compute the KSPs to several targets, |
|||
reusing all partial results.) |
|||
- Keeping the computation "clean" so that all results remain universally |
|||
valid (and thus reusable for other targets) by means of reversibly |
|||
"overlaying" the graph modifications. |
|||
|
|||
It is not clear if there will ever be a need for changing targets. While |
|||
the overlay option is alluring, in the spirit of YAGNI, I choose the |
|||
destructive, simple route. |
|||
--> |
|||
|
|||
I chose to implement this by modifying the precomputation results, meaning |
|||
that they are only valid for a fixed group of target states. Thus, the |
|||
target group is required in the constructor. (It would have been possible to |
|||
allow for interchangeable target groups, but I don't anticipate that use |
|||
case.) |
|||
|
|||
#### Special case: Using Matrix/Vector from SamplingModel |
|||
|
|||
The class has been updated to support the matrix/vector that `SamplingModel` |
|||
generates (as an instance of a PDTMC) as input. This is in fact closely |
|||
related to the target groups, since it works as follows: |
|||
|
|||
The input is a (sub-stochastic) transition matrix of the maybe-states (only!) |
|||
and a vector (again over the maybe-states) with the probabilities to an |
|||
implied target state. |
|||
|
|||
This naturally corresponds to having a meta-target, except the probability |
|||
of its incoming edges range over $(0,1]$ rather than being $1$. |
|||
Thus, applying the term "target group" to the set of states with non-zero |
|||
transitions to the meta-target is now misleading[^1], but nevertheless it |
|||
should work exactly the same. [Right?] |
|||
|
|||
In terms of implementation, in `getEdgeDistance` as well as in the loop of |
|||
the Dijkstra, the "virtual" edges to the meta-target were checked for and |
|||
set to probability $1$; this must now be changed to use the probability as |
|||
indicated in the `targetProbVector` if this input format is used. |
|||
|
|||
### Minimality of paths |
|||
Secondly, we define shortest paths as "minimal" shortest paths in the |
|||
following sense: The path may not visit any target state except at the |
|||
end. As a corollary, no KSP (to a target node) may be a prefix of another. |
|||
This in particular forbids shortest path progressions such as these: |
|||
|
|||
1-SP: 1 2 3 |
|||
2-SP: 1 2 3 3 |
|||
3-SP: 1 2 3 3 3 |
|||
... |
|||
|
|||
This is a common feature if the target state is a sink; but we are not |
|||
interested in such paths. |
|||
|
|||
(In fact, ideally we'd like to see paths whose node-intersection with all |
|||
shorter paths is non-empty [^2] (which is an even stronger statement than |
|||
loop-free-ness of paths), because we want to take a union of those node |
|||
sets. But that's a different matter.) |
|||
|
|||
|
|||
## Data structures, in particular: Path representation |
|||
|
|||
The implicit shortest path representation that J&M describe in the paper |
|||
is used, except that indices rather than back-pointers are stored to |
|||
refer to the tail of the path. |
|||
[Maybe pointers would be faster? STL vector access via index should be |
|||
pretty fast too, though, and less error-prone.] |
|||
|
|||
A bit more detail (recap of the paper): |
|||
All shortest paths (from `s` to `t`) can be described as some k-shortest |
|||
path to some node `u` plus an edge to `t`: |
|||
|
|||
s ~~k-shortest path~~> u --> t |
|||
|
|||
Further, the shortest paths to some node are always computed in order and |
|||
without gaps, e.g., the 1, 2, 3-shortest paths to `t` will be computed |
|||
before the 4-SP. Thus, we store the SPs in a linked list for each node, |
|||
with the k-th entry[^3] being the k-th SP to that node. |
|||
|
|||
Thus for an SP as shown above we simply store the predecessor node (`u`) |
|||
and the `k`, which allows us to look up the tail of the SP. |
|||
By recursively looking up the tail (until it's empty), we reconstruct |
|||
the entire path back-to-front. |
|||
|
|||
[^1]: I suppose the correct term would now be "meta-target predecessors". |
|||
In fact, I will rename all occurences of `target` in the source to |
|||
`metaTargetPredecessors` – clumsy but accurate. |
|||
[^2]: (2016-08-20:) Is this correct? Didn't I mean that the path should |
|||
contain new nodes, i.e., non-emptiness of |
|||
((nodes in path) set-minus (union(nodes in shorter paths)))? |
|||
Yeah, I'm pretty sure that's what I meant. |
|||
[^3]: Which due to 0-based indexing has index `k-1`, of course! Damn it. |
Write
Preview
Loading…
Cancel
Save
Reference in new issue