Skip to content

Commit dda4704

Browse files
committed
askrene: add dijkstra algorithm
Changelog-EXPERIMENTAL: askrene: add dijkstra algorithm Signed-off-by: Lagrang3 <[email protected]>
1 parent 7c1eafe commit dda4704

File tree

4 files changed

+250
-1
lines changed

4 files changed

+250
-1
lines changed

plugins/askrene/algorithm.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,95 @@ bool BFS_path(const tal_t *ctx, const struct graph *graph,
7777
tal_free(this_ctx);
7878
return target_found;
7979
}
80+
81+
bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
82+
const struct node source, const struct node destination,
83+
bool prune, const s64 *capacity, const s64 cap_threshold,
84+
const s64 *cost, const s64 *potential, struct arc *prev,
85+
s64 *distance)
86+
{
87+
bool target_found = false;
88+
const size_t max_num_arcs = graph_max_num_arcs(graph);
89+
const size_t max_num_nodes = graph_max_num_nodes(graph);
90+
tal_t *this_ctx = tal(ctx, tal_t);
91+
92+
/* check preconditions */
93+
if (!graph || source.idx >=max_num_nodes || !cost || !capacity ||
94+
!prev || !distance)
95+
goto finish;
96+
97+
/* if prune is true then the destination cannot be invalid */
98+
if (destination.idx >=max_num_nodes && prune)
99+
goto finish;
100+
101+
if (tal_count(cost) != max_num_arcs ||
102+
tal_count(capacity) != max_num_arcs ||
103+
tal_count(prev) != max_num_nodes ||
104+
tal_count(distance) != max_num_nodes)
105+
goto finish;
106+
107+
/* FIXME: maybe this is unnecessary */
108+
bitmap *visited = tal_arrz(this_ctx, bitmap,
109+
BITMAP_NWORDS(max_num_nodes));
110+
111+
if (!visited)
112+
/* bad allocation */
113+
goto finish;
114+
115+
for (size_t i = 0; i < max_num_nodes; ++i)
116+
prev[i].idx = INVALID_INDEX;
117+
118+
struct priorityqueue *q;
119+
q = priorityqueue_new(this_ctx, max_num_nodes);
120+
const s64 *const dijkstra_distance = priorityqueue_value(q);
121+
122+
priorityqueue_init(q);
123+
priorityqueue_update(q, source.idx, 0);
124+
125+
while (!priorityqueue_empty(q)) {
126+
const u32 cur = priorityqueue_top(q);
127+
priorityqueue_pop(q);
128+
129+
/* FIXME: maybe this is unnecessary */
130+
if (bitmap_test_bit(visited, cur))
131+
continue;
132+
bitmap_set_bit(visited, cur);
133+
134+
if (cur == destination.idx) {
135+
target_found = true;
136+
if (prune)
137+
break;
138+
}
139+
140+
for (struct arc arc =
141+
node_adjacency_begin(graph, node_obj(cur));
142+
!node_adjacency_end(arc);
143+
arc = node_adjacency_next(graph, arc)) {
144+
/* check if this arc is traversable */
145+
if (capacity[arc.idx] < cap_threshold)
146+
continue;
147+
148+
const struct node next = arc_head(graph, arc);
149+
150+
const s64 cij = cost[arc.idx] - potential[cur] +
151+
potential[next.idx];
152+
153+
/* Dijkstra only works with non-negative weights */
154+
assert(cij >= 0);
155+
156+
if (dijkstra_distance[next.idx] <=
157+
dijkstra_distance[cur] + cij)
158+
continue;
159+
160+
priorityqueue_update(q, next.idx,
161+
dijkstra_distance[cur] + cij);
162+
prev[next.idx] = arc;
163+
}
164+
}
165+
for (size_t i = 0; i < max_num_nodes; i++)
166+
distance[i] = dijkstra_distance[i];
167+
168+
finish:
169+
tal_free(this_ctx);
170+
return target_found;
171+
}

