Skip to content

Commit

Permalink
Unique runner validation
Browse files Browse the repository at this point in the history
  • Loading branch information
ekoutanov committed Oct 30, 2023
1 parent 6367a61 commit d61b286
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 36 deletions.
28 changes: 0 additions & 28 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,34 +210,6 @@ pub struct Model {
impl Model {
pub fn derive_multi(&self, selections: &[Selection]) -> Result<Timed<DerivedPrice>, anyhow::Error> {
validate_plausible_selections(selections)?;
// let runners = self.fit_outcome.fitted_probs.cols();
// let check_runner_active = |runner : &Runner| {
// let runner_index = runner.as_index();
// if runner_index > runners - 1 {
// bail!("invalid runner {runner}");
// }
// if self.fit_outcome.fitted_probs[(0, runner_index)] == 0. {
// bail!("runner has a zero finishing probability");
// }
// Ok(())
// };
// for selection in selections {
// match selection {
// Selection::Span { runner, ranks} => {
// check_runner_active(runner)?;
// if ranks.end().as_index() > PODIUM - 1 {
// bail!("invalid finishing rank {}", ranks.end());
// }
// }
// Selection::Exact { runner, rank } => {
// check_runner_active(runner)?;
// if rank.as_index() > PODIUM - 1 {
// bail!("invalid finishing rank {rank}");
// }
// }
// }
// }

let start_time = Instant::now();
let mut overround = 1.;
let win_probs = &self.fit_outcome.fitted_probs[0];
Expand Down
55 changes: 47 additions & 8 deletions src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ pub fn validate_plausible_selections(selections: &[Selection]) -> Result<(), any
if selections.is_empty() {
bail!("empty selections");
}

let podium = selections
.iter()
.map(|selection| match selection {
Expand All @@ -236,18 +237,35 @@ pub fn validate_plausible_selections(selections: &[Selection]) -> Result<(), any
.max()
.unwrap();

//TODO validate unique runners
let runners = selections
.iter()
.map(|selection| match selection {
Selection::Span { runner, .. } => runner.as_number(),
Selection::Exact { runner, .. } => runner.as_number(),
})
.max()
.unwrap();

let mut runner_index_seen = vec![false; runners];
let mut placings = Matrix::allocate(selections.len(), podium);
for (selection_index, selection) in selections.iter().enumerate() {
match selection {
Selection::Span { ranks, .. } => {
let runner = match selection {
Selection::Span { runner, ranks } => {
placings[(selection_index, ranks.start().as_index())] = true;
runner
}
Selection::Exact { rank, .. } => {
Selection::Exact { runner, rank } => {
placings[(selection_index, rank.as_index())] = true;
runner
}
};
if runner_index_seen[runner.as_index()] {
bail!(
"duplicate runner {runner} in {}",
DisplaySlice::from(selections)
);
}
runner_index_seen[runner.as_index()] = true;
}

for rank in 0..podium {
Expand Down Expand Up @@ -286,6 +304,8 @@ pub fn validate_plausible_selections(selections: &[Selection]) -> Result<(), any
}
}

// println!("selections: {}:\n{}", DisplaySlice::from(selections), placings.verbose());

Ok(())
}

Expand Down Expand Up @@ -626,15 +646,15 @@ mod tests {
Runner::number(1).top(Rank::number(1)),
Runner::number(2).top(Rank::number(3)),
Runner::number(3).top(Rank::number(3)),
Runner::number(3).top(Rank::number(4)),
Runner::number(4).top(Rank::number(4)),
];
assert!(validate_plausible_selections(&selections).is_ok());
}
{
let selections = vec![
Runner::number(1).top(Rank::number(1)),
Runner::number(3).top(Rank::number(3)),
Runner::number(3).top(Rank::number(4)),
Runner::number(4).top(Rank::number(4)),
];
assert!(validate_plausible_selections(&selections).is_ok());
}
Expand Down Expand Up @@ -686,15 +706,27 @@ mod tests {
Runner::number(1).top(Rank::number(1)),
Runner::number(2).top(Rank::number(1)),
];
assert_eq!("cannot accommodate rank @1 in [r1 in @1-@1, r2 in @1-@1]", validate_plausible_selections(&selections).err().unwrap().to_string());
assert_eq!(
"cannot accommodate rank @1 in [r1 in @1-@1, r2 in @1-@1]",
validate_plausible_selections(&selections)
.err()
.unwrap()
.to_string()
);
}
{
let selections = vec![
Runner::number(1).top(Rank::number(1)),
Runner::number(2).top(Rank::number(1)),
Runner::number(3).top(Rank::number(3)),
];
assert_eq!("cannot accommodate rank @1 in [r1 in @1-@1, r2 in @1-@1, r3 in @1-@3]", validate_plausible_selections(&selections).err().unwrap().to_string());
assert_eq!(
"cannot accommodate rank @1 in [r1 in @1-@1, r2 in @1-@1, r3 in @1-@3]",
validate_plausible_selections(&selections)
.err()
.unwrap()
.to_string()
);
}
{
let selections = vec![
Expand All @@ -705,5 +737,12 @@ mod tests {
];
assert_eq!("cannot accommodate rank @3 in [r1 in @1-@3, r2 in @1-@3, r3 in @1-@3, r4 in @1-@3]", validate_plausible_selections(&selections).err().unwrap().to_string());
}
{
let selections = vec![
Runner::number(1).top(Rank::number(3)),
Runner::number(1).top(Rank::number(4)),
];
assert_eq!("duplicate runner r1 in [r1 in @1-@3, r1 in @1-@4]", validate_plausible_selections(&selections).err().unwrap().to_string());
}
}
}

0 comments on commit d61b286

Please sign in to comment.