use candle_core::{DType, Device, Tensor as CandleTensor};
use crate::runtime::{
error::RuntimeError,
tensor::{Shape, Tensor, TensorData, TensorDataView},
};
fn backend_err(e: impl std::fmt::Display) -> RuntimeError {
RuntimeError::InferenceFailed(e.to_string())
}
pub(crate) fn to_candle(t: &Tensor, dev: &Device) -> Result<CandleTensor, RuntimeError> {
let dims = t.shape().dims().to_vec();
match t.view().data() {
TensorDataView::F32(data) => {
CandleTensor::from_slice(data, dims.as_slice(), dev).map_err(backend_err)
}
TensorDataView::I64(data) => {
CandleTensor::from_slice(data, dims.as_slice(), dev).map_err(backend_err)
}
TensorDataView::I32(data) => {
let widened: Vec<i64> = data.iter().map(|&v| v as i64).collect();
CandleTensor::from_slice(&widened, dims.as_slice(), dev).map_err(backend_err)
}
}
}
pub(crate) fn from_candle(c: &CandleTensor) -> Result<Tensor, RuntimeError> {
let dims = c.dims().to_vec();
let flat = c.flatten_all().map_err(backend_err)?;
match c.dtype() {
DType::F32 => {
let data = flat.to_vec1::<f32>().map_err(backend_err)?;
Tensor::new(Shape::new(dims), TensorData::F32(data))
}
DType::I64 => {
let data = flat.to_vec1::<i64>().map_err(backend_err)?;
Tensor::new(Shape::new(dims), TensorData::I64(data))
}
other => Err(RuntimeError::InferenceFailed(format!(
"from_candle: unsupported candle dtype {other:?}"
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f32_roundtrip_preserves_shape_and_data() {
let original = Tensor::new(
Shape::new(vec![1, 2, 3]),
TensorData::F32(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]),
)
.unwrap();
let dev = Device::Cpu;
let c = to_candle(&original, &dev).unwrap();
assert_eq!(c.dims(), &[1, 2, 3]);
let recovered = from_candle(&c).unwrap();
assert_eq!(recovered.shape().dims(), &[1, 2, 3]);
assert_eq!(
recovered.view().data().as_f32(),
Some(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0][..])
);
}
#[test]
fn test_i64_to_candle_preserves_values() {
let t = Tensor::new(Shape::new(vec![1]), TensorData::I64(vec![123])).unwrap();
let c = to_candle(&t, &Device::Cpu).unwrap();
assert_eq!(c.dims(), &[1]);
assert_eq!(
c.flatten_all().unwrap().to_vec1::<i64>().unwrap(),
vec![123]
);
}
#[test]
fn test_i64_roundtrip_preserves_dtype_and_data() {
let original = Tensor::new(Shape::new(vec![1, 3]), TensorData::I64(vec![1, 2, 3])).unwrap();
let c = to_candle(&original, &Device::Cpu).unwrap();
assert_eq!(c.dtype(), DType::I64);
let recovered = from_candle(&c).unwrap();
assert_eq!(recovered.shape().dims(), &[1, 3]);
assert_eq!(recovered.view().data().as_i64(), Some(&[1, 2, 3][..]));
}
}