plugins/askrene/algorithm.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,40 @@ bool BFS_path(const tal_t *ctx, const struct graph *graph,
3333
const struct node source, const struct node destination,
3434
const s64 *capacity, const s64 cap_threshold, struct arc *prev);
3535

36+
37+
/* Computes the distance from the source to every other node in the network
38+
* using Dijkstra's algorithm.
39+
*
40+
* input:
41+
* @ctx: tal context for internal allocation
42+
* @graph: topological information of the graph
43+
* @source: source node
44+
* @destination: destination node
45+
* @prune: if prune is true the algorithm stops when the optimal path is found
46+
* for the destination node
47+
* @capacity: arcs capacity
48+
* @cap_threshold: an arc i is traversable if capacity[i]>=cap_threshold
49+
* @cost: arc's cost
50+
* @potential: nodes' potential, ie. reduced cost for an arc
51+
* c_ij = cost_ij - potential[i] + potential[j]
52+
*
53+
* output:
54+
* @prev: for each node, this is the arc that was used to arrive to it, this can
55+
* be used to reconstruct the path from the destination to the source,
56+
* @distance: node's best distance
57+
* returns true if an optimal path is found for the destination, false otherwise
58+
*
59+
* precondition:
60+
* |capacity|=|cost|=graph_max_num_arcs
61+
* |prev|=|distance|=graph_max_num_nodes
62+
* cost[i]>=0
63+
* if prune is true the destination must be valid
64+
* */
65+
bool dijkstra_path(const tal_t *ctx, const struct graph *graph,
66+
const struct node source, const struct node destination,
67+
bool prune, const s64 *capacity, const s64 cap_threshold,
68+
const s64 *cost, const s64 *potential, struct arc *prev,
69+
s64 *distance);
70+
71+
3672
#endif /* LIGHTNING_PLUGINS_ASKRENE_ALGORITHM_H */

plugins/askrene/test/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_ASKRENE_SRC)
1010

1111
PLUGIN_ASKRENE_TEST_COMMON_OBJS :=
1212

