use tokitai_operator::backend::TensorStore;
use tokitai_operator::facade::Tokitai;
use tokitai_operator::object::{ObjectMeta, Representation, Shape, Tensor};
use tokitai_operator::op::arithmetic::AbsOp;
use tokitai_operator::planner::PlanStepKind;
fn integer_meta(shape: Vec<usize>) -> ObjectMeta {
ObjectMeta::tensor(
tokitai_operator::domain::DomainId::new("integer"),
Shape::from(shape),
Representation::dense_cpu(),
)
}
#[test]
fn abs_planner_lowers_single_input_to_single_step() {
let api = Tokitai::cpu_only();
let mut builder = tokitai_operator::facade::FacadeGraphBuilder::new();
let data = builder.input(integer_meta(vec![5]));
let out = builder.add_op(AbsOp, &[data]).expect("abs infer")[0];
let graph = builder.build();
let plan = api.plan_public(&graph).expect("plan should succeed");
let single_steps: Vec<_> = plan
.plan
.steps
.iter()
.filter(|step| step.kind == PlanStepKind::Single && step.op_name == "abs")
.collect();
assert_eq!(single_steps.len(), 1);
assert_eq!(
single_steps[0].lowering_rule_id.as_deref(),
Some("cpu.abs.dense")
);
let mut store = TensorStore::<i64>::new();
let int_domain = tokitai_operator::domain::DomainId::new("integer");
store.insert(
data,
Tensor::dense_cpu(int_domain, Shape::from(vec![5]), vec![-3, -1, 0, 1, 3]),
);
let (executed, _report) = api
.execute_i64(&graph, &plan.plan, store, &[out])
.expect("abs execution should succeed");
let result = executed.get(out).unwrap();
assert_eq!(result.data, vec![3, 1, 0, 1, 3]);
}
#[test]
fn abs_explicit_per_element_reference_with_saturating_min() {
let api = Tokitai::cpu_only();
let mut builder = tokitai_operator::facade::FacadeGraphBuilder::new();
let data = builder.input(integer_meta(vec![5]));
let out = builder.add_op(api.abs(), &[data]).unwrap()[0];
let graph = builder.build();
let plan = api.plan_public(&graph).unwrap();
let input_data = vec![-100, -5, 0, 5, i64::MIN];
let mut store = TensorStore::<i64>::new();
let int_domain = tokitai_operator::domain::DomainId::new("integer");
store.insert(
data,
Tensor::dense_cpu(int_domain, Shape::from(vec![5]), input_data.clone()),
);
let (executed, _report) = api.execute_i64(&graph, &plan.plan, store, &[out]).unwrap();
let result = executed.get(out).unwrap().data.clone();
let expected: Vec<i64> = input_data.iter().map(|v| v.saturating_abs()).collect();
assert_eq!(result, expected);
assert_eq!(expected.last(), Some(&i64::MAX));
}
#[test]
fn abs_with_empty_data_returns_empty() {
let api = Tokitai::cpu_only();
let mut builder = tokitai_operator::facade::FacadeGraphBuilder::new();
let data = builder.input(integer_meta(vec![0]));
let out = builder.add_op(api.abs(), &[data]).unwrap()[0];
let graph = builder.build();
let plan = api.plan_public(&graph).unwrap();
let mut store = TensorStore::<i64>::new();
let int_domain = tokitai_operator::domain::DomainId::new("integer");
store.insert(
data,
Tensor::dense_cpu(int_domain, Shape::from(vec![0]), vec![]),
);
let (executed, _report) = api.execute_i64(&graph, &plan.plan, store, &[out]).unwrap();
let result = executed.get(out).unwrap();
assert!(result.is_empty());
}
#[test]
fn abs_planner_rejects_wrong_input_count() {
let mut builder = tokitai_operator::facade::FacadeGraphBuilder::new();
let a = builder.input(integer_meta(vec![2]));
let b = builder.input(integer_meta(vec![2]));
let err = builder
.add_op(AbsOp, &[a, b])
.expect_err("abs should reject 2 inputs");
let msg = format!("{err}");
assert!(msg.contains("1 input"), "got: {msg}");
}