@@ -1154,6 +1154,67 @@ 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
+ #[ 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
+
1157
1218
/// Given liquidity bounds, calculates the success probability (in the form of a numerator and
1158
1219
/// denominator) of an HTLC. This is a key assumption in our scoring models.
1159
1220
///
@@ -1174,54 +1235,25 @@ fn success_probability(
1174
1235
debug_assert ! ( total_inflight_amount_msat < max_liquidity_msat) ;
1175
1236
debug_assert ! ( max_liquidity_msat <= capacity_msat) ;
1176
1237
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
+ ) ;
1213
1247
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)
1222
1256
}
1223
-
1224
- ( numerator, denominator)
1225
1257
}
1226
1258
1227
1259
impl < L : Deref < Target = u64 > , HT : Deref < Target = HistoricalLiquidityTracker > , T : Deref < Target = Duration > >
0 commit comments