Skip to content

Commit 39f0b3e

Browse files
committed
Use log approximation in ProbabilisticScorer
Since f64::log10 exists in std but not core, unconditionally use log approximation so --feature=no-std will compile.
1 parent 0528924 commit 39f0b3e

File tree

1 file changed

+70
-48
lines changed

1 file changed

+70
-48
lines changed

lightning/src/routing/scoring.rs

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,28 @@ impl<L: DerefMut<Target = u64>, T: Time, U: DerefMut<Target = T>> DirectedChanne
733733
}
734734
}
735735

736+
const FRACTIONAL_BITS: u32 = 4;
737+
const FRACTIONAL_BITMASK: u64 = (1 << FRACTIONAL_BITS) - 1;
738+
const LOG2_FRACTIONAL_PART: [f64; 1 << FRACTIONAL_BITS] = [
739+
0.000, 0.087, 0.170, 0.248, 0.322, 0.392, 0.459, 0.524,
740+
0.585, 0.644, 0.700, 0.755, 0.807, 0.858, 0.907, 0.954,
741+
];
742+
const LOG2_10: f64 = 3.322;
743+
744+
fn log10_approx(numerator: u64, denominator: u64) -> f64 {
745+
(log2_approx(numerator) - log2_approx(denominator)) / LOG2_10
746+
}
747+
748+
#[inline]
749+
fn log2_approx(x: u64) -> f64 {
750+
let leading_zeros = x.leading_zeros();
751+
let integer_part = (63 - leading_zeros) as f64;
752+
let fractional_part = LOG2_FRACTIONAL_PART[
753+
(((x << leading_zeros) >> (63 - FRACTIONAL_BITS)) & FRACTIONAL_BITMASK) as usize
754+
];
755+
integer_part + fractional_part
756+
}
757+
736758
impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsingTime<G, T> {
737759
fn channel_penalty_msat(
738760
&self, short_channel_id: u64, amount_msat: u64, capacity_msat: u64, source: &NodeId,
@@ -749,8 +771,8 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
749771
Probability::Zero => u64::max_value(),
750772
Probability::One => 0,
751773
Probability::Ratio { numerator, denominator } => {
752-
let success_probability = numerator as f64 / denominator as f64;
753-
(-(success_probability.log10()) * liquidity_penalty_multiplier_msat as f64) as u64
774+
let log_success_probability = log10_approx(numerator, denominator);
775+
(-log_success_probability * liquidity_penalty_multiplier_msat as f64) as u64
754776
}
755777
};
756778
// Upper bound the penalty to ensure some channel is selected.
@@ -1625,18 +1647,18 @@ mod tests {
16251647
let source = source_node_id();
16261648
let target = target_node_id();
16271649

1628-
assert_eq!(scorer.channel_penalty_msat(42, 100, 100_000, &source, &target), 0);
1629-
assert_eq!(scorer.channel_penalty_msat(42, 1_000, 100_000, &source, &target), 4);
1630-
assert_eq!(scorer.channel_penalty_msat(42, 10_000, 100_000, &source, &target), 45);
1631-
assert_eq!(scorer.channel_penalty_msat(42, 100_000, 100_000, &source, &target), 2_000);
1650+
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024_000, &source, &target), 0);
1651+
assert_eq!(scorer.channel_penalty_msat(42, 10_240, 1_024_000, &source, &target), 14);
1652+
assert_eq!(scorer.channel_penalty_msat(42, 102_400, 1_024_000, &source, &target), 44);
1653+
assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, 1_024_000, &source, &target), 2_000);
16321654

1633-
assert_eq!(scorer.channel_penalty_msat(42, 125, 1_000, &source, &target), 57);
1634-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
1635-
assert_eq!(scorer.channel_penalty_msat(42, 375, 1_000, &source, &target), 203);
1636-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1637-
assert_eq!(scorer.channel_penalty_msat(42, 625, 1_000, &source, &target), 425);
1638-
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
1639-
assert_eq!(scorer.channel_penalty_msat(42, 875, 1_000, &source, &target), 900);
1655+
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58);
1656+
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 124);
1657+
assert_eq!(scorer.channel_penalty_msat(42, 374, 1_024, &source, &target), 204);
1658+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
1659+
assert_eq!(scorer.channel_penalty_msat(42, 640, 1_024, &source, &target), 425);
1660+
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 602);
1661+
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 903);
16401662
}
16411663

16421664
#[test]
@@ -1672,13 +1694,13 @@ mod tests {
16721694
let failed_path = payment_path_for_amount(500);
16731695
let successful_path = payment_path_for_amount(200);
16741696

1675-
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 300);
1697+
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
16761698

16771699
scorer.payment_path_failed(&failed_path.iter().collect::<Vec<_>>(), 41);
1678-
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 300);
1700+
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
16791701

