From bdf661b5840186b3efae9bd6af50e35569a03850 Mon Sep 17 00:00:00 2001 From: Emil Koutanov Date: Sun, 24 Dec 2023 16:43:28 +1100 Subject: [PATCH] More macro work --- brumby-soccer/src/fit.rs | 25 +++----- brumby-soccer/src/interval.rs | 7 +-- brumby-soccer/src/model.rs | 1 + brumby/src/stack_vec.rs | 115 ++++++++++++++++++++++++---------- 4 files changed, 93 insertions(+), 55 deletions(-) diff --git a/brumby-soccer/src/fit.rs b/brumby-soccer/src/fit.rs index e53a626..09e11db 100644 --- a/brumby-soccer/src/fit.rs +++ b/brumby-soccer/src/fit.rs @@ -12,7 +12,6 @@ use brumby::opt::{ use brumby::probs::SliceExt; use crate::domain::{Offer, OfferType, OutcomeType, Player, Side}; -use crate::domain::OfferType::{AnytimeAssist, FirstGoalscorer}; use crate::domain::Player::Named; use crate::interval::{ BivariateProbs, Config, explore, PlayerProbs, PruneThresholds, TeamProbs, @@ -296,7 +295,7 @@ pub fn fit_first_goalscorer_all<'a>( Player::Other => unreachable!(), }; let init_estimate = first_goalscorer.market.probs[index] / side_ratio; - // let start = Instant::now(); + // let per_start = Instant::now(); let player_search_outcome = fit_first_goalscorer_one( h1_probs, h2_probs, @@ -306,7 +305,7 @@ pub fn fit_first_goalscorer_all<'a>( intervals, max_total_goals ); - // println!("first goal for player {player:?}, {player_search_outcome:?}, sample prob. {}, init_estimate: {init_estimate}, took {:?}", first_goalscorer.market.probs[index], start.elapsed()); + // println!("first goal for player {player:?}, {player_search_outcome:?}, sample prob. {}, init_estimate: {init_estimate}, took {:?}", first_goalscorer.market.probs[index], per_start.elapsed()); (player.clone(), player_search_outcome.optimal_value) } _ => unreachable!(), @@ -332,10 +331,7 @@ fn fit_first_goalscorer_one( team_probs: TeamProbs { h1_goals: h1_goals.clone(), h2_goals: h2_goals.clone(), - assists: UnivariateProbs { - home: 1.0, - away: 1.0, - }, + assists: UnivariateProbs::default(), }, player_probs: vec![( player.clone(), @@ -348,14 +344,14 @@ fn fit_first_goalscorer_one( max_total_goals, min_prob: GOALSCORER_MIN_PROB, }, - expansions: requirements(&FirstGoalscorer), + expansions: requirements(&OfferType::FirstGoalscorer), }; let outcome_type = OutcomeType::Player(player.clone()); univariate_descent( &UnivariateDescentConfig { init_value: init_estimate, init_step: init_estimate * 0.1, - min_step: init_estimate * 0.001, + min_step: init_estimate * 0.0001, max_steps: 100, acceptable_residual: 1e-9, }, @@ -438,10 +434,7 @@ fn fit_anytime_goalscorer_one( team_probs: TeamProbs { h1_goals: h1_goals.clone(), h2_goals: h2_goals.clone(), - assists: UnivariateProbs { - home: 1.0, - away: 1.0, - }, + assists: UnivariateProbs::default(), }, player_probs: vec![( player.clone(), @@ -461,7 +454,7 @@ fn fit_anytime_goalscorer_one( &UnivariateDescentConfig { init_value: init_estimate, init_step: init_estimate * 0.1, - min_step: init_estimate * 0.001, + min_step: init_estimate * 0.0001, max_steps: 100, acceptable_residual: 1e-9, }, @@ -559,14 +552,14 @@ fn fit_anytime_assist_one( max_total_goals, min_prob: GOALSCORER_MIN_PROB, }, - expansions: requirements(&AnytimeAssist), + expansions: requirements(&OfferType::AnytimeAssist), }; let outcome_type = OutcomeType::Player(player.clone()); univariate_descent( &UnivariateDescentConfig { init_value: init_estimate, init_step: init_estimate * 0.1, - min_step: init_estimate * 0.001, + min_step: init_estimate * 0.0001, max_steps: 100, acceptable_residual: 1e-9, }, diff --git a/brumby-soccer/src/interval.rs b/brumby-soccer/src/interval.rs index a78f22e..f719f63 100644 --- a/brumby-soccer/src/interval.rs +++ b/brumby-soccer/src/interval.rs @@ -5,6 +5,7 @@ use rustc_hash::FxHashMap; use brumby::hash_lookup::HashLookup; use brumby::stack_vec::StackVec; +use brumby::sv; use crate::domain::{Player, Score, Side}; @@ -22,11 +23,7 @@ pub struct Prospect { } impl Prospect { fn init(players: usize) -> Prospect { - // let stats = vec![PlayerStats::default(); players]; - let mut stats = StackVec::default(); - for _ in 0..players { - stats.push(PlayerStats::default()); - } + let stats = sv![PlayerStats::default(); players]; Prospect { ht_score: Score::nil_all(), ft_score: Score::nil_all(), diff --git a/brumby-soccer/src/model.rs b/brumby-soccer/src/model.rs index 8e8917b..17e2f29 100644 --- a/brumby-soccer/src/model.rs +++ b/brumby-soccer/src/model.rs @@ -268,6 +268,7 @@ impl Model { ); probs.push(prob); } + debug!("... normalizing: {} -> {}", probs.sum(), stub.normal); probs.normalise(stub.normal); let market = Market::frame(&stub.overround, probs, price_bounds); ( diff --git a/brumby/src/stack_vec.rs b/brumby/src/stack_vec.rs index d909245..40d9570 100644 --- a/brumby/src/stack_vec.rs +++ b/brumby/src/stack_vec.rs @@ -21,8 +21,41 @@ impl StackVec { #[inline] pub fn push(&mut self, value: T) { - self.array[self.len] = Some(value); - self.len += 1; + self.try_push(value).unwrap_or_else(|err| panic!("{}", err)) + } + + #[inline] + pub fn try_push(&mut self, value: T) -> Result<(), CapacityExceeded> { + if self.len < C { + self.array[self.len] = Some(value); + self.len += 1; + Ok(()) + } else { + Err(CapacityExceeded { target_capacity: C }) + } + } + + #[inline] + pub fn push_repeat(&mut self, value: T, times: usize) + where + T: Clone, + { + self.try_push_repeat(value, times) + .unwrap_or_else(|err| panic!("{}", err)) + } + + #[inline] + pub fn try_push_repeat(&mut self, value: T, times: usize) -> Result<(), CapacityExceeded> + where + T: Clone, + { + for _ in 1..times { + self.try_push(value.clone())?; + } + if times > 0 { + self.try_push(value)?; + } + Ok(()) } pub fn iter(&self) -> Iter { @@ -34,14 +67,8 @@ impl StackVec { self.array.fill_with(|| None); self.len = 0; } - - #[inline] - pub fn capacity(&self) -> usize { - C - } } - impl PartialEq for StackVec { #[inline] fn eq(&self, other: &Self) -> bool { @@ -52,7 +79,7 @@ impl PartialEq for StackVec { for index in 0..self.len { let self_item = &self.array[index]; let other_item = &other.array[index]; - if self_item.ne(other_item) { + if self_item != other_item { return false; } } @@ -61,7 +88,6 @@ impl PartialEq for StackVec { } } - impl Hash for StackVec { #[inline] fn hash(&self, state: &mut H) { @@ -87,21 +113,17 @@ impl Debug for StackVec { } #[derive(Debug, Error, PartialEq, Eq)] -#[error("source array len {source_len} exceeds target len {target_len}")] -pub struct ArrayOverflow { - source_len: usize, - target_len: usize, +#[error("exceeds capacity ({target_capacity})")] +pub struct CapacityExceeded { + target_capacity: usize, } impl TryFrom<[T; B]> for StackVec { - type Error = ArrayOverflow; + type Error = CapacityExceeded; fn try_from(source: [T; B]) -> Result { if B > C { - return Err(ArrayOverflow { - source_len: B, - target_len: C, - }); + return Err(CapacityExceeded { target_capacity: C }); } let mut array: [Option; C] = std::array::from_fn(|_| None); @@ -120,22 +142,42 @@ impl TryFrom<[T; B]> for StackVec { // sv // } +pub mod __macro_support { + use crate::stack_vec::StackVec; + + pub fn sv_repeat(value: T, times: usize) -> StackVec { + let mut sv = StackVec::default(); + sv.push_repeat(value, times); + sv + } +} + #[macro_export] macro_rules! sv { () => ( - StackVec::default() + $crate::__rust_force_expr!($crate::stack_vec::StackVec::default()) + ); + ($elem:expr; $n:expr) => ( + $crate::__rust_force_expr!($crate::stack_vec::__macro_support::sv_repeat($elem, $n)) ); - ( $( $x:expr ),* ) => { + ($($elem:expr), *) => { { - let mut sv = StackVec::default(); + let mut sv = $crate::stack_vec::StackVec::default(); $( - sv.push($x); + $crate::__rust_force_expr!(sv.push($elem)); )* sv } }; } +#[macro_export] +macro_rules! __rust_force_expr { + ($e:expr) => { + $e + }; +} + impl Default for StackVec { #[inline] fn default() -> Self { @@ -239,7 +281,6 @@ mod tests { #[test] fn init() { let sv = StackVec::<(), 4>::default(); - assert_eq!(4, sv.capacity()); assert!(sv.is_empty()); assert_eq!(0, sv.len()); assert_eq!(None, sv.iter().next()); @@ -247,7 +288,7 @@ mod tests { } #[test] - fn debug() { + fn macro_and_debug() { { let sv: StackVec<(), 0> = sv![]; assert_eq!("[]", format!("{sv:?}")); @@ -264,6 +305,18 @@ mod tests { let sv: StackVec<_, 3> = sv!["zero", "one", "two"]; assert_eq!(r#"["zero", "one", "two"]"#, format!("{sv:?}")); } + { + let sv: StackVec<_, 2> = sv!["zero"; 0]; + assert_eq!(r#"[]"#, format!("{sv:?}")); + } + { + let sv: StackVec<_, 2> = sv!["zero"; 1]; + assert_eq!(r#"["zero"]"#, format!("{sv:?}")); + } + { + let sv: StackVec<_, 2> = sv!["zero"; 2]; + assert_eq!(r#"["zero", "zero"]"#, format!("{sv:?}")); + } } #[test] @@ -279,7 +332,7 @@ mod tests { } #[test] - #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] + #[should_panic(expected = "exceeds capacity (2)")] fn push_with_overflow() { let mut sv = StackVec::<_, 2>::default(); sv.push("zero"); @@ -329,13 +382,7 @@ mod tests { #[test] fn from_array_overflow() { let result = StackVec::<_, 2>::try_from(["zero", "one", "two"]); - assert_eq!( - ArrayOverflow { - source_len: 3, - target_len: 2 - }, - result.unwrap_err() - ); + assert_eq!(CapacityExceeded { target_capacity: 2 }, result.unwrap_err()); } #[test] @@ -376,7 +423,7 @@ mod tests { } #[test] - #[should_panic(expected = "the len is 2 but the index is 2")] + #[should_panic(expected = "exceeds capacity (2)")] fn sv_overflow() { let _: StackVec<_, 2> = sv!["0", "1", "2"]; }