Skip to content

Commit 6e6e181

Browse files
committed
Split success_probability calculation into two separate methods
In the next commit we'll want to return floats or ints from `success_probability` depending on the callsite, so instead of duplicating the calculation logic, here we split the linear (which always uses int math) and nonlinear (which always uses float math) into separate methods, allowing us to write trivial `success_probability` wrappers that return the desired type.
1 parent e9b3327 commit 6e6e181

File tree

1 file changed

+78
-46
lines changed

1 file changed

+78
-46
lines changed

lightning/src/routing/scoring.rs

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,67 @@ fn three_f64_pow_9(a: f64, b: f64, c: f64) -> (f64, f64, f64) {
11541154
(a * a4 * a4, b * b4 * b4, c * c4 * c4)
11551155
}
11561156

1157+
#[inline(always)]
1158+
fn linear_success_probability(
1159+
total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1160+
min_zero_implies_no_successes: bool,
1161+
) -> (u64, u64) {
1162+
let (numerator, mut denominator) =
1163+
(max_liquidity_msat - total_inflight_amount_msat,
1164+
(max_liquidity_msat - min_liquidity_msat).saturating_add(1));
1165+
1166+
if min_zero_implies_no_successes && min_liquidity_msat == 0 &&
1167+
denominator < u64::max_value() / 78
1168+
{
1169+
// If we have no knowledge of the channel, scale probability down by a multiple of ~82%.
1170+
// Note that we prefer to increase the denominator rather than decrease the numerator as
1171+
// the denominator is more likely to be larger and thus provide greater precision. This is
1172+
// mostly an overoptimization but makes a large difference in tests.
1173+
denominator = denominator * 78 / 64
1174+
}
1175+
1176+
(numerator, denominator)
1177+
}
1178+
1179+
/// Returns a (numerator, denominator) pair each between 0 and 0.0078125, inclusive.
1180+
#[inline(always)]
1181+
fn nonlinear_success_probability(
1182+
total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1183+
capacity_msat: u64, min_zero_implies_no_successes: bool,
1184+
) -> (f64, f64) {
1185+
let capacity = capacity_msat as f64;
1186+
let max = (max_liquidity_msat as f64) / capacity;
1187+
let min = (min_liquidity_msat as f64) / capacity;
1188+
let amount = (total_inflight_amount_msat as f64) / capacity;
1189+
1190+
// Assume the channel has a probability density function of
1191+
// `128 * (1/256 + 9*(x - 0.5)^8)` for values from 0 to 1 (where 1 is the channel's
1192+
// full capacity). The success probability given some liquidity bounds is thus the
1193+
// integral under the curve from the amount to maximum estimated liquidity, divided by
1194+
// the same integral from the minimum to the maximum estimated liquidity bounds.
1195+
//
1196+
// Because the integral from x to y is simply
1197+
// `128*(1/256 * (y - 0.5) + (y - 0.5)^9) - 128*(1/256 * (x - 0.5) + (x - 0.5)^9), we
1198+
// can calculate the cumulative density function between the min/max bounds trivially.
1199+
// Note that we don't bother to normalize the CDF to total to 1 (using the 128
1200+
// multiple), as it will come out in the division of num / den.
1201+
let (max_norm, min_norm, amt_norm) = (max - 0.5, min - 0.5, amount - 0.5);
1202+
let (max_pow, min_pow, amt_pow) = three_f64_pow_9(max_norm, min_norm, amt_norm);
1203+
let (max_v, min_v, amt_v) = (max_pow + max_norm / 256.0, min_pow + min_norm / 256.0, amt_pow + amt_norm / 256.0);
1204+
let mut denominator = max_v - min_v;
1205+
let numerator = max_v - amt_v;
1206+
1207+
if min_zero_implies_no_successes && min_liquidity_msat == 0 {
1208+
// If we have no knowledge of the channel, scale probability down by a multiple of ~82%.
1209+
// Note that we prefer to increase the denominator rather than decrease the numerator as
1210+
// the denominator is more likely to be larger and thus provide greater precision. This is
1211+
// mostly an overoptimization but makes a large difference in tests.
1212+
denominator = denominator * 78.0 / 64.0;
1213+
}
1214+
1215+
(numerator, denominator)
1216+
}
1217+
11571218
/// Given liquidity bounds, calculates the success probability (in the form of a numerator and
11581219
/// denominator) of an HTLC. This is a key assumption in our scoring models.
11591220
///
@@ -1174,54 +1235,25 @@ fn success_probability(
11741235
debug_assert!(total_inflight_amount_msat < max_liquidity_msat);
11751236
debug_assert!(max_liquidity_msat <= capacity_msat);
11761237

1177-
let (numerator, mut denominator) =
1178-
if params.linear_success_probability {
1179-
(max_liquidity_msat - total_inflight_amount_msat,
1180-
(max_liquidity_msat - min_liquidity_msat).saturating_add(1))
1181-
} else {
1182-
let capacity = capacity_msat as f64;
1183-
let min = (min_liquidity_msat as f64) / capacity;
1184-
let max = (max_liquidity_msat as f64) / capacity;
1185-
let amount = (total_inflight_amount_msat as f64) / capacity;
1186-
1187-
// Assume the channel has a probability density function of
1188-
// `128 * (1/256 + 9*(x - 0.5)^8)` for values from 0 to 1 (where 1 is the channel's
1189-
// full capacity). The success probability given some liquidity bounds is thus the
1190-
// integral under the curve from the amount to maximum estimated liquidity, divided by
1191-
// the same integral from the minimum to the maximum estimated liquidity bounds.
1192-
//
1193-
// Because the integral from x to y is simply
1194-
// `128*(1/256 * (y - 0.5) + (y - 0.5)^9) - 128*(1/256 * (x - 0.5) + (x - 0.5)^9), we
1195-
// can calculate the cumulative density function between the min/max bounds trivially.
1196-
// Note that we don't bother to normalize the CDF to total to 1 (using the 128
1197-
// multiple), as it will come out in the division of num / den.
1198-
let (max_norm, amt_norm, min_norm) = (max - 0.5, amount - 0.5, min - 0.5);
1199-
let (max_pow, amt_pow, min_pow) = three_f64_pow_9(max_norm, amt_norm, min_norm);
1200-
let (max_v, amt_v, min_v) = (max_pow + max_norm / 256.0, amt_pow + amt_norm / 256.0, min_pow + min_norm / 256.0);
1201-
let num = max_v - amt_v;
1202-
let den = max_v - min_v;
1203-
1204-
// Because our numerator and denominator max out at 0.0078125 we need to multiply them
1205-
// by quite a large factor to get something useful (ideally in the 2^30 range).
1206-
const BILLIONISH: f64 = 1024.0 * 1024.0 * 1024.0 * 64.0;
1207-
let numerator = (num * BILLIONISH) as u64 + 1;
1208-
let denominator = (den * BILLIONISH) as u64 + 1;
1209-
debug_assert!(numerator <= 1 << 30, "Got large numerator ({}) from float {}.", numerator, num);
1210-
debug_assert!(denominator <= 1 << 30, "Got large denominator ({}) from float {}.", denominator, den);
1211-
(numerator, denominator)
1212-
};
1238+
if params.linear_success_probability {
1239+
linear_success_probability(total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, min_zero_implies_no_successes)
1240+
} else {
1241+
// We calculate the nonlinear probabilities using floats anyway, so just stub out to
1242+
// the float version and then convert to integers.
1243+
let (num, den) = nonlinear_success_probability(
1244+
total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, capacity_msat,
1245+
min_zero_implies_no_successes,
1246+
);
12131247

1214-
if min_zero_implies_no_successes && min_liquidity_msat == 0 &&
1215-
denominator < u64::max_value() / 78
1216-
{
1217-
// If we have no knowledge of the channel, scale probability down by a multiple of ~82%.
1218-
// Note that we prefer to increase the denominator rather than decrease the numerator as
1219-
// the denominator is more likely to be larger and thus provide greater precision. This is
1220-
// mostly an overoptimization but makes a large difference in tests.
1221-
denominator = denominator * 78 / 64
1248+
// Because our numerator and denominator max out at 0.0078125 we need to multiply them
1249+
// by quite a large factor to get something useful (ideally in the 2^30 range).
1250+
const BILLIONISH: f64 = 1024.0 * 1024.0 * 1024.0 * 64.0;
1251+
let numerator = (num * BILLIONISH) as u64 + 1;
1252+
let denominator = (den * BILLIONISH) as u64 + 1;
1253+
debug_assert!(numerator <= 1 << 30, "Got large numerator ({}) from float {}.", numerator, num);
1254+
debug_assert!(denominator <= 1 << 30, "Got large denominator ({}) from float {}.", denominator, den);
1255+
(numerator, denominator)
12221256
}
1223-
1224-
(numerator, denominator)
12251257
}
12261258

12271259
impl<L: Deref<Target = u64>, HT: Deref<Target = HistoricalLiquidityTracker>, T: Deref<Target = Duration>>

0 commit comments

Comments
 (0)