use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use crate::eager_backend::EagerBackend;
use crate::{EagerRuntime, Error};
use tenferro_cpu::CpuBackend;
use tenferro_tensor::{
MemoryKind, Placement, Tensor, TensorRead, TensorStructural, TensorView, TypedTensor,
TypedTensorView,
};
use tenferro_tensor_core::{ErasedHostTensor, HostTensor};
use super::super::EagerTensor;
#[test]
fn host_leaf_construction_does_not_enter_a_backend_session() -> Result<(), Error> {
let materializations = Arc::new(AtomicUsize::new(0));
let sessions = Arc::new(AtomicUsize::new(0));
let ctx = Arc::new(EagerRuntime::from_backend(
EagerBackend::recording_cpu_counting_sessions(
Arc::clone(&materializations),
Arc::clone(&sessions),
),
)?);
let native = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])
.map_err(Error::from)?;
let leaf = EagerTensor::from_tensor_in(native, Arc::clone(&ctx))?;
assert_eq!(
sessions.load(Ordering::Relaxed),
0,
"a host-placement leaf must not open a backend session"
);
assert_eq!(
leaf.value()?.as_slice::<f64>().map_err(Error::from)?,
&[1.0, 2.0, 3.0, 4.0],
"the session-free path must materialize the same value"
);
let doubled = leaf.mul(&leaf)?;
assert!(
sessions.load(Ordering::Relaxed) > 0,
"an eager operation must still enter a backend session"
);
assert_eq!(
doubled.value()?.as_slice::<f64>().map_err(Error::from)?,
&[1.0, 4.0, 9.0, 16.0]
);
Ok(())
}
#[test]
fn host_leaf_materialization_matches_the_cpu_backend_acceptance() -> Result<(), Error> {
let backend = EagerBackend::cpu(CpuBackend::new());
let host = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])
.map_err(Error::from)?;
let fast = backend
.to_contiguous_host_read(&TensorRead::from_tensor(&host))
.expect("an owned host tensor has a session-free path")
.map_err(Error::from)?;
let mut cpu = CpuBackend::new();
let session = TensorStructural::to_contiguous_read(&mut cpu, TensorRead::from_tensor(&host))
.map_err(Error::from)?;
assert_eq!(
fast.as_slice::<f64>().map_err(Error::from)?,
session.as_slice::<f64>().map_err(Error::from)?
);
let data = [1.0_f64, 2.0, 3.0, 4.0];
let view = TensorView::F64(
TypedTensorView::from_col_major(&[2, 2], &data)
.map_err(Error::from)?
.transpose_view([1, 0])
.map_err(Error::from)?,
);
assert!(backend
.to_contiguous_host_read(&TensorRead::from_view(view))
.is_none());
let mut placed =
TypedTensor::<f64>::from_vec_col_major(vec![1], vec![1.0]).map_err(Error::from)?;
placed.set_placement(Placement {
memory_kind: MemoryKind::Device,
device: None,
cpu_affinity: None,
});
let placed = Tensor::from_typed(placed);
assert!(backend
.to_contiguous_host_read(&TensorRead::from_tensor(&placed))
.is_none());
assert!(TensorStructural::to_contiguous_read(
&mut CpuBackend::new(),
TensorRead::from_tensor(&placed)
)
.is_err());
let payload =
HostTensor::from_vec_col_major(vec![1], vec![7.0_f64]).expect("valid host tensor");
let external = Tensor::external(ErasedHostTensor::new(payload));
assert!(backend
.to_contiguous_host_read(&TensorRead::from_tensor(&external))
.is_none());
Ok(())
}