mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-24 07:07:46 +01:00
askrene: use the new MCF solver
Changelog-none: askrene: use the new MCF solver Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
This commit is contained in:
parent
84a9476311
commit
937cf7a554
1 changed files with 124 additions and 515 deletions
|
@ -8,9 +8,11 @@
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <plugins/askrene/algorithm.h>
|
||||||
#include <plugins/askrene/askrene.h>
|
#include <plugins/askrene/askrene.h>
|
||||||
#include <plugins/askrene/dijkstra.h>
|
#include <plugins/askrene/dijkstra.h>
|
||||||
#include <plugins/askrene/flow.h>
|
#include <plugins/askrene/flow.h>
|
||||||
|
#include <plugins/askrene/graph.h>
|
||||||
#include <plugins/askrene/mcf.h>
|
#include <plugins/askrene/mcf.h>
|
||||||
#include <plugins/libplugin.h>
|
#include <plugins/libplugin.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -163,7 +165,6 @@
|
||||||
static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95};
|
static const double CHANNEL_PIVOTS[]={0,0.5,0.8,0.95};
|
||||||
|
|
||||||
static const s64 INFINITE = INT64_MAX;
|
static const s64 INFINITE = INT64_MAX;
|
||||||
static const u32 INVALID_INDEX = 0xffffffff;
|
|
||||||
static const s64 MU_MAX = 100;
|
static const s64 MU_MAX = 100;
|
||||||
|
|
||||||
/* Let's try this encoding of arcs:
|
/* Let's try this encoding of arcs:
|
||||||
|
@ -234,10 +235,6 @@ static const s64 MU_MAX = 100;
|
||||||
* [ 0 1 2 3 4 5 6 ... 31 ]
|
* [ 0 1 2 3 4 5 6 ... 31 ]
|
||||||
* dual part chandir chanidx
|
* dual part chandir chanidx
|
||||||
*/
|
*/
|
||||||
struct arc {
|
|
||||||
u32 idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ARC_DUAL_BITOFF (0)
|
#define ARC_DUAL_BITOFF (0)
|
||||||
#define ARC_PART_BITOFF (1)
|
#define ARC_PART_BITOFF (1)
|
||||||
#define ARC_CHANDIR_BITOFF (1 + PARTS_BITS)
|
#define ARC_CHANDIR_BITOFF (1 + PARTS_BITS)
|
||||||
|
@ -304,19 +301,12 @@ struct pay_parameters {
|
||||||
* capacity; these quantities remain constant during MCF execution. */
|
* capacity; these quantities remain constant during MCF execution. */
|
||||||
struct linear_network
|
struct linear_network
|
||||||
{
|
{
|
||||||
u32 *arc_tail_node;
|
struct graph *graph;
|
||||||
// notice that a tail node is not needed,
|
|
||||||
// because the tail of arc is the head of dual(arc)
|
|
||||||
|
|
||||||
struct arc *node_adjacency_next_arc;
|
|
||||||
struct arc *node_adjacency_first_arc;
|
|
||||||
|
|
||||||
// probability and fee cost associated to an arc
|
// probability and fee cost associated to an arc
|
||||||
double *arc_prob_cost;
|
double *arc_prob_cost;
|
||||||
s64 *arc_fee_cost;
|
s64 *arc_fee_cost;
|
||||||
s64 *capacity;
|
s64 *capacity;
|
||||||
|
|
||||||
size_t max_num_arcs,max_num_nodes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is the structure that keeps track of the network properties while we
|
/* This is the structure that keeps track of the network properties while we
|
||||||
|
@ -330,78 +320,22 @@ struct residual_network {
|
||||||
|
|
||||||
/* potential function on nodes */
|
/* potential function on nodes */
|
||||||
s64 *potential;
|
s64 *potential;
|
||||||
};
|
|
||||||
|
|
||||||
/* Helper function.
|
/* auxiliary data, the excess of flow on nodes */
|
||||||
* Given an arc idx, return the dual's idx in the residual network. */
|
s64 *excess;
|
||||||
static struct arc arc_dual(struct arc arc)
|
};
|
||||||
{
|
|
||||||
arc.idx ^= (1U << ARC_DUAL_BITOFF);
|
|
||||||
return arc;
|
|
||||||
}
|
|
||||||
/* Helper function. */
|
|
||||||
static bool arc_is_dual(struct arc arc)
|
|
||||||
{
|
|
||||||
bool dual;
|
|
||||||
arc_to_parts(arc, NULL, NULL, NULL, &dual);
|
|
||||||
return dual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function.
|
/* Helper function.
|
||||||
* Given an arc of the network (not residual) give me the flow. */
|
* Given an arc of the network (not residual) give me the flow. */
|
||||||
static s64 get_arc_flow(
|
static s64 get_arc_flow(
|
||||||
const struct residual_network *network,
|
const struct residual_network *network,
|
||||||
|
const struct graph *graph,
|
||||||
const struct arc arc)
|
const struct arc arc)
|
||||||
{
|
{
|
||||||
assert(!arc_is_dual(arc));
|
assert(!arc_is_dual(graph, arc));
|
||||||
assert(arc_dual(arc).idx < tal_count(network->cap));
|
struct arc dual = arc_dual(graph, arc);
|
||||||
return network->cap[ arc_dual(arc).idx ];
|
assert(dual.idx < tal_count(network->cap));
|
||||||
}
|
return network->cap[dual.idx];
|
||||||
|
|
||||||
/* Helper function.
|
|
||||||
* Given an arc idx, return the node from which this arc emanates in the residual network. */
|
|
||||||
static u32 arc_tail(const struct linear_network *linear_network,
|
|
||||||
const struct arc arc)
|
|
||||||
{
|
|
||||||
assert(arc.idx < linear_network->max_num_arcs);
|
|
||||||
return linear_network->arc_tail_node[ arc.idx ];
|
|
||||||
}
|
|
||||||
/* Helper function.
|
|
||||||
* Given an arc idx, return the node that this arc is pointing to in the residual network. */
|
|
||||||
static u32 arc_head(const struct linear_network *linear_network,
|
|
||||||
const struct arc arc)
|
|
||||||
{
|
|
||||||
const struct arc dual = arc_dual(arc);
|
|
||||||
assert(dual.idx < linear_network->max_num_arcs);
|
|
||||||
return linear_network->arc_tail_node[dual.idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function.
|
|
||||||
* Given node idx `node`, return the idx of the first arc whose tail is `node`.
|
|
||||||
* */
|
|
||||||
static struct arc node_adjacency_begin(
|
|
||||||
const struct linear_network * linear_network,
|
|
||||||
const u32 node)
|
|
||||||
{
|
|
||||||
assert(node < linear_network->max_num_nodes);
|
|
||||||
return linear_network->node_adjacency_first_arc[node];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function.
|
|
||||||
* Is this the end of the adjacency list. */
|
|
||||||
static bool node_adjacency_end(const struct arc arc)
|
|
||||||
{
|
|
||||||
return arc.idx == INVALID_INDEX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function.
|
|
||||||
* Given node idx `node` and `arc`, returns the idx of the next arc whose tail is `node`. */
|
|
||||||
static struct arc node_adjacency_next(
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
const struct arc arc)
|
|
||||||
{
|
|
||||||
assert(arc.idx < linear_network->max_num_arcs);
|
|
||||||
return linear_network->node_adjacency_next_arc[arc.idx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set *capacity to value, up to *cap_on_capacity. Reduce cap_on_capacity */
|
/* Set *capacity to value, up to *cap_on_capacity. Reduce cap_on_capacity */
|
||||||
|
@ -456,6 +390,8 @@ alloc_residual_network(const tal_t *ctx, const size_t max_num_nodes,
|
||||||
residual_network->cost = tal_arrz(residual_network, s64, max_num_arcs);
|
residual_network->cost = tal_arrz(residual_network, s64, max_num_arcs);
|
||||||
residual_network->potential =
|
residual_network->potential =
|
||||||
tal_arrz(residual_network, s64, max_num_nodes);
|
tal_arrz(residual_network, s64, max_num_nodes);
|
||||||
|
residual_network->excess =
|
||||||
|
tal_arrz(residual_network, s64, max_num_nodes);
|
||||||
|
|
||||||
return residual_network;
|
return residual_network;
|
||||||
}
|
}
|
||||||
|
@ -464,23 +400,25 @@ static void init_residual_network(
|
||||||
const struct linear_network * linear_network,
|
const struct linear_network * linear_network,
|
||||||
struct residual_network* residual_network)
|
struct residual_network* residual_network)
|
||||||
{
|
{
|
||||||
const size_t max_num_arcs = linear_network->max_num_arcs;
|
const struct graph *graph = linear_network->graph;
|
||||||
const size_t max_num_nodes = linear_network->max_num_nodes;
|
const size_t max_num_arcs = graph_max_num_arcs(graph);
|
||||||
|
const size_t max_num_nodes = graph_max_num_nodes(graph);
|
||||||
|
|
||||||
for(struct arc arc = {0};arc.idx < max_num_arcs; ++arc.idx)
|
for (struct arc arc = {.idx = 0}; arc.idx < max_num_arcs; ++arc.idx) {
|
||||||
{
|
if (arc_is_dual(graph, arc) || !arc_enabled(graph, arc))
|
||||||
if(arc_is_dual(arc))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
struct arc dual = arc_dual(arc);
|
struct arc dual = arc_dual(graph, arc);
|
||||||
residual_network->cap[arc.idx]=linear_network->capacity[arc.idx];
|
residual_network->cap[arc.idx] =
|
||||||
residual_network->cap[dual.idx]=0;
|
linear_network->capacity[arc.idx];
|
||||||
|
residual_network->cap[dual.idx] = 0;
|
||||||
|
|
||||||
residual_network->cost[arc.idx]=residual_network->cost[dual.idx]=0;
|
residual_network->cost[arc.idx] =
|
||||||
|
residual_network->cost[dual.idx] = 0;
|
||||||
}
|
}
|
||||||
for(u32 i=0;i<max_num_nodes;++i)
|
for (u32 i = 0; i < max_num_nodes; ++i) {
|
||||||
{
|
residual_network->potential[i] = 0;
|
||||||
residual_network->potential[i]=0;
|
residual_network->excess[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,14 +443,16 @@ static int cmp_double(const double *a, const double *b, void *unused)
|
||||||
static double get_median_ratio(const tal_t *working_ctx,
|
static double get_median_ratio(const tal_t *working_ctx,
|
||||||
const struct linear_network* linear_network)
|
const struct linear_network* linear_network)
|
||||||
{
|
{
|
||||||
u64 *u64_arr = tal_arr(working_ctx, u64, linear_network->max_num_arcs/2);
|
const struct graph *graph = linear_network->graph;
|
||||||
double *double_arr = tal_arr(working_ctx, double, linear_network->max_num_arcs/2);
|
const size_t max_num_arcs = graph_max_num_arcs(graph);
|
||||||
|
u64 *u64_arr = tal_arr(working_ctx, u64, max_num_arcs);
|
||||||
|
double *double_arr = tal_arr(working_ctx, double, max_num_arcs);
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
|
||||||
for (struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx) {
|
for (struct arc arc = {.idx=0};arc.idx < max_num_arcs; ++arc.idx) {
|
||||||
if (arc_is_dual(arc))
|
if (arc_is_dual(graph, arc) || !arc_enabled(graph, arc))
|
||||||
continue;
|
continue;
|
||||||
assert(n < linear_network->max_num_arcs/2);
|
assert(n < max_num_arcs/2);
|
||||||
u64_arr[n] = linear_network->arc_fee_cost[arc.idx];
|
u64_arr[n] = linear_network->arc_fee_cost[arc.idx];
|
||||||
double_arr[n] = linear_network->arc_prob_cost[arc.idx];
|
double_arr[n] = linear_network->arc_prob_cost[arc.idx];
|
||||||
n++;
|
n++;
|
||||||
|
@ -539,10 +479,12 @@ static void combine_cost_function(
|
||||||
* Scale by ratio of (positive) medians. */
|
* Scale by ratio of (positive) medians. */
|
||||||
const double k = get_median_ratio(working_ctx, linear_network);
|
const double k = get_median_ratio(working_ctx, linear_network);
|
||||||
const double ln_30 = log(30);
|
const double ln_30 = log(30);
|
||||||
|
const struct graph *graph = linear_network->graph;
|
||||||
|
const size_t max_num_arcs = graph_max_num_arcs(graph);
|
||||||
|
|
||||||
for(struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx)
|
for(struct arc arc = {.idx=0};arc.idx < max_num_arcs; ++arc.idx)
|
||||||
{
|
{
|
||||||
if(arc_tail(linear_network,arc)==INVALID_INDEX)
|
if (!arc_enabled(graph, arc))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const double pcost = linear_network->arc_prob_cost[arc.idx];
|
const double pcost = linear_network->arc_prob_cost[arc.idx];
|
||||||
|
@ -573,21 +515,6 @@ static void combine_cost_function(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void linear_network_add_adjacenct_arc(
|
|
||||||
struct linear_network *linear_network,
|
|
||||||
const u32 node_idx,
|
|
||||||
const struct arc arc)
|
|
||||||
{
|
|
||||||
assert(arc.idx < linear_network->max_num_arcs);
|
|
||||||
linear_network->arc_tail_node[arc.idx] = node_idx;
|
|
||||||
|
|
||||||
assert(node_idx < linear_network->max_num_nodes);
|
|
||||||
const struct arc first_arc = linear_network->node_adjacency_first_arc[node_idx];
|
|
||||||
|
|
||||||
linear_network->node_adjacency_next_arc[arc.idx]=first_arc;
|
|
||||||
linear_network->node_adjacency_first_arc[node_idx]=arc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the fee cost associated to this directed channel.
|
/* Get the fee cost associated to this directed channel.
|
||||||
* Cost is expressed as PPM of the payment.
|
* Cost is expressed as PPM of the payment.
|
||||||
*
|
*
|
||||||
|
@ -651,20 +578,8 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
|
||||||
const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL;
|
const size_t max_num_arcs = max_num_chans * ARCS_PER_CHANNEL;
|
||||||
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
|
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
|
||||||
|
|
||||||
linear_network->max_num_arcs = max_num_arcs;
|
linear_network->graph =
|
||||||
linear_network->max_num_nodes = max_num_nodes;
|
graph_new(ctx, max_num_nodes, max_num_arcs, ARC_DUAL_BITOFF);
|
||||||
|
|
||||||
linear_network->arc_tail_node = tal_arr(linear_network,u32,max_num_arcs);
|
|
||||||
for(size_t i=0;i<max_num_arcs;++i)
|
|
||||||
linear_network->arc_tail_node[i]=INVALID_INDEX;
|
|
||||||
|
|
||||||
linear_network->node_adjacency_next_arc = tal_arr(linear_network,struct arc,max_num_arcs);
|
|
||||||
for(size_t i=0;i<max_num_arcs;++i)
|
|
||||||
linear_network->node_adjacency_next_arc[i].idx=INVALID_INDEX;
|
|
||||||
|
|
||||||
linear_network->node_adjacency_first_arc = tal_arr(linear_network,struct arc,max_num_nodes);
|
|
||||||
for(size_t i=0;i<max_num_nodes;++i)
|
|
||||||
linear_network->node_adjacency_first_arc[i].idx=INVALID_INDEX;
|
|
||||||
|
|
||||||
linear_network->arc_prob_cost = tal_arr(linear_network,double,max_num_arcs);
|
linear_network->arc_prob_cost = tal_arr(linear_network,double,max_num_arcs);
|
||||||
for(size_t i=0;i<max_num_arcs;++i)
|
for(size_t i=0;i<max_num_arcs;++i)
|
||||||
|
@ -722,25 +637,24 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
|
||||||
// when the `i` hits the `next` node.
|
// when the `i` hits the `next` node.
|
||||||
for(size_t k=0;k<CHANNEL_PARTS;++k)
|
for(size_t k=0;k<CHANNEL_PARTS;++k)
|
||||||
{
|
{
|
||||||
// if(capacity[k]==0)continue;
|
/* FIXME: Can we prune arcs with 0 capacity?
|
||||||
|
* if(capacity[k]==0)continue; */
|
||||||
|
|
||||||
struct arc arc = arc_from_parts(chan_id, half, k, false);
|
struct arc arc = arc_from_parts(chan_id, half, k, false);
|
||||||
|
|
||||||
linear_network_add_adjacenct_arc(linear_network,node_id,arc);
|
graph_add_arc(linear_network->graph, arc,
|
||||||
|
node_obj(node_id),
|
||||||
|
node_obj(next_id));
|
||||||
|
|
||||||
linear_network->capacity[arc.idx] = capacity[k];
|
linear_network->capacity[arc.idx] = capacity[k];
|
||||||
linear_network->arc_prob_cost[arc.idx] = prob_cost[k];
|
linear_network->arc_prob_cost[arc.idx] = prob_cost[k];
|
||||||
|
|
||||||
linear_network->arc_fee_cost[arc.idx] = fee_cost;
|
linear_network->arc_fee_cost[arc.idx] = fee_cost;
|
||||||
|
|
||||||
// + the respective dual
|
// + the respective dual
|
||||||
struct arc dual = arc_dual(arc);
|
struct arc dual = arc_dual(linear_network->graph, arc);
|
||||||
|
|
||||||
linear_network_add_adjacenct_arc(linear_network,next_id,dual);
|
|
||||||
|
|
||||||
linear_network->capacity[dual.idx] = 0;
|
linear_network->capacity[dual.idx] = 0;
|
||||||
linear_network->arc_prob_cost[dual.idx] = -prob_cost[k];
|
linear_network->arc_prob_cost[dual.idx] = -prob_cost[k];
|
||||||
|
|
||||||
linear_network->arc_fee_cost[dual.idx] = -fee_cost;
|
linear_network->arc_fee_cost[dual.idx] = -fee_cost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -749,321 +663,6 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
|
||||||
return linear_network;
|
return linear_network;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(eduardo): unit test this
|
|
||||||
/* Finds an admissible path from source to target, traversing arcs in the
|
|
||||||
* residual network with capacity greater than 0.
|
|
||||||
* The path is encoded into prev, which contains the idx of the arcs that are
|
|
||||||
* traversed. */
|
|
||||||
|
|
||||||
/* Note we eschew tmpctx here, as this can be called multiple times! */
|
|
||||||
static bool
|
|
||||||
find_admissible_path(const tal_t *working_ctx,
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
const struct residual_network *residual_network,
|
|
||||||
const u32 source, const u32 target, struct arc *prev)
|
|
||||||
{
|
|
||||||
bool target_found = false;
|
|
||||||
/* Simple linear queue of node indexes */
|
|
||||||
u32 *queue = tal_arr(working_ctx, u32, linear_network->max_num_arcs);
|
|
||||||
size_t qstart, qend, prev_len = tal_count(prev);
|
|
||||||
|
|
||||||
for(size_t i=0;i<prev_len;++i)
|
|
||||||
prev[i].idx=INVALID_INDEX;
|
|
||||||
|
|
||||||
// The graph is dense, and the farthest node is just a few hops away,
|
|
||||||
// hence let's BFS search.
|
|
||||||
queue[0] = source;
|
|
||||||
qstart = 0;
|
|
||||||
qend = 1;
|
|
||||||
|
|
||||||
while (qstart < qend) {
|
|
||||||
u32 cur = queue[qstart++];
|
|
||||||
|
|
||||||
if(cur==target)
|
|
||||||
{
|
|
||||||
target_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(struct arc arc = node_adjacency_begin(linear_network,cur);
|
|
||||||
!node_adjacency_end(arc);
|
|
||||||
arc = node_adjacency_next(linear_network,arc))
|
|
||||||
{
|
|
||||||
// check if this arc is traversable
|
|
||||||
if(residual_network->cap[arc.idx] <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
u32 next = arc_head(linear_network,arc);
|
|
||||||
|
|
||||||
assert(next < prev_len);
|
|
||||||
|
|
||||||
// if that node has been seen previously
|
|
||||||
if(prev[next].idx!=INVALID_INDEX)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
prev[next] = arc;
|
|
||||||
assert(qend < linear_network->max_num_arcs);
|
|
||||||
queue[qend++] = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the max amount of flow one can send from source to target along the path
|
|
||||||
* encoded in `prev`. */
|
|
||||||
static s64 get_augmenting_flow(
|
|
||||||
const struct linear_network* linear_network,
|
|
||||||
const struct residual_network *residual_network,
|
|
||||||
const u32 source,
|
|
||||||
const u32 target,
|
|
||||||
const struct arc *prev)
|
|
||||||
{
|
|
||||||
s64 flow = INFINITE;
|
|
||||||
|
|
||||||
u32 cur = target;
|
|
||||||
while(cur!=source)
|
|
||||||
{
|
|
||||||
assert(cur<tal_count(prev));
|
|
||||||
const struct arc arc = prev[cur];
|
|
||||||
flow = MIN(flow , residual_network->cap[arc.idx]);
|
|
||||||
|
|
||||||
// we are traversing in the opposite direction to the flow,
|
|
||||||
// hence the next node is at the tail of the arc.
|
|
||||||
cur = arc_tail(linear_network,arc);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(flow<INFINITE && flow>0);
|
|
||||||
return flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Augment a `flow` amount along the path defined by `prev`.*/
|
|
||||||
static void augment_flow(
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
struct residual_network *residual_network,
|
|
||||||
const u32 source,
|
|
||||||
const u32 target,
|
|
||||||
const struct arc *prev,
|
|
||||||
s64 flow)
|
|
||||||
{
|
|
||||||
u32 cur = target;
|
|
||||||
|
|
||||||
while(cur!=source)
|
|
||||||
{
|
|
||||||
assert(cur < tal_count(prev));
|
|
||||||
const struct arc arc = prev[cur];
|
|
||||||
const struct arc dual = arc_dual(arc);
|
|
||||||
|
|
||||||
assert(arc.idx < tal_count(residual_network->cap));
|
|
||||||
assert(dual.idx < tal_count(residual_network->cap));
|
|
||||||
|
|
||||||
residual_network->cap[arc.idx] -= flow;
|
|
||||||
residual_network->cap[dual.idx] += flow;
|
|
||||||
|
|
||||||
assert(residual_network->cap[arc.idx] >=0 );
|
|
||||||
|
|
||||||
// we are traversing in the opposite direction to the flow,
|
|
||||||
// hence the next node is at the tail of the arc.
|
|
||||||
cur = arc_tail(linear_network,arc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO(eduardo): unit test this
|
|
||||||
/* Finds any flow that satisfy the capacity and balance constraints of the
|
|
||||||
* uncertainty network. For the balance function condition we have:
|
|
||||||
* balance(source) = - balance(target) = amount
|
|
||||||
* balance(node) = 0 , for every other node
|
|
||||||
* Returns an error code if no feasible flow is found.
|
|
||||||
*
|
|
||||||
* 13/04/2023 This implementation uses a simple augmenting path approach.
|
|
||||||
* */
|
|
||||||
static bool find_feasible_flow(const tal_t *working_ctx,
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
struct residual_network *residual_network,
|
|
||||||
const u32 source, const u32 target, s64 amount)
|
|
||||||
{
|
|
||||||
assert(amount>=0);
|
|
||||||
|
|
||||||
/* path information
|
|
||||||
* prev: is the id of the arc that lead to the node. */
|
|
||||||
struct arc *prev = tal_arr(working_ctx,struct arc,linear_network->max_num_nodes);
|
|
||||||
|
|
||||||
while(amount>0)
|
|
||||||
{
|
|
||||||
// find a path from source to target
|
|
||||||
if (!find_admissible_path(working_ctx,
|
|
||||||
linear_network,
|
|
||||||
residual_network, source, target,
|
|
||||||
prev)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// traverse the path and see how much flow we can send
|
|
||||||
s64 delta = get_augmenting_flow(linear_network,
|
|
||||||
residual_network,
|
|
||||||
source,target,prev);
|
|
||||||
|
|
||||||
// commit that flow to the path
|
|
||||||
delta = MIN(amount,delta);
|
|
||||||
assert(delta>0 && delta<=amount);
|
|
||||||
|
|
||||||
augment_flow(linear_network,residual_network,source,target,prev,delta);
|
|
||||||
amount -= delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(eduardo): unit test this
|
|
||||||
/* Similar to `find_admissible_path` but use Dijkstra to optimize the distance
|
|
||||||
* label. Stops when the target is hit. */
|
|
||||||
static bool find_optimal_path(const tal_t *working_ctx,
|
|
||||||
struct dijkstra *dijkstra,
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
const struct residual_network *residual_network,
|
|
||||||
const u32 source, const u32 target,
|
|
||||||
struct arc *prev)
|
|
||||||
{
|
|
||||||
bool target_found = false;
|
|
||||||
|
|
||||||
bitmap *visited = tal_arrz(working_ctx, bitmap,
|
|
||||||
BITMAP_NWORDS(linear_network->max_num_nodes));
|
|
||||||
for(size_t i=0;i<tal_count(prev);++i)
|
|
||||||
prev[i].idx=INVALID_INDEX;
|
|
||||||
|
|
||||||
const s64 *const distance=dijkstra_distance_data(dijkstra);
|
|
||||||
|
|
||||||
dijkstra_init(dijkstra);
|
|
||||||
dijkstra_update(dijkstra,source,0);
|
|
||||||
|
|
||||||
while(!dijkstra_empty(dijkstra))
|
|
||||||
{
|
|
||||||
u32 cur = dijkstra_top(dijkstra);
|
|
||||||
dijkstra_pop(dijkstra);
|
|
||||||
|
|
||||||
if(bitmap_test_bit(visited,cur))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bitmap_set_bit(visited,cur);
|
|
||||||
|
|
||||||
if(cur==target)
|
|
||||||
{
|
|
||||||
target_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(struct arc arc = node_adjacency_begin(linear_network,cur);
|
|
||||||
!node_adjacency_end(arc);
|
|
||||||
arc = node_adjacency_next(linear_network,arc))
|
|
||||||
{
|
|
||||||
// check if this arc is traversable
|
|
||||||
if(residual_network->cap[arc.idx] <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
u32 next = arc_head(linear_network,arc);
|
|
||||||
|
|
||||||
s64 cij = residual_network->cost[arc.idx]
|
|
||||||
- residual_network->potential[cur]
|
|
||||||
+ residual_network->potential[next];
|
|
||||||
|
|
||||||
// Dijkstra only works with non-negative weights
|
|
||||||
assert(cij>=0);
|
|
||||||
|
|
||||||
if(distance[next]<=distance[cur]+cij)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dijkstra_update(dijkstra,next,distance[cur]+cij);
|
|
||||||
prev[next]=arc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set zero flow in the residual network. */
|
|
||||||
static void zero_flow(
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
struct residual_network *residual_network)
|
|
||||||
{
|
|
||||||
for(u32 node=0;node<linear_network->max_num_nodes;++node)
|
|
||||||
{
|
|
||||||
residual_network->potential[node]=0;
|
|
||||||
for(struct arc arc=node_adjacency_begin(linear_network,node);
|
|
||||||
!node_adjacency_end(arc);
|
|
||||||
arc = node_adjacency_next(linear_network,arc))
|
|
||||||
{
|
|
||||||
if(arc_is_dual(arc))continue;
|
|
||||||
|
|
||||||
struct arc dual = arc_dual(arc);
|
|
||||||
|
|
||||||
residual_network->cap[arc.idx] = linear_network->capacity[arc.idx];
|
|
||||||
residual_network->cap[dual.idx] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(eduardo): unit test this
|
|
||||||
/* Starting from a feasible flow (satisfies the balance and capacity
|
|
||||||
* constraints), find a solution that minimizes the network->cost function.
|
|
||||||
*
|
|
||||||
* TODO(eduardo) The MCF must be called several times until we get a good
|
|
||||||
* compromise between fees and probabilities. Instead of re-computing the MCF at
|
|
||||||
* each step, we might use the previous flow result, which is not optimal in the
|
|
||||||
* current iteration but I might be not too far from the truth.
|
|
||||||
* It comes to mind to use cycle cancelling. */
|
|
||||||
static bool optimize_mcf(const tal_t *working_ctx,
|
|
||||||
struct dijkstra *dijkstra,
|
|
||||||
const struct linear_network *linear_network,
|
|
||||||
struct residual_network *residual_network,
|
|
||||||
const u32 source, const u32 target, const s64 amount)
|
|
||||||
{
|
|
||||||
assert(amount>=0);
|
|
||||||
|
|
||||||
zero_flow(linear_network,residual_network);
|
|
||||||
struct arc *prev = tal_arr(working_ctx,struct arc,linear_network->max_num_nodes);
|
|
||||||
|
|
||||||
const s64 *const distance = dijkstra_distance_data(dijkstra);
|
|
||||||
|
|
||||||
s64 remaining_amount = amount;
|
|
||||||
|
|
||||||
while(remaining_amount>0)
|
|
||||||
{
|
|
||||||
if (!find_optimal_path(working_ctx, dijkstra, linear_network,
|
|
||||||
residual_network, source, target, prev)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// traverse the path and see how much flow we can send
|
|
||||||
s64 delta = get_augmenting_flow(linear_network,residual_network,source,target,prev);
|
|
||||||
|
|
||||||
// commit that flow to the path
|
|
||||||
delta = MIN(remaining_amount,delta);
|
|
||||||
assert(delta>0 && delta<=remaining_amount);
|
|
||||||
|
|
||||||
augment_flow(linear_network,residual_network,source,target,prev,delta);
|
|
||||||
remaining_amount -= delta;
|
|
||||||
|
|
||||||
// update potentials
|
|
||||||
for(u32 n=0;n<linear_network->max_num_nodes;++n)
|
|
||||||
{
|
|
||||||
// see page 323 of Ahuja-Magnanti-Orlin
|
|
||||||
residual_network->potential[n] -= MIN(distance[target],distance[n]);
|
|
||||||
|
|
||||||
/* Notice:
|
|
||||||
* if node i is permanently labeled we have
|
|
||||||
* d_i<=d_t
|
|
||||||
* which implies
|
|
||||||
* MIN(d_i,d_t) = d_i
|
|
||||||
* if node i is temporarily labeled we have
|
|
||||||
* d_i>=d_t
|
|
||||||
* which implies
|
|
||||||
* MIN(d_i,d_t) = d_t
|
|
||||||
* */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flow on directed channels
|
// flow on directed channels
|
||||||
struct chan_flow
|
struct chan_flow
|
||||||
{
|
{
|
||||||
|
@ -1074,11 +673,11 @@ struct chan_flow
|
||||||
* positive balance (returns a node idx with positive balance)
|
* positive balance (returns a node idx with positive balance)
|
||||||
* or we discover a cycle (returns a node idx with 0 balance).
|
* or we discover a cycle (returns a node idx with 0 balance).
|
||||||
* */
|
* */
|
||||||
static u32 find_path_or_cycle(
|
static struct node find_path_or_cycle(
|
||||||
const tal_t *working_ctx,
|
const tal_t *working_ctx,
|
||||||
const struct gossmap *gossmap,
|
const struct gossmap *gossmap,
|
||||||
const struct chan_flow *chan_flow,
|
const struct chan_flow *chan_flow,
|
||||||
const u32 start_idx,
|
const struct node source,
|
||||||
const s64 *balance,
|
const s64 *balance,
|
||||||
|
|
||||||
const struct gossmap_chan **prev_chan,
|
const struct gossmap_chan **prev_chan,
|
||||||
|
@ -1088,8 +687,8 @@ static u32 find_path_or_cycle(
|
||||||
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
|
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
|
||||||
bitmap *visited =
|
bitmap *visited =
|
||||||
tal_arrz(working_ctx, bitmap, BITMAP_NWORDS(max_num_nodes));
|
tal_arrz(working_ctx, bitmap, BITMAP_NWORDS(max_num_nodes));
|
||||||
u32 final_idx = start_idx;
|
u32 final_idx = source.idx;
|
||||||
bitmap_set_bit(visited, start_idx);
|
bitmap_set_bit(visited, final_idx);
|
||||||
|
|
||||||
/* It is guaranteed to halt, because we either find a node with
|
/* It is guaranteed to halt, because we either find a node with
|
||||||
* balance[]>0 or we hit a node twice and we stop. */
|
* balance[]>0 or we hit a node twice and we stop. */
|
||||||
|
@ -1110,9 +709,9 @@ static u32 find_path_or_cycle(
|
||||||
|
|
||||||
/* follow the flow */
|
/* follow the flow */
|
||||||
if (chan_flow[c_idx].half[dir] > 0) {
|
if (chan_flow[c_idx].half[dir] > 0) {
|
||||||
const struct gossmap_node *next =
|
const struct gossmap_node *n =
|
||||||
gossmap_nth_node(gossmap, c, !dir);
|
gossmap_nth_node(gossmap, c, !dir);
|
||||||
u32 next_idx = gossmap_node_idx(gossmap, next);
|
u32 next_idx = gossmap_node_idx(gossmap, n);
|
||||||
|
|
||||||
prev_dir[next_idx] = dir;
|
prev_dir[next_idx] = dir;
|
||||||
prev_chan[next_idx] = c;
|
prev_chan[next_idx] = c;
|
||||||
|
@ -1135,7 +734,7 @@ static u32 find_path_or_cycle(
|
||||||
}
|
}
|
||||||
bitmap_set_bit(visited, updated_idx);
|
bitmap_set_bit(visited, updated_idx);
|
||||||
}
|
}
|
||||||
return final_idx;
|
return node_obj(final_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct list_data
|
struct list_data
|
||||||
|
@ -1149,20 +748,21 @@ struct list_data
|
||||||
* the channels allocation. */
|
* the channels allocation. */
|
||||||
static struct flow *substract_flow(const tal_t *ctx,
|
static struct flow *substract_flow(const tal_t *ctx,
|
||||||
const struct gossmap *gossmap,
|
const struct gossmap *gossmap,
|
||||||
const u32 start_idx, const u32 final_idx,
|
const struct node source,
|
||||||
|
const struct node sink,
|
||||||
s64 *balance, struct chan_flow *chan_flow,
|
s64 *balance, struct chan_flow *chan_flow,
|
||||||
const u32 *prev_idx, const int *prev_dir,
|
const u32 *prev_idx, const int *prev_dir,
|
||||||
const struct gossmap_chan *const *prev_chan)
|
const struct gossmap_chan *const *prev_chan)
|
||||||
{
|
{
|
||||||
assert(balance[start_idx] < 0);
|
assert(balance[source.idx] < 0);
|
||||||
assert(balance[final_idx] > 0);
|
assert(balance[sink.idx] > 0);
|
||||||
s64 delta = -balance[start_idx];
|
s64 delta = -balance[source.idx];
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
delta = MIN(delta, balance[final_idx]);
|
delta = MIN(delta, balance[sink.idx]);
|
||||||
|
|
||||||
/* We can only walk backwards, now get me the legth of the path and the
|
/* We can only walk backwards, now get me the legth of the path and the
|
||||||
* max flow we can send through this route. */
|
* max flow we can send through this route. */
|
||||||
for (u32 cur_idx = final_idx; cur_idx != start_idx;
|
for (u32 cur_idx = sink.idx; cur_idx != source.idx;
|
||||||
cur_idx = prev_idx[cur_idx]) {
|
cur_idx = prev_idx[cur_idx]) {
|
||||||
assert(cur_idx != INVALID_INDEX);
|
assert(cur_idx != INVALID_INDEX);
|
||||||
const int dir = prev_dir[cur_idx];
|
const int dir = prev_dir[cur_idx];
|
||||||
|
@ -1183,9 +783,9 @@ static struct flow *substract_flow(const tal_t *ctx,
|
||||||
|
|
||||||
/* Walk again and substract the flow value (delta). */
|
/* Walk again and substract the flow value (delta). */
|
||||||
assert(delta > 0);
|
assert(delta > 0);
|
||||||
balance[start_idx] += delta;
|
balance[source.idx] += delta;
|
||||||
balance[final_idx] -= delta;
|
balance[sink.idx] -= delta;
|
||||||
for (u32 cur_idx = final_idx; cur_idx != start_idx;
|
for (u32 cur_idx = sink.idx; cur_idx != source.idx;
|
||||||
cur_idx = prev_idx[cur_idx]) {
|
cur_idx = prev_idx[cur_idx]) {
|
||||||
const int dir = prev_dir[cur_idx];
|
const int dir = prev_dir[cur_idx];
|
||||||
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
||||||
|
@ -1204,7 +804,8 @@ static struct flow *substract_flow(const tal_t *ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Substract a flow cycle from the channel allocation. */
|
/* Substract a flow cycle from the channel allocation. */
|
||||||
static void substract_cycle(const struct gossmap *gossmap, const u32 final_idx,
|
static void substract_cycle(const struct gossmap *gossmap,
|
||||||
|
const struct node sink,
|
||||||
struct chan_flow *chan_flow, const u32 *prev_idx,
|
struct chan_flow *chan_flow, const u32 *prev_idx,
|
||||||
const int *prev_dir,
|
const int *prev_dir,
|
||||||
const struct gossmap_chan *const *prev_chan)
|
const struct gossmap_chan *const *prev_chan)
|
||||||
|
@ -1213,7 +814,7 @@ static void substract_cycle(const struct gossmap *gossmap, const u32 final_idx,
|
||||||
u32 cur_idx;
|
u32 cur_idx;
|
||||||
|
|
||||||
/* Compute greatest flow in this cycle. */
|
/* Compute greatest flow in this cycle. */
|
||||||
for (cur_idx = final_idx; cur_idx!=INVALID_INDEX;) {
|
for (cur_idx = sink.idx; cur_idx!=INVALID_INDEX;) {
|
||||||
const int dir = prev_dir[cur_idx];
|
const int dir = prev_dir[cur_idx];
|
||||||
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
||||||
const u32 chan_idx = gossmap_chan_idx(gossmap, chan);
|
const u32 chan_idx = gossmap_chan_idx(gossmap, chan);
|
||||||
|
@ -1221,17 +822,17 @@ static void substract_cycle(const struct gossmap *gossmap, const u32 final_idx,
|
||||||
delta = MIN(delta, chan_flow[chan_idx].half[dir]);
|
delta = MIN(delta, chan_flow[chan_idx].half[dir]);
|
||||||
|
|
||||||
cur_idx = prev_idx[cur_idx];
|
cur_idx = prev_idx[cur_idx];
|
||||||
if (cur_idx == final_idx)
|
if (cur_idx == sink.idx)
|
||||||
/* we have come back full circle */
|
/* we have come back full circle */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(cur_idx==final_idx);
|
assert(cur_idx==sink.idx);
|
||||||
|
|
||||||
/* Walk again and substract the flow value (delta). */
|
/* Walk again and substract the flow value (delta). */
|
||||||
assert(delta < INFINITE);
|
assert(delta < INFINITE);
|
||||||
assert(delta > 0);
|
assert(delta > 0);
|
||||||
|
|
||||||
for (cur_idx = final_idx;cur_idx!=INVALID_INDEX;) {
|
for (cur_idx = sink.idx;cur_idx!=INVALID_INDEX;) {
|
||||||
const int dir = prev_dir[cur_idx];
|
const int dir = prev_dir[cur_idx];
|
||||||
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
const struct gossmap_chan *const chan = prev_chan[cur_idx];
|
||||||
const u32 chan_idx = gossmap_chan_idx(gossmap, chan);
|
const u32 chan_idx = gossmap_chan_idx(gossmap, chan);
|
||||||
|
@ -1239,11 +840,11 @@ static void substract_cycle(const struct gossmap *gossmap, const u32 final_idx,
|
||||||
chan_flow[chan_idx].half[dir] -= delta;
|
chan_flow[chan_idx].half[dir] -= delta;
|
||||||
|
|
||||||
cur_idx = prev_idx[cur_idx];
|
cur_idx = prev_idx[cur_idx];
|
||||||
if (cur_idx == final_idx)
|
if (cur_idx == sink.idx)
|
||||||
/* we have come back full circle */
|
/* we have come back full circle */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(cur_idx==final_idx);
|
assert(cur_idx==sink.idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Given a flow in the residual network, build a set of payment flows in the
|
/* Given a flow in the residual network, build a set of payment flows in the
|
||||||
|
@ -1268,7 +869,7 @@ get_flow_paths(const tal_t *ctx,
|
||||||
|
|
||||||
|
|
||||||
int *prev_dir = tal_arr(working_ctx,int,max_num_nodes);
|
int *prev_dir = tal_arr(working_ctx,int,max_num_nodes);
|
||||||
u32 *prev_idx = tal_arr(working_ctx,u32,max_num_nodes);
|
u32 *prev_idx = tal_arr(working_ctx, u32, max_num_nodes);
|
||||||
|
|
||||||
for (u32 node_idx = 0; node_idx < max_num_nodes; node_idx++)
|
for (u32 node_idx = 0; node_idx < max_num_nodes; node_idx++)
|
||||||
prev_idx[node_idx] = INVALID_INDEX;
|
prev_idx[node_idx] = INVALID_INDEX;
|
||||||
|
@ -1276,56 +877,52 @@ get_flow_paths(const tal_t *ctx,
|
||||||
// Convert the arc based residual network flow into a flow in the
|
// Convert the arc based residual network flow into a flow in the
|
||||||
// directed channel network.
|
// directed channel network.
|
||||||
// Compute balance on the nodes.
|
// Compute balance on the nodes.
|
||||||
for(u32 n = 0;n<max_num_nodes;++n)
|
const struct graph *graph = linear_network->graph;
|
||||||
{
|
for (struct node n = {.idx = 0}; n.idx < max_num_nodes; n.idx++) {
|
||||||
for(struct arc arc = node_adjacency_begin(linear_network,n);
|
for(struct arc arc = node_adjacency_begin(graph,n);
|
||||||
!node_adjacency_end(arc);
|
!node_adjacency_end(arc);
|
||||||
arc = node_adjacency_next(linear_network,arc))
|
arc = node_adjacency_next(graph,arc))
|
||||||
{
|
{
|
||||||
if(arc_is_dual(arc))
|
if(arc_is_dual(graph, arc))
|
||||||
continue;
|
continue;
|
||||||
u32 m = arc_head(linear_network,arc);
|
struct node m = arc_head(graph,arc);
|
||||||
s64 flow = get_arc_flow(residual_network,arc);
|
s64 flow = get_arc_flow(residual_network,
|
||||||
|
graph, arc);
|
||||||
u32 chanidx;
|
u32 chanidx;
|
||||||
int chandir;
|
int chandir;
|
||||||
|
|
||||||
balance[n] -= flow;
|
balance[n.idx] -= flow;
|
||||||
balance[m] += flow;
|
balance[m.idx] += flow;
|
||||||
|
|
||||||
arc_to_parts(arc, &chanidx, &chandir, NULL, NULL);
|
arc_to_parts(arc, &chanidx, &chandir, NULL, NULL);
|
||||||
chan_flow[chanidx].half[chandir] +=flow;
|
chan_flow[chanidx].half[chandir] +=flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select all nodes with negative balance and find a flow that reaches a
|
// Select all nodes with negative balance and find a flow that reaches a
|
||||||
// positive balance node.
|
// positive balance node.
|
||||||
for(u32 node_idx=0;node_idx<max_num_nodes;++node_idx)
|
for (struct node source = {.idx = 0}; source.idx < max_num_nodes;
|
||||||
{
|
source.idx++) {
|
||||||
// this node has negative balance, flows leaves from here
|
// this node has negative balance, flows leaves from here
|
||||||
while(balance[node_idx]<0)
|
while (balance[source.idx] < 0) {
|
||||||
{
|
prev_chan[source.idx] = NULL;
|
||||||
prev_chan[node_idx] = NULL;
|
struct node sink = find_path_or_cycle(
|
||||||
u32 final_idx = find_path_or_cycle(
|
working_ctx, rq->gossmap, chan_flow, source,
|
||||||
working_ctx,
|
balance, prev_chan, prev_dir, prev_idx);
|
||||||
rq->gossmap, chan_flow, node_idx, balance,
|
|
||||||
prev_chan, prev_dir, prev_idx);
|
|
||||||
|
|
||||||
if (balance[final_idx] > 0)
|
if (balance[sink.idx] > 0)
|
||||||
/* case 1. found a path */
|
/* case 1. found a path */
|
||||||
{
|
{
|
||||||
struct flow *fp = substract_flow(
|
struct flow *fp = substract_flow(
|
||||||
flows, rq->gossmap, node_idx, final_idx,
|
flows, rq->gossmap, source, sink, balance,
|
||||||
balance, chan_flow, prev_idx, prev_dir,
|
chan_flow, prev_idx, prev_dir, prev_chan);
|
||||||
prev_chan);
|
|
||||||
|
|
||||||
tal_arr_expand(&flows, fp);
|
tal_arr_expand(&flows, fp);
|
||||||
} else
|
} else
|
||||||
/* case 2. found a cycle */
|
/* case 2. found a cycle */
|
||||||
{
|
{
|
||||||
substract_cycle(rq->gossmap, final_idx,
|
substract_cycle(rq->gossmap, sink, chan_flow,
|
||||||
chan_flow, prev_idx, prev_dir,
|
prev_idx, prev_dir, prev_chan);
|
||||||
prev_chan);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1357,7 +954,6 @@ struct flow **minflow(const tal_t *ctx,
|
||||||
* as we can be called multiple times without cleaning tmpctx! */
|
* as we can be called multiple times without cleaning tmpctx! */
|
||||||
tal_t *working_ctx = tal(NULL, char);
|
tal_t *working_ctx = tal(NULL, char);
|
||||||
struct pay_parameters *params = tal(working_ctx, struct pay_parameters);
|
struct pay_parameters *params = tal(working_ctx, struct pay_parameters);
|
||||||
struct dijkstra *dijkstra;
|
|
||||||
|
|
||||||
params->rq = rq;
|
params->rq = rq;
|
||||||
params->source = source;
|
params->source = source;
|
||||||
|
@ -1373,9 +969,6 @@ struct flow **minflow(const tal_t *ctx,
|
||||||
params->cost_fraction[i]=
|
params->cost_fraction[i]=
|
||||||
log((1-CHANNEL_PIVOTS[i-1])/(1-CHANNEL_PIVOTS[i]))
|
log((1-CHANNEL_PIVOTS[i-1])/(1-CHANNEL_PIVOTS[i]))
|
||||||
/params->cap_fraction[i];
|
/params->cap_fraction[i];
|
||||||
|
|
||||||
// printf("channel part: %ld, fraction: %lf, cost_fraction: %lf\n",
|
|
||||||
// i,params->cap_fraction[i],params->cost_fraction[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params->delay_feefactor = delay_feefactor;
|
params->delay_feefactor = delay_feefactor;
|
||||||
|
@ -1383,13 +976,14 @@ struct flow **minflow(const tal_t *ctx,
|
||||||
|
|
||||||
// build the uncertainty network with linearization and residual arcs
|
// build the uncertainty network with linearization and residual arcs
|
||||||
struct linear_network *linear_network= init_linear_network(working_ctx, params);
|
struct linear_network *linear_network= init_linear_network(working_ctx, params);
|
||||||
|
const struct graph *graph = linear_network->graph;
|
||||||
|
const size_t max_num_arcs = graph_max_num_arcs(graph);
|
||||||
|
const size_t max_num_nodes = graph_max_num_nodes(graph);
|
||||||
struct residual_network *residual_network =
|
struct residual_network *residual_network =
|
||||||
alloc_residual_network(working_ctx, linear_network->max_num_nodes,
|
alloc_residual_network(working_ctx, max_num_nodes, max_num_arcs);
|
||||||
linear_network->max_num_arcs);
|
|
||||||
dijkstra = dijkstra_new(working_ctx, gossmap_max_node_idx(rq->gossmap));
|
|
||||||
|
|
||||||
const u32 target_idx = gossmap_node_idx(rq->gossmap,target);
|
const struct node dst = {.idx = gossmap_node_idx(rq->gossmap, target)};
|
||||||
const u32 source_idx = gossmap_node_idx(rq->gossmap,source);
|
const struct node src = {.idx = gossmap_node_idx(rq->gossmap, source)};
|
||||||
|
|
||||||
init_residual_network(linear_network,residual_network);
|
init_residual_network(linear_network,residual_network);
|
||||||
|
|
||||||
|
@ -1409,20 +1003,25 @@ struct flow **minflow(const tal_t *ctx,
|
||||||
* flow units. */
|
* flow units. */
|
||||||
const u64 pay_amount_sats = (params->amount.millisatoshis + 999)/1000; /* Raw: minflow */
|
const u64 pay_amount_sats = (params->amount.millisatoshis + 999)/1000; /* Raw: minflow */
|
||||||
|
|
||||||
if (!find_feasible_flow(working_ctx, linear_network, residual_network,
|
if (!simple_feasibleflow(working_ctx, linear_network->graph, src, dst,
|
||||||
source_idx, target_idx, pay_amount_sats)) {
|
residual_network->cap, pay_amount_sats)) {
|
||||||
tal_free(working_ctx);
|
rq_log(tmpctx, rq, LOG_INFORM,
|
||||||
return NULL;
|
"%s failed: unable to find a feasible flow.", __func__);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
combine_cost_function(working_ctx, linear_network, residual_network,
|
combine_cost_function(working_ctx, linear_network, residual_network,
|
||||||
rq->biases, mu);
|
rq->biases, mu);
|
||||||
|
|
||||||
/* We solve a linear MCF problem. */
|
/* We solve a linear MCF problem. */
|
||||||
if(!optimize_mcf(working_ctx, dijkstra,linear_network,residual_network,
|
if (!mcf_refinement(working_ctx,
|
||||||
source_idx,target_idx,pay_amount_sats))
|
linear_network->graph,
|
||||||
{
|
residual_network->excess,
|
||||||
tal_free(working_ctx);
|
residual_network->cap,
|
||||||
return NULL;
|
residual_network->cost,
|
||||||
|
residual_network->potential)) {
|
||||||
|
rq_log(tmpctx, rq, LOG_BROKEN,
|
||||||
|
"%s: MCF optimization step failed", __func__);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We dissect the solution of the MCF into payment routes.
|
/* We dissect the solution of the MCF into payment routes.
|
||||||
|
@ -1430,6 +1029,16 @@ struct flow **minflow(const tal_t *ctx,
|
||||||
* channel in the routes. */
|
* channel in the routes. */
|
||||||
flow_paths = get_flow_paths(ctx, working_ctx, rq,
|
flow_paths = get_flow_paths(ctx, working_ctx, rq,
|
||||||
linear_network, residual_network);
|
linear_network, residual_network);
|
||||||
|
if(!flow_paths){
|
||||||
|
rq_log(tmpctx, rq, LOG_BROKEN,
|
||||||
|
"%s: failed to extract flow paths from the MCF solution",
|
||||||
|
__func__);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
tal_free(working_ctx);
|
tal_free(working_ctx);
|
||||||
return flow_paths;
|
return flow_paths;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
tal_free(working_ctx);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue