@@ -1154,6 +1154,71 @@ fn three_f64_pow_9(a: f64, b: f64, c: f64) -> (f64, f64, f64) {
1154
1154
( a * a4 * a4, b * b4 * b4, c * c4 * c4)
1155
1155
}
1156
1156
1157
+ /// If we have no knowledge of the channel, we scale probability down by a multiple of ~82% for the
1158
+ /// historical model by multiplying the denominator of a success probability by this before
1159
+ /// dividing by 64.
1160
+ ///
1161
+ /// This number (as well as the PDF) was picked experimentally on probing results to maximize the
1162
+ /// log-loss of succeeding and failing hops.
1163
+ ///
1164
+ /// Note that we prefer to increase the denominator rather than decrease the numerator as the
1165
+ /// denominator is more likely to be larger and thus provide greater precision. This is mostly an
1166
+ /// overoptimization but makes a large difference in tests.
1167
+ const MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64 : u64 = 78 ;
1168
+
1169
+ #[ inline( always) ]
1170
+ fn linear_success_probability (
1171
+ total_inflight_amount_msat : u64 , min_liquidity_msat : u64 , max_liquidity_msat : u64 ,
1172
+ min_zero_implies_no_successes : bool ,
1173
+ ) -> ( u64 , u64 ) {
1174
+ let ( numerator, mut denominator) =
1175
+ ( max_liquidity_msat - total_inflight_amount_msat,
1176
+ ( max_liquidity_msat - min_liquidity_msat) . saturating_add ( 1 ) ) ;
1177
+
1178
+ if min_zero_implies_no_successes && min_liquidity_msat == 0 &&
1179
+ denominator < u64:: max_value ( ) / MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64
1180
+ {
1181
+ denominator = denominator * MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64 / 64
1182
+ }
1183
+
1184
+ ( numerator, denominator)
1185
+ }
1186
+
1187
+ /// Returns a (numerator, denominator) pair each between 0 and 0.0078125, inclusive.
1188
+ #[ inline( always) ]
1189
+ fn nonlinear_success_probability (
1190
+ total_inflight_amount_msat : u64 , min_liquidity_msat : u64 , max_liquidity_msat : u64 ,
1191
+ capacity_msat : u64 , min_zero_implies_no_successes : bool ,
1192
+ ) -> ( f64 , f64 ) {
1193
+ let capacity = capacity_msat as f64 ;
1194
+ let max = ( max_liquidity_msat as f64 ) / capacity;
1195
+ let min = ( min_liquidity_msat as f64 ) / capacity;
1196
+ let amount = ( total_inflight_amount_msat as f64 ) / capacity;
1197
+
1198
+ // Assume the channel has a probability density function of
1199
+ // `128 * (1/256 + 9*(x - 0.5)^8)` for values from 0 to 1 (where 1 is the channel's
1200
+ // full capacity). The success probability given some liquidity bounds is thus the
1201
+ // integral under the curve from the amount to maximum estimated liquidity, divided by
1202
+ // the same integral from the minimum to the maximum estimated liquidity bounds.
1203
+ //
1204
+ // Because the integral from x to y is simply
1205
+ // `128*(1/256 * (y - 0.5) + (y - 0.5)^9) - 128*(1/256 * (x - 0.5) + (x - 0.5)^9), we
1206
+ // can calculate the cumulative density function between the min/max bounds trivially.
1207
+ // Note that we don't bother to normalize the CDF to total to 1 (using the 128
1208
+ // multiple), as it will come out in the division of num / den.
1209
+ let ( max_norm, min_norm, amt_norm) = ( max - 0.5 , min - 0.5 , amount - 0.5 ) ;
1210
+ let ( max_pow, min_pow, amt_pow) = three_f64_pow_9 ( max_norm, min_norm, amt_norm) ;
1211
+ 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 ) ;
1212
+ let mut denominator = max_v - min_v;
1213
+ let numerator = max_v - amt_v;
1214
+
1215
+ if min_zero_implies_no_successes && min_liquidity_msat == 0 {
1216
+ denominator = denominator * ( MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64 as f64 ) / 64.0 ;
1217
+ }
1218
+
1219
+ ( numerator, denominator)
1220
+ }
1221
+
1157
1222
/// Given liquidity bounds, calculates the success probability (in the form of a numerator and
1158
1223
/// denominator) of an HTLC. This is a key assumption in our scoring models.
1159
1224
///
@@ -1174,54 +1239,25 @@ fn success_probability(
1174
1239
debug_assert ! ( total_inflight_amount_msat < max_liquidity_msat) ;
1175
1240
debug_assert ! ( max_liquidity_msat <= capacity_msat) ;
1176
1241
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
- } ;
1242
+ if params. linear_success_probability {
1243
+ linear_success_probability ( total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, min_zero_implies_no_successes)
1244
+ } else {
1245
+ // We calculate the nonlinear probabilities using floats anyway, so just stub out to
1246
+ // the float version and then convert to integers.
1247
+ let ( num, den) = nonlinear_success_probability (
1248
+ total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, capacity_msat,
1249
+ min_zero_implies_no_successes,
1250
+ ) ;
1213
1251
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
1252
+ // Because our numerator and denominator max out at 0.0078125 we need to multiply them
1253
+ // by quite a large factor to get something useful (ideally in the 2^30 range).
1254
+ const BILLIONISH : f64 = 1024.0 * 1024.0 * 1024.0 * 64.0 ;
1255
+ let numerator = ( num * BILLIONISH ) as u64 + 1 ;
1256
+ let denominator = ( den * BILLIONISH ) as u64 + 1 ;
1257
+ debug_assert ! ( numerator <= 1 << 30 , "Got large numerator ({}) from float {}." , numerator , num ) ;
1258
+ debug_assert ! ( denominator <= 1 << 30 , "Got large denominator ({}) from float {}." , denominator , den ) ;
1259
+ ( numerator , denominator)
1222
1260
}
1223
-
1224
- ( numerator, denominator)
1225
1261
}
1226
1262
1227
1263
impl < L : Deref < Target = u64 > , HT : Deref < Target = HistoricalLiquidityTracker > , T : Deref < Target = Duration > >
0 commit comments