#![cfg(feature = "cpu")]
use rlx_ir::{DType, Graph, Shape};
use rlx_runtime::{Device, Session};
fn build_sample_graph(b: usize, v: usize, top_k: usize, top_p: f32, temp: f32, seed: u64) -> Graph {
let mut g = Graph::new("sample");
let logits = g.input("logits", Shape::new(&[b, v], DType::F32));
let y = g.sample(
logits,
top_k,
top_p,
temp,
seed,
Shape::new(&[b], DType::F32),
);
g.set_outputs(vec![y]);
g
}
fn logits_data(b: usize, v: usize) -> Vec<f32> {
(0..b * v)
.map(|i| {
let r = (i % v) as f32;
((r * 0.37).sin() * 3.0) + ((i / v) as f32) * 0.1
})
.collect()
}
#[allow(dead_code)]
fn run_on(
device: Device,
b: usize,
v: usize,
top_k: usize,
top_p: f32,
temp: f32,
seed: u64,
) -> Vec<f32> {
let logits = logits_data(b, v);
let mut exe = Session::new(device).compile(build_sample_graph(b, v, top_k, top_p, temp, seed));
exe.run(&[("logits", logits.as_slice())]).pop().unwrap()
}
#[allow(dead_code)]
fn cases() -> Vec<(&'static str, usize, usize, usize, f32, f32, u64)> {
vec![
("argmax-temp0", 4, 256, 0, 1.0, 0.0, 0), ("topk1", 3, 1024, 1, 1.0, 1.0, 42), ("topk40", 2, 32000, 40, 1.0, 0.8, 7), ("topp", 2, 4096, 0, 0.9, 1.0, 123), ("topk-topp", 5, 8192, 50, 0.95, 0.7, 999), ]
}
#[test]
fn sample_cpu_runs() {
for (name, b, v, k, p, t, s) in cases() {
let out = run_on(Device::Cpu, b, v, k, p, t, s);
assert_eq!(out.len(), b, "{name}: expected {b} sampled indices");
for &idx in &out {
assert!(
idx >= 0.0 && (idx as usize) < v,
"{name}: index {idx} out of range"
);
}
}
}
#[test]
#[cfg(all(target_os = "macos", feature = "metal"))]
fn sample_metal_matches_cpu() {
for (name, b, v, k, p, t, s) in cases() {
let metal = run_on(Device::Metal, b, v, k, p, t, s);
let cpu = run_on(Device::Cpu, b, v, k, p, t, s);
assert_eq!(
metal, cpu,
"metal {name}: sampled indices diverge {metal:?} vs {cpu:?}"
);
eprintln!("metal {name}: {metal:?} == cpu");
}
}