research-weights.rs
5.2 kB · rust · 167 lines
1use mrlycore::errors::Result;2use mrlycore::Color;3use mrlyfig::{ink, plot, save, Board, Frame};45const LEVEL: u32 = 8;6const SAMPLES: usize = 720;7const CELLS: [(i64, i64); 3] = [(0, 0), (2, 0), (0, 2)];89fn root(costs: [f64; 3]) -> f64 {10 let (mut lo, mut hi) = (0.0f64, 8.0f64);11 for _ in 0..200 {12 let mid = 0.5 * (lo + hi);13 let mass: f64 = costs.iter().map(|c| (-mid * c).exp()).sum();14 if mass > 1.0 {15 lo = mid;16 } else {17 hi = mid;18 }19 }20 0.5 * (lo + hi)21}2223fn pascal(n: usize) -> Vec<Vec<u128>> {24 let mut rows: Vec<Vec<u128>> = vec![vec![1]];25 for i in 1..=n {26 let mut row = vec![1u128; i + 1];27 for k in 1..i {28 row[k] = rows[i - 1][k - 1] + rows[i - 1][k];29 }30 rows.push(row);31 }32 rows33}3435fn stopping(budget: f64, cheap: f64, dear: f64, binom: &[Vec<u128>]) -> u128 {36 let mut total = 0u128;37 let mut p = 0usize;38 while p as f64 * cheap <= budget {39 let mut q = 0usize;40 while p as f64 * cheap + q as f64 * dear <= budget {41 total += binom[p + q][q] << p;42 q += 1;43 }44 p += 1;45 }46 total47}4849fn mass_side(dear: f64, span: (f64, f64)) -> Vec<f64> {50 let cheap = 2.0f64.ln();51 let delta = root([cheap, cheap, dear]);52 let binom = pascal(2 + (span.1 / cheap) as usize);53 (0..SAMPLES)54 .map(|i| {55 let a = span.0 + (span.1 - span.0) * i as f64 / (SAMPLES - 1) as f64;56 (stopping(a, cheap, dear, &binom) as f64).ln() - delta * a57 })58 .collect()59}6061fn gasket(nums: [u64; 3]) -> Vec<(i64, i64, u128)> {62 let mut pts = vec![(0i64, 0i64, 1u128)];63 for _ in 0..LEVEL {64 let mut next = Vec::with_capacity(pts.len() * 3);65 for &(x, y, m) in &pts {66 for (k, &(dx, dy)) in CELLS.iter().enumerate() {67 next.push((x * 3 + dx, y * 3 + dy, m * nums[k] as u128));68 }69 }70 pts = next;71 }72 pts73}7475fn length_side(nums: [u64; 3], den: u64) -> (f64, Vec<f64>) {76 let pts = gasket(nums);77 assert_eq!(pts.len(), 3usize.pow(LEVEL));78 let whole: u128 = pts.iter().map(|p| p.2).sum();79 assert_eq!(whole, (den as u128).pow(LEVEL));80 let mut rows: Vec<(u128, u128)> = pts81 .iter()82 .map(|&(x, y, m)| ((x * x + y * y) as u128, m))83 .collect();84 rows.sort_by_key(|row| row.0);85 let mut running = 0u128;86 let shells: Vec<(f64, f64)> = rows87 .iter()88 .map(|&(square, m)| {89 running += m;90 (square as f64, running as f64)91 })92 .collect();93 let base = 3.0f64.ln();94 let alpha = -(nums[0] as f64 / den as f64).ln() / base;95 let ys = (0..SAMPLES)96 .map(|i| {97 let u = 4.0 + 4.0 * i as f64 / (SAMPLES - 1) as f64;98 let radius = (u * base).exp();99 let reach = radius * radius;100 let cut = shells.partition_point(|shell| shell.0 <= reach);101 let held = if cut == 0 { 0.0 } else { shells[cut - 1].1 };102 held.ln() - LEVEL as f64 * (den as f64).ln() - alpha * u * base103 })104 .collect();105 (alpha, ys)106}107108fn center(ys: &[f64]) -> Vec<f64> {109 let mean = ys.iter().sum::<f64>() / ys.len() as f64;110 ys.iter().map(|y| y - mean).collect()111}112113fn reach(first: &[f64], second: &[f64]) -> f64 {114 first115 .iter()116 .chain(second.iter())117 .fold(0.0f64, |a, y| a.max(y.abs()))118 * 1.14119}120121fn trace(board: &mut Board, area: Frame, ys: &[f64], span: f64, thick: f64, color: Color) {122 let last = (ys.len() - 1) as f64;123 let pts: Vec<(f64, f64)> = ys124 .iter()125 .enumerate()126 .map(|(i, y)| {127 (128 area.x + area.w * i as f64 / last,129 area.y + area.h * (0.5 - 0.5 * y / span),130 )131 })132 .collect();133 board.polyline(&pts, thick, color);134}135136fn panel(board: &mut Board, area: Frame, first: &[f64], second: &[f64]) {137 plot::axis(board, area, ink::line());138 let inner = area.inset(18.0);139 let mid = inner.y + inner.h / 2.0;140 board.segment((inner.x, mid), (inner.x + inner.w, mid), 1.0, ink::line());141 let span = reach(first, second);142 trace(board, inner, second, span, 3.4, ink::orange());143 trace(board, inner, first, span, 2.1, ink::blue());144}145146fn main() -> Result<()> {147 let window = (48.0, 48.0 + 6.0 * 2.0f64.ln());148 let lattice = mass_side(4.0f64.ln(), window);149 let smooth = mass_side(3.0f64.ln(), window);150 assert!((root([2.0f64.ln(), 2.0f64.ln(), 4.0f64.ln()]) - 1.2715533032).abs() < 1e-9);151 assert!((root([2.0f64.ln(), 2.0f64.ln(), 3.0f64.ln()]) - 1.3646005647).abs() < 1e-9);152153 let (thin, ring_thin) = length_side([2, 2, 1], 5);154 let (fat, ring_fat) = length_side([3, 3, 2], 8);155 assert!((thin - 0.834043767).abs() < 1e-9);156 assert!((fat - 0.892789261).abs() < 1e-9);157158 let mut board = Board::square();159 let frame = board.frame(0.08);160 let half = frame.h / 2.0;161 let top = Frame::new(frame.x, frame.y, frame.w, half).inset(14.0);162 let foot = Frame::new(frame.x, frame.y + half, frame.w, half).inset(14.0);163 panel(&mut board, top, ¢er(&lattice), ¢er(&smooth));164 panel(&mut board, foot, ¢er(&ring_thin), ¢er(&ring_fat));165 save("research-weights", &board)?;166 Ok(())167}