16801702
scorer.payment_path_successful(&successful_path.iter().collect::<Vec<_>>());
1681-
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 300);
1703+
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
16821704
}
16831705

16841706
#[test]
@@ -1692,15 +1714,15 @@ mod tests {
16921714
let target = target_node_id();
16931715
let path = payment_path_for_amount(500);
16941716

1695-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
1696-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1697-
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
1717+
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 129);
1718+
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
1719+
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602);
16981720

16991721
scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 43);
17001722

17011723
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 0);
17021724
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 0);
1703-
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 300);
1725+
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 301);
17041726
}
17051727

17061728
#[test]
@@ -1714,13 +1736,13 @@ mod tests {
17141736
let target = target_node_id();
17151737
let path = payment_path_for_amount(500);
17161738

1717-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
1718-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1719-
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 600);
1739+
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 129);
1740+
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
1741+
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602);
17201742

17211743
scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 42);
17221744

1723-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300);
1745+
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 301);
17241746
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 2_000);
17251747
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 2_000);
17261748
}
@@ -1738,15 +1760,15 @@ mod tests {
17381760
let recipient = recipient_node_id();
17391761
let path = payment_path_for_amount(500);
17401762

1741-
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 124);
1742-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 124);
1743-
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 124);
1763+
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 129);
1764+
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 129);
1765+
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 129);
17441766

17451767
scorer.payment_path_successful(&path.iter().collect::<Vec<_>>());
17461768

1747-
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 124);
1748-
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300);
1749-
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 300);
1769+
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 129);
1770+
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 301);
1771+
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 301);
17501772
}
17511773

17521774
#[test]
@@ -1767,20 +1789,20 @@ mod tests {
17671789
scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::<Vec<_>>(), 43);
17681790

17691791
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
1770-
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 92);
1771-
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_424);
1792+
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 96);
1793+
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_408);
17721794
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
17731795

17741796
SinceEpoch::advance(Duration::from_secs(9));
17751797
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
1776-
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 92);
1777-
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_424);
1798+
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 96);
1799+
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_408);
17781800
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
17791801

17801802
SinceEpoch::advance(Duration::from_secs(1));
17811803
assert_eq!(scorer.channel_penalty_msat(42, 64, 1_024, &source, &target), 0);
17821804
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 34);
1783-
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_812);
1805+
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_806);
17841806
assert_eq!(scorer.channel_penalty_msat(42, 960, 1_024, &source, &target), 2_000);
17851807

17861808
// Fully decay liquidity lower bound.
@@ -1813,7 +1835,7 @@ mod tests {
18131835
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 124);
18141836

18151837
scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::<Vec<_>>(), 42);
1816-
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 281);
1838+
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 274);
18171839

18181840
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
18191841
// would cause an overflow.
@@ -1835,30 +1857,30 @@ mod tests {
18351857
let source = source_node_id();
18361858
let target = target_node_id();
18371859

1838-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300);
1860+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
18391861

18401862
// More knowledge gives higher confidence (256, 768), meaning a lower penalty.
18411863
scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
18421864
scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
1843-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 281);
1865+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 274);
18441866

18451867
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
18461868
SinceEpoch::advance(Duration::from_secs(10));
1847-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 293);
1869+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
18481870

18491871
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
18501872
// is closer to the upper bound, meaning a higher penalty.
18511873
scorer.payment_path_successful(&payment_path_for_amount(64).iter().collect::<Vec<_>>());
1852-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 333);
1874+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 342);
18531875

18541876
// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
18551877
// is closer to the lower bound, meaning a lower penalty.
18561878
scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
1857-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 247);
1879+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 255);
18581880

18591881
// Further decaying affects the lower bound more than the upper bound (128, 928).
18601882
SinceEpoch::advance(Duration::from_secs(10));
1861-
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 280);
1883+
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 284);
18621884
}
18631885

18641886
#[test]
@@ -1876,18 +1898,18 @@ mod tests {
18761898
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 2_000);
18771899

18781900
SinceEpoch::advance(Duration::from_secs(10));
1879-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 475);
1901+
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
18801902

18811903
scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
1882-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1904+
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
18831905

18841906
let mut serialized_scorer = Vec::new();
18851907
scorer.write(&mut serialized_scorer).unwrap();
18861908

18871909
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
18881910
let deserialized_scorer =
18891911
<ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph)).unwrap();
1890-
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1912+
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
18911913
}
18921914

18931915
#[test]
@@ -1912,12 +1934,12 @@ mod tests {
19121934
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
19131935
let deserialized_scorer =
19141936
<ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph)).unwrap();
1915-
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 475);
1937+
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
19161938

19171939
scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
1918-
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
1940+
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
19191941

19201942
SinceEpoch::advance(Duration::from_secs(10));
1921-
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 367);
1943+
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 370);
19221944
}
19231945
}

0 commit comments

Comments
 (0)