mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-23 23:06:44 +01:00
askrene: add graph algorithms module
Changelog-EXPERIMENTAL: askrene: add graph algorithms module Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
This commit is contained in:
parent
59ce410699
commit
507153a1cd
8 changed files with 226 additions and 3 deletions
|
@ -8,7 +8,8 @@ PLUGIN_ASKRENE_SRC := \
|
|||
plugins/askrene/refine.c \
|
||||
plugins/askrene/explain_failure.c \
|
||||
plugins/askrene/graph.c \
|
||||
plugins/askrene/priorityqueue.c
|
||||
plugins/askrene/priorityqueue.c \
|
||||
plugins/askrene/algorithm.c
|
||||
|
||||
PLUGIN_ASKRENE_HEADER := \
|
||||
plugins/askrene/askrene.h \
|
||||
|
@ -20,7 +21,8 @@ PLUGIN_ASKRENE_HEADER := \
|
|||
plugins/askrene/refine.h \
|
||||
plugins/askrene/explain_failure.h \
|
||||
plugins/askrene/graph.h \
|
||||
plugins/askrene/priorityqueue.h
|
||||
plugins/askrene/priorityqueue.h \
|
||||
plugins/askrene/algorithm.h
|
||||
|
||||
PLUGIN_ASKRENE_OBJS := $(PLUGIN_ASKRENE_SRC:.c=.o)
|
||||
|
||||
|
|
79
plugins/askrene/algorithm.c
Normal file
79
plugins/askrene/algorithm.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "config.h"
|
||||
#include <ccan/bitmap/bitmap.h>
|
||||
#include <ccan/lqueue/lqueue.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <plugins/askrene/algorithm.h>
|
||||
#include <plugins/askrene/priorityqueue.h>
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
/* Simple queue to traverse the network. */
|
||||
struct queue_data {
|
||||
u32 idx;
|
||||
struct lqueue_link ql;
|
||||
};
|
||||
|
||||
bool BFS_path(const tal_t *ctx, const struct graph *graph,
|
||||
const struct node source, const struct node destination,
|
||||
const s64 *capacity, const s64 cap_threshold, struct arc *prev)
|
||||
{
|
||||
tal_t *this_ctx = tal(ctx, tal_t);
|
||||
bool target_found = false;
|
||||
const size_t max_num_arcs = graph_max_num_arcs(graph);
|
||||
const size_t max_num_nodes = graph_max_num_nodes(graph);
|
||||
|
||||
/* check preconditions */
|
||||
if (!graph || source.idx >= max_num_nodes || !capacity || !prev)
|
||||
goto finish;
|
||||
|
||||
if (tal_count(capacity) != max_num_arcs ||
|
||||
tal_count(prev) != max_num_nodes)
|
||||
goto finish;
|
||||
|
||||
for (size_t i = 0; i < max_num_nodes; i++)
|
||||
prev[i].idx = INVALID_INDEX;
|
||||
|
||||
LQUEUE(struct queue_data, ql) myqueue = LQUEUE_INIT;
|
||||
struct queue_data *qdata;
|
||||
|
||||
qdata = tal(this_ctx, struct queue_data);
|
||||
qdata->idx = source.idx;
|
||||
lqueue_enqueue(&myqueue, qdata);
|
||||
|
||||
while (!lqueue_empty(&myqueue)) {
|
||||
qdata = lqueue_dequeue(&myqueue);
|
||||
struct node cur = {.idx = qdata->idx};
|
||||
|
||||
tal_free(qdata);
|
||||
|
||||
if (cur.idx == destination.idx) {
|
||||
target_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (struct arc arc = node_adjacency_begin(graph, cur);
|
||||
!node_adjacency_end(arc);
|
||||
arc = node_adjacency_next(graph, arc)) {
|
||||
/* check if this arc is traversable */
|
||||
if (capacity[arc.idx] < cap_threshold)
|
||||
continue;
|
||||
|
||||
const struct node next = arc_head(graph, arc);
|
||||
|
||||
/* if that node has been seen previously */
|
||||
if (prev[next.idx].idx != INVALID_INDEX)
|
||||
continue;
|
||||
|
||||
prev[next.idx] = arc;
|
||||
|
||||
qdata = tal(this_ctx, struct queue_data);
|
||||
qdata->idx = next.idx;
|
||||
lqueue_enqueue(&myqueue, qdata);
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
tal_free(this_ctx);
|
||||
return target_found;
|
||||
}
|
36
plugins/askrene/algorithm.h
Normal file
36
plugins/askrene/algorithm.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef LIGHTNING_PLUGINS_ASKRENE_ALGORITHM_H
|
||||
#define LIGHTNING_PLUGINS_ASKRENE_ALGORITHM_H
|
||||
|
||||
/* Implementation of network algorithms: shortests path, minimum cost flow, etc.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <plugins/askrene/graph.h>
|
||||
|
||||
/* Search any path from source to destination using Breadth First Search.
|
||||
*
|
||||
* input:
|
||||
* @ctx: tal allocator,
|
||||
* @graph: graph of the network,
|
||||
* @source: source node,
|
||||
* @destination: destination node,
|
||||
* @capacity: arcs capacity
|
||||
* @cap_threshold: an arc i is traversable if capacity[i]>=cap_threshold
|
||||
*
|
||||
* output:
|
||||
* @prev: prev[i] is the arc that leads to node i for an optimal solution, it
|
||||
* @return: true if the destination node was reached.
|
||||
*
|
||||
* precondition:
|
||||
* |capacity|=graph_max_num_arcs
|
||||
* |prev|=graph_max_num_nodes
|
||||
*
|
||||
* The destination is only used as a stopping condition, if destination is
|
||||
* passed with an invalid idx then the algorithm will produce a discovery tree
|
||||
* of all reacheable nodes from the source.
|
||||
* */
|
||||
bool BFS_path(const tal_t *ctx, const struct graph *graph,
|
||||
const struct node source, const struct node destination,
|
||||
const s64 *capacity, const s64 cap_threshold, struct arc *prev);
|
||||
|
||||
#endif /* LIGHTNING_PLUGINS_ASKRENE_ALGORITHM_H */
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef LIGHTNING_PLUGINS_ASKRENE_GRAPH_H
|
||||
#define LIGHTNING_PLUGINS_ASKRENE_GRAPH_H
|
||||
|
||||
/* Solves a Minimum Cost Flow with arc selection costs and side constraints. */
|
||||
/* Defines a graph data structure. */
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
|
|
|
@ -10,6 +10,10 @@ $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_ASKRENE_SRC)
|
|||
|
||||
PLUGIN_ASKRENE_TEST_COMMON_OBJS :=
|
||||
|
||||
plugins/askrene/test/run-bfs: \
|
||||
plugins/askrene/priorityqueue.o \
|
||||
plugins/askrene/graph.o
|
||||
|
||||
$(PLUGIN_ASKRENE_TEST_PROGRAMS): $(PLUGIN_ASKRENE_TEST_COMMON_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS)
|
||||
|
||||
check-askrene: $(PLUGIN_ASKRENE_TEST_PROGRAMS:%=unittest/%)
|
||||
|
|
100
plugins/askrene/test/run-bfs.c
Normal file
100
plugins/askrene/test/run-bfs.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <common/setup.h>
|
||||
#include <inttypes.h>
|
||||
#include <plugins/askrene/graph.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../algorithm.c"
|
||||
|
||||
#define MAX_NODES 256
|
||||
#define MAX_ARCS 256
|
||||
#define DUAL_BIT 7
|
||||
|
||||
#define CHECK(arg) if(!(arg)){fprintf(stderr, "failed CHECK at line %d: %s\n", __LINE__, #arg); abort();}
|
||||
|
||||
static void show(struct graph *graph, struct node node)
|
||||
{
|
||||
printf("Showing node %" PRIu32 "\n", node.idx);
|
||||
for (struct arc arc = node_adjacency_begin(graph, node);
|
||||
!node_adjacency_end(arc); arc = node_adjacency_next(graph, arc)) {
|
||||
printf("arc id: %" PRIu32 ", (%" PRIu32 " -> %" PRIu32 ")\n",
|
||||
arc.idx, arc_tail(graph, arc).idx,
|
||||
arc_head(graph, arc).idx);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
common_setup(argv[0]);
|
||||
printf("Allocating a memory context\n");
|
||||
tal_t *ctx = tal(NULL, tal_t);
|
||||
assert(ctx);
|
||||
|
||||
printf("Allocating a graph\n");
|
||||
struct graph *graph = graph_new(ctx, MAX_NODES, MAX_ARCS, DUAL_BIT);
|
||||
assert(graph);
|
||||
|
||||
s64 *capacity = tal_arrz(ctx, s64, MAX_ARCS);
|
||||
struct arc *prev = tal_arr(ctx, struct arc, MAX_NODES);
|
||||
|
||||
graph_add_arc(graph, arc_obj(0), node_obj(1), node_obj(2));
|
||||
capacity[0] = 1;
|
||||
graph_add_arc(graph, arc_obj(1), node_obj(1), node_obj(3));
|
||||
capacity[1] = 1;
|
||||
graph_add_arc(graph, arc_obj(2), node_obj(1), node_obj(6));
|
||||
capacity[2] = 1;
|
||||
graph_add_arc(graph, arc_obj(3), node_obj(2), node_obj(3));
|
||||
capacity[3] = 1;
|
||||
graph_add_arc(graph, arc_obj(4), node_obj(2), node_obj(4));
|
||||
capacity[4] = 0; /* disable this arc */
|
||||
graph_add_arc(graph, arc_obj(5), node_obj(3), node_obj(4));
|
||||
capacity[5] = 1;
|
||||
graph_add_arc(graph, arc_obj(6), node_obj(3), node_obj(6));
|
||||
capacity[6] = 1;
|
||||
graph_add_arc(graph, arc_obj(7), node_obj(4), node_obj(5));
|
||||
capacity[7] = 1;
|
||||
graph_add_arc(graph, arc_obj(8), node_obj(5), node_obj(6));
|
||||
capacity[8] = 1;
|
||||
|
||||
show(graph, node_obj(1));
|
||||
show(graph, node_obj(2));
|
||||
show(graph, node_obj(3));
|
||||
show(graph, node_obj(4));
|
||||
show(graph, node_obj(5));
|
||||
show(graph, node_obj(6));
|
||||
|
||||
struct node src = {.idx = 1};
|
||||
struct node dst = {.idx = 5};
|
||||
|
||||
bool result = BFS_path(ctx, graph, src, dst, capacity, 1, prev);
|
||||
assert(result);
|
||||
|
||||
int pathlen = 0;
|
||||
int arc_sequence[] = {7, 5, 1};
|
||||
int node_sequence[] = {4, 3, 1};
|
||||
|
||||
printf("path: ");
|
||||
for (struct node cur = dst; cur.idx != src.idx;) {
|
||||
struct arc arc = prev[cur.idx];
|
||||
printf("node(%" PRIu32 ") arc(%" PRIu32 ") - ", cur.idx,
|
||||
arc.idx);
|
||||
cur = arc_tail(graph, arc);
|
||||
CHECK(pathlen < 3);
|
||||
CHECK(cur.idx == node_sequence[pathlen]);
|
||||
CHECK(arc.idx == arc_sequence[pathlen]);
|
||||
pathlen ++;
|
||||
}
|
||||
CHECK(pathlen == 3);
|
||||
printf("node(%" PRIu32 ") arc(NONE)\n", src.idx);
|
||||
printf("path length: %d\n", pathlen);
|
||||
|
||||
printf("Freeing memory\n");
|
||||
ctx = tal_free(ctx);
|
||||
|
||||
common_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -57,5 +57,6 @@ int main(int argc, char *argv[])
|
|||
printf("Freeing memory\n");
|
||||
ctx = tal_free(ctx);
|
||||
common_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,4 +101,5 @@ int main(int argc, char *argv[])
|
|||
printf("Freeing memory\n");
|
||||
ctx = tal_free(ctx);
|
||||
common_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue