use rudb_kernels::Connective;
const WINDOW: usize = 16;
#[derive(Debug)]
pub(crate) struct Ordering {
op: Connective,
order: Vec<usize>,
costs: Vec<f64>,
seen: Vec<Window>,
ranks: Vec<f64>,
}
impl Ordering {
pub(crate) fn new(op: Connective, costs: Vec<f64>) -> Self {
let len = costs.len();
Self {
op,
order: (0..len).collect(),
costs,
seen: (0..len).map(|_| Window::default()).collect(),
ranks: vec![0.0; len],
}
}
pub(crate) fn at(&self, slot: usize) -> usize {
self.order.get(slot).copied().unwrap_or(slot)
}
#[cfg(test)]
pub(crate) fn order(&self) -> &[usize] {
&self.order
}
pub(crate) fn observed(&mut self, operand: usize, given: usize, kept: usize) {
if given == 0 {
return;
}
if let Some(window) = self.seen.get_mut(operand) {
window.record(given, kept);
}
}
pub(crate) fn relearn(&mut self) {
for operand in 0..self.ranks.len() {
self.ranks[operand] = self.rank(operand);
}
let ranks = &self.ranks;
self.order.sort_by(|&left, &right| ranks[right].total_cmp(&ranks[left]));
}
fn rank(&self, operand: usize) -> f64 {
let Some(passed) = self.seen[operand].passed() else {
return f64::INFINITY;
};
if self.costs[operand] <= 0.0 {
return f64::INFINITY;
}
let worth = match self.op {
Connective::And => 1.0 - passed,
Connective::Or => passed,
};
worth / self.costs[operand]
}
}
#[derive(Debug, Default)]
struct Window {
ring: [(u32, u32); WINDOW],
at: usize,
given: u64,
kept: u64,
}
impl Window {
fn record(&mut self, given: usize, kept: usize) {
let given = u32::try_from(given).unwrap_or(u32::MAX);
let kept = u32::try_from(kept).unwrap_or(u32::MAX);
let (stale_given, stale_kept) = self.ring[self.at];
self.given = self.given + u64::from(given) - u64::from(stale_given);
self.kept = self.kept + u64::from(kept) - u64::from(stale_kept);
self.ring[self.at] = (given, kept);
self.at = (self.at + 1) % WINDOW;
}
fn passed(&self) -> Option<f64> {
(self.given > 0).then(|| self.kept as f64 / self.given as f64)
}
}
#[cfg(test)]
mod tests {
use super::{Ordering, WINDOW, Window};
use rudb_kernels::Connective;
fn ordering(op: Connective, costs: &[f64]) -> Ordering {
Ordering::new(op, costs.to_vec())
}
fn chunk(ordering: &mut Ordering, kept: &[usize]) {
for (operand, &rows) in kept.iter().enumerate() {
ordering.observed(operand, 1000, rows);
}
ordering.relearn();
}
#[test]
fn a_connective_that_has_seen_nothing_runs_its_operands_in_the_order_it_was_given() {
let mut ordering = ordering(Connective::And, &[1.0, 1.0, 1.0]);
assert_eq!(ordering.order(), &[0, 1, 2]);
ordering.relearn();
assert_eq!(ordering.order(), &[0, 1, 2], "nothing observed is nothing to go on");
}
#[test]
fn the_conjunct_that_rejects_the_most_for_the_least_goes_first() {
let mut ordering = ordering(Connective::And, &[1.0, 1.0, 1.0]);
chunk(&mut ordering, &[900, 100, 800]);
assert_eq!(ordering.order(), &[1, 2, 0]);
}
#[test]
fn a_cheap_conjunct_beats_an_expensive_one_that_rejects_a_little_more() {
let mut ordering = ordering(Connective::And, &[20.0, 1.0]);
chunk(&mut ordering, &[100, 200]);
assert_eq!(ordering.order(), &[1, 0]);
chunk(&mut ordering, &[100, 1000]);
for _ in 0..WINDOW {
chunk(&mut ordering, &[100, 1000]);
}
assert_eq!(ordering.order(), &[0, 1]);
}
#[test]
fn an_or_runs_the_branch_that_accepts_the_most_first() {
let mut ordering = ordering(Connective::Or, &[1.0, 1.0]);
chunk(&mut ordering, &[100, 900]);
assert_eq!(ordering.order(), &[1, 0]);
}
#[test]
fn an_operand_that_has_not_run_goes_in_front_of_every_operand_that_has() {
let mut ordering = ordering(Connective::And, &[1.0, 1.0]);
ordering.observed(0, 1000, 10);
ordering.relearn();
assert_eq!(ordering.order(), &[1, 0]);
}
#[test]
fn a_conjunct_that_costs_nothing_goes_first_whatever_it_rejects() {
let mut ordering = ordering(Connective::And, &[0.0, 1.0]);
chunk(&mut ordering, &[990, 10]);
assert_eq!(ordering.order(), &[0, 1]);
}
#[test]
fn what_the_window_holds_is_the_recent_past_and_not_the_whole_scan() {
let mut ordering = ordering(Connective::And, &[1.0, 1.0]);
for _ in 0..WINDOW {
chunk(&mut ordering, &[0, 1000]);
}
assert_eq!(ordering.order(), &[0, 1]);
for _ in 0..WINDOW {
chunk(&mut ordering, &[1000, 0]);
}
assert_eq!(ordering.order(), &[1, 0], "the first half is out of the window by now");
}
#[test]
fn a_chunk_that_reached_an_operand_with_no_rows_is_not_an_observation() {
let mut ordering = ordering(Connective::And, &[1.0, 1.0]);
ordering.observed(0, 1000, 0);
ordering.observed(1, 0, 0);
ordering.relearn();
assert_eq!(ordering.order(), &[1, 0], "an operand given nothing has still not run");
}
#[test]
fn the_totals_a_window_carries_are_what_a_walk_over_it_would_say() {
let mut window = Window::default();
for at in 0..WINDOW * 2 {
window.record(100, at % 10);
}
let walked: u64 = window.ring.iter().map(|&(_, kept)| u64::from(kept)).sum();
assert_eq!(window.kept, walked);
assert_eq!(window.given, 100 * WINDOW as u64);
}
}