13-
plugins/askrene/test/run-bfs: \
13+
plugins/askrene/test/run-bfs plugins/askrene/test/run-dijkstra: \
1414
plugins/askrene/priorityqueue.o \
1515
plugins/askrene/graph.o
1616

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "config.h"
2+
#include <assert.h>
3+
#include <ccan/tal/tal.h>
4+
#include <common/setup.h>
5+
#include <inttypes.h>
6+
#include <plugins/askrene/graph.h>
7+
#include <stdio.h>
8+
9+
#include "../algorithm.c"
10+
11+
// 1->2 7
12+
// 1->3 9
13+
// 1->6 14
14+
// 2->3 10
15+
// 2->4 15
16+
// 3->6 2
17+
// 3->4 11
18+
// 4->5 6
19+
// 5->6 9
20+
21+
#define MAX_NODES 256
22+
#define MAX_ARCS 256
23+
#define DUAL_BIT 7
24+
25+
#define CHECK(arg) if(!(arg)){fprintf(stderr, "failed CHECK at line %d: %s\n", __LINE__, #arg); abort();}
26+
27+
static void show(struct graph *graph, struct node node)
28+
{
29+
printf("Showing node %" PRIu32 "\n", node.idx);
30+
for (struct arc arc = node_adjacency_begin(graph, node);
31+
!node_adjacency_end(arc); arc = node_adjacency_next(graph, arc)) {
32+
printf("arc id: %" PRIu32 ", (%" PRIu32 " -> %" PRIu32 ")\n",
33+
arc.idx, arc_tail(graph, arc).idx,
34+
arc_head(graph, arc).idx);
35+
}
36+
printf("\n");
37+
}
38+
39+
int main(int argc, char *argv[])
40+
{
41+
common_setup(argv[0]);
42+
printf("Allocating a memory context\n");
43+
tal_t *ctx = tal(NULL, tal_t);
44+
assert(ctx);
45+
46+
printf("Allocating a graph\n");
47+
struct graph *graph = graph_new(ctx, MAX_NODES, MAX_ARCS, DUAL_BIT);
48+
assert(graph);
49+
50+
s64 *capacity = tal_arrz(ctx, s64, MAX_ARCS);
51+
s64 *cost = tal_arrz(ctx, s64, MAX_ARCS);
52+
s64 *potential = tal_arrz(ctx, s64, MAX_NODES);
53+
s64 *distance = tal_arr(ctx, s64, MAX_NODES);
54+
struct arc *prev = tal_arr(ctx, struct arc, MAX_NODES);
55+
56+
graph_add_arc(graph, arc_obj(0), node_obj(1), node_obj(2));
57+
cost[0] = 7, capacity[0] = 1;
58+
graph_add_arc(graph, arc_obj(1), node_obj(1), node_obj(3));
59+
cost[1] = 9, capacity[1] = 1;
60+
graph_add_arc(graph, arc_obj(2), node_obj(1), node_obj(6));
61+
cost[2] = 14, capacity[2] = 1;
62+
graph_add_arc(graph, arc_obj(3), node_obj(2), node_obj(3));
63+
cost[3] = 10, capacity[3] = 1;
64+
graph_add_arc(graph, arc_obj(4), node_obj(2), node_obj(4));
65+
cost[4] = 15, capacity[4] = 1;
66+
graph_add_arc(graph, arc_obj(5), node_obj(3), node_obj(4));
67+
cost[5] = 11, capacity[5] = 1;
68+
graph_add_arc(graph, arc_obj(6), node_obj(3), node_obj(6));
69+
cost[6] = 2, capacity[6] = 1;
70+
graph_add_arc(graph, arc_obj(7), node_obj(4), node_obj(5));
71+
cost[7] = 6, capacity[7] = 1;
72+
graph_add_arc(graph, arc_obj(8), node_obj(5), node_obj(6));
73+
cost[8] = 9, capacity[8] = 1;
74+
75+
show(graph, node_obj(1));
76+
show(graph, node_obj(2));
77+
show(graph, node_obj(3));
78+
show(graph, node_obj(4));
79+
show(graph, node_obj(5));
80+
show(graph, node_obj(6));
81+
82+
struct node src = {.idx = 1};
83+
struct node dst = {.idx = 6};
84+
85+
bool result = dijkstra_path(ctx, graph, src, dst, false, capacity, 1,
86+
cost, potential, prev, distance);
87+
CHECK(result);
88+
89+
int pathlen = 0;
90+
int arc_sequence[] = {6, 1};
91+
int node_sequence[] = {3, 1};
92+
93+
for (struct node cur = dst; cur.idx != src.idx;) {
94+
struct arc arc = prev[cur.idx];
95+
printf("node(%" PRIu32 ") arc(%" PRIu32 ") - ", cur.idx,
96+
arc.idx);
97+
cur = arc_tail(graph, arc);
98+
CHECK(pathlen < 2);
99+
CHECK(cur.idx == node_sequence[pathlen]);
100+
CHECK(arc.idx == arc_sequence[pathlen]);
101+
pathlen++;
102+
}
103+
CHECK(pathlen == 2);
104+
105+
for (size_t i = 1; i <= 6; i++) {
106+
printf("node: %zu, distance: %" PRIi64 "\n", i, distance[i]);
107+
}
108+
109+
CHECK(distance[1] == 0);
110+
CHECK(distance[2] == 7);
111+
CHECK(distance[3] == 9);
112+
CHECK(distance[4] == 20);
113+
CHECK(distance[5] == 26);
114+
CHECK(distance[6] == 11);
115+
116+
printf("Freeing memory\n");
117+
ctx = tal_free(ctx);
118+
119+
common_shutdown();
120+
return 0;
121+
}

0 commit comments

Comments
 (0)