@@ -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
12271259impl < L : Deref < Target = u64 > , HT : Deref < Target = HistoricalLiquidityTracker > , T : Deref < Target = Duration > >
0 commit comments