Skip to content

Commit 6786294

Browse files
committed
askrene: calculate k value dynamically, using medians.
While the `k=8` value worked for the current main network tests with the amounts in those tests, it wasn't robust across a wider range of values (as demonstrated when other test changes broke tests!). Time to do this properly: calculate the ratio at the time we combine them, using median values. Signed-off-by: Rusty Russell <[email protected]>
1 parent 5763c7d commit 6786294

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

plugins/askrene/mcf.c

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#include "config.h"
22
#include <assert.h>
3+
#include <ccan/asort/asort.h>
34
#include <ccan/bitmap/bitmap.h>
45
#include <ccan/list/list.h>
56
#include <ccan/tal/str/str.h>
67
#include <ccan/tal/tal.h>
78
#include <common/utils.h>
9+
#include <float.h>
810
#include <math.h>
911
#include <plugins/askrene/askrene.h>
1012
#include <plugins/askrene/dijkstra.h>
@@ -293,7 +295,6 @@ struct pay_parameters {
293295

294296
double delay_feefactor;
295297
double base_fee_penalty;
296-
double k_factor;
297298
};
298299

299300
/* Representation of the linear MCF network.
@@ -311,7 +312,8 @@ struct linear_network
311312
struct arc *node_adjacency_first_arc;
312313

313314
// probability and fee cost associated to an arc
314-
s64 *arc_prob_cost, *arc_fee_cost;
315+
double *arc_prob_cost;
316+
s64 *arc_fee_cost;
315317
s64 *capacity;
316318

317319
size_t max_num_arcs,max_num_nodes;
@@ -413,7 +415,7 @@ static void set_capacity(s64 *capacity, u64 value, u64 *cap_on_capacity)
413415
/* Split a directed channel into parts with linear cost function. */
414416
static void linearize_channel(const struct pay_parameters *params,
415417
const struct gossmap_chan *c, const int dir,
416-
s64 *capacity, s64 *cost)
418+
s64 *capacity, double *cost)
417419
{
418420
struct amount_msat mincap, maxcap;
419421

@@ -439,7 +441,7 @@ static void linearize_channel(const struct pay_parameters *params,
439441

440442
cost[i] = params->cost_fraction[i]
441443
*params->amount.millisatoshis /* Raw: linearize_channel */
442-
*params->k_factor/(b-a);
444+
/(b-a);
443445
}
444446
}
445447

@@ -482,22 +484,71 @@ static void init_residual_network(
482484
}
483485
}
484486

487+
static int cmp_u64(const u64 *a, const u64 *b, void *unused)
488+
{
489+
if (*a < *b)
490+
return -1;
491+
if (*a > *b)
492+
return 1;
493+
return 0;
494+
}
495+
496+
static int cmp_double(const double *a, const double *b, void *unused)
497+
{
498+
if (*a < *b)
499+
return -1;
500+
if (*a > *b)
501+
return 1;
502+
return 0;
503+
}
504+
505+
static double get_median_ratio(const tal_t *working_ctx,
506+
const struct linear_network* linear_network)
507+
{
508+
u64 *u64_arr = tal_arr(working_ctx, u64, linear_network->max_num_arcs/2);
509+
double *double_arr = tal_arr(working_ctx, double, linear_network->max_num_arcs/2);
510+
size_t n = 0;
511+
512+
for (struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx) {
513+
if (arc_is_dual(arc))
514+
continue;
515+
assert(n < linear_network->max_num_arcs/2);
516+
u64_arr[n] = linear_network->arc_fee_cost[arc.idx];
517+
double_arr[n] = linear_network->arc_prob_cost[arc.idx];
518+
n++;
519+
}
520+
asort(u64_arr, n, cmp_u64, NULL);
521+
asort(double_arr, n, cmp_double, NULL);
522+
523+
/* Empty network, or tiny probability, nobody cares */
524+
if (n == 0 || double_arr[n/2] < 0.001)
525+
return 1;
526+
527+
/* You need to scale arc_prob_cost by this to match arc_fee_cost */
528+
return u64_arr[n/2] / double_arr[n/2];
529+
}
530+
485531
static void combine_cost_function(
532+
const tal_t *working_ctx,
486533
const struct linear_network* linear_network,
487534
struct residual_network *residual_network,
488535
s64 mu)
489536
{
537+
/* probabilty and fee costs are not directly comparable!
538+
* Scale by ratio of (positive) medians. */
539+
const double k = get_median_ratio(working_ctx, linear_network);
540+
490541
for(struct arc arc = {0};arc.idx < linear_network->max_num_arcs; ++arc.idx)
491542
{
492543
if(arc_tail(linear_network,arc)==INVALID_INDEX)
493544
continue;
494545

495-
const s64 pcost = linear_network->arc_prob_cost[arc.idx],
496-
fcost = linear_network->arc_fee_cost[arc.idx];
546+
const double pcost = linear_network->arc_prob_cost[arc.idx];
547+
const s64 fcost = linear_network->arc_fee_cost[arc.idx];
497548

498-
assert(pcost != INFINITE);
499549
assert(fcost != INFINITE);
500-
residual_network->cost[arc.idx] = fcost*mu + (MU_MAX-mu)*pcost;
550+
assert(pcost != DBL_MAX);
551+
residual_network->cost[arc.idx] = fcost*mu + (MU_MAX-mu)*pcost*k;
501552
}
502553
}
503554

