use cudarc::driver::DeviceRepr;
use super::error::GpuError;
use crate::truncation::BuiltinTruncation;
pub(super) const KEEP_NODES: usize = 15;
pub(super) const OP_KEEP: u32 = 0;
pub(super) const OP_COEFF: u32 = 1;
pub(super) const OP_WEIGHT: u32 = 2;
pub(super) const OP_AND: u32 = 3;
pub(super) const OP_OR: u32 = 4;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct KeepProgram {
len: u32,
op: [u32; KEEP_NODES],
arg: [u64; KEEP_NODES],
}
unsafe impl DeviceRepr for KeepProgram {}
fn per_term(tree: &BuiltinTruncation) -> BuiltinTruncation {
use BuiltinTruncation as T;
match tree {
T::Keep | T::TopN(_) | T::ApproxTopN(_) | T::CollapseSample(_) => T::Keep,
T::Coefficient(eps) => T::Coefficient(*eps),
T::Weight(k) => T::Weight(*k),
T::And(a, b) => match (per_term(a), per_term(b)) {
(T::Keep, x) | (x, T::Keep) => x,
(x, y) => T::And(Box::new(x), Box::new(y)),
},
T::Or(a, b) => match (per_term(a), per_term(b)) {
(T::Keep, _) | (_, T::Keep) => T::Keep,
(x, y) => T::Or(Box::new(x), Box::new(y)),
},
}
}
fn emit(tree: &BuiltinTruncation, out: &mut Vec<(u32, u64)>) {
use BuiltinTruncation as T;
match tree {
T::Coefficient(eps) => out.push((OP_COEFF, eps.to_bits())),
T::Weight(k) => out.push((OP_WEIGHT, u64::from(*k))),
T::And(a, b) | T::Or(a, b) => {
emit(a, out);
emit(b, out);
out.push((if matches!(tree, T::And(..)) { OP_AND } else { OP_OR }, 0));
}
T::Keep | T::TopN(_) | T::ApproxTopN(_) | T::CollapseSample(_) => out.push((OP_KEEP, 0)),
}
}
impl KeepProgram {
pub(crate) const KEEP: Self = Self { len: 1, op: [OP_KEEP; KEEP_NODES], arg: [0; KEEP_NODES] };
pub(crate) fn lower(tree: &BuiltinTruncation) -> Result<Self, GpuError> {
let mut nodes = Vec::new();
emit(&per_term(tree), &mut nodes);
if nodes.len() > KEEP_NODES {
return Err(GpuError::Unsupported(
"a per-term truncation program longer than 15 nodes",
));
}
let mut program =
Self { len: nodes.len() as u32, op: [OP_KEEP; KEEP_NODES], arg: [0; KEEP_NODES] };
for (i, (op, arg)) in nodes.into_iter().enumerate() {
program.op[i] = op;
program.arg[i] = arg;
}
Ok(program)
}
}
pub(crate) fn layer_pass_leaves(
tree: &BuiltinTruncation,
visit: &mut dyn FnMut(&BuiltinTruncation),
) {
use BuiltinTruncation as T;
match tree {
T::TopN(_) | T::ApproxTopN(_) | T::CollapseSample(_) => visit(tree),
T::And(a, b) => {
layer_pass_leaves(a, visit);
layer_pass_leaves(b, visit);
}
T::Keep | T::Coefficient(_) | T::Weight(_) | T::Or(_, _) => {}
}
}
#[cfg(test)]
mod tests;