@@ -594,9 +645,9 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
594645
for(size_t i=0;i<max_num_nodes;++i)
595646
linear_network->node_adjacency_first_arc[i].idx=INVALID_INDEX;
596647

597-
linear_network->arc_prob_cost = tal_arr(linear_network,s64,max_num_arcs);
648+
linear_network->arc_prob_cost = tal_arr(linear_network,double,max_num_arcs);
598649
for(size_t i=0;i<max_num_arcs;++i)
599-
linear_network->arc_prob_cost[i]=INFINITE;
650+
linear_network->arc_prob_cost[i]=DBL_MAX;
600651

601652
linear_network->arc_fee_cost = tal_arr(linear_network,s64,max_num_arcs);
602653
for(size_t i=0;i<max_num_arcs;++i)
@@ -631,7 +682,8 @@ init_linear_network(const tal_t *ctx, const struct pay_parameters *params)
631682

632683
// `cost` is the word normally used to denote cost per
633684
// unit of flow in the context of MCF.
634-
s64 prob_cost[CHANNEL_PARTS], capacity[CHANNEL_PARTS];
685+
double prob_cost[CHANNEL_PARTS];
686+
s64 capacity[CHANNEL_PARTS];
635687

636688
// split this channel direction to obtain the arcs
637689
// that are outgoing to `node`
@@ -1307,7 +1359,6 @@ struct flow **minflow(const tal_t *ctx,
13071359

13081360
params->delay_feefactor = delay_feefactor;
13091361
params->base_fee_penalty = base_fee_penalty_estimate(amount);
1310-
params->k_factor = 8.0;
13111362

13121363
// build the uncertainty network with linearization and residual arcs
13131364
struct linear_network *linear_network= init_linear_network(working_ctx, params);
@@ -1342,7 +1393,7 @@ struct flow **minflow(const tal_t *ctx,
13421393
tal_free(working_ctx);
13431394
return NULL;
13441395
}
1345-
combine_cost_function(linear_network, residual_network, mu);
1396+
combine_cost_function(working_ctx, linear_network, residual_network, mu);
13461397

13471398
/* We solve a linear MCF problem. */
13481399
if(!optimize_mcf(working_ctx, dijkstra,linear_network,residual_network,

tests/test_askrene.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,4 +992,4 @@ def test_real_data(node_factory, bitcoind):
992992
if len(fees[n]) > len(fees[best]):
993993
best = n
994994

995-
assert (len(fees[best]), len(improved), total_first_fee, total_final_fee, percent_fee_reduction) == (9, 96, 19961361, 650099, 97)
995+
assert (len(fees[best]), len(improved), total_first_fee, total_final_fee, percent_fee_reduction) == (8, 95, 19384459, 564997, 98)

0 commit comments

Comments
 (0)