use core::any::{Any, TypeId};
use std::sync::Arc;
use crate::{Host, TypedTensor};
use tenferro_tensor_core::{DynRank, Scalar, TensorLayout};
type Payload<T> = TypedTensor<T, DynRank, Host>;
const OP: &str = "ErasedHostTensor";
fn element_count(layout: &TensorLayout) -> usize {
layout.shape().iter().product()
}
fn is_dense(layout: &TensorLayout) -> bool {
if layout.offset() != 0 {
return false;
}
let mut running: isize = 1;
for (extent, stride) in layout.shape().iter().zip(layout.strides()) {
if *stride != running {
return false;
}
running = running.saturating_mul(*extent as isize);
}
true
}
fn linear_index(layout: &TensorLayout, index: &[usize]) -> Option<isize> {
if index.len() != layout.shape().len() {
return None;
}
let mut linear = layout.offset();
for ((position, extent), stride) in index.iter().zip(layout.shape()).zip(layout.strides()) {
if position >= extent {
return None;
}
linear += stride * *position as isize;
}
Some(linear)
}
trait ErasedPayload: Send + Sync {
fn clone_payload(&self) -> Box<dyn ErasedPayload>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn gather(&self, layout: &TensorLayout) -> crate::Result<Box<dyn ErasedPayload>>;
}
impl<T: Scalar> ErasedPayload for Payload<T> {
fn clone_payload(&self) -> Box<dyn ErasedPayload> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn gather(&self, layout: &TensorLayout) -> crate::Result<Box<dyn ErasedPayload>> {
let source = self.as_slice();
let expected = element_count(layout);
let mut gathered = Vec::with_capacity(expected);
let mut index = vec![0usize; layout.shape().len()];
for _ in 0..expected {
let linear = linear_index(layout, &index)
.and_then(|linear| usize::try_from(linear).ok())
.filter(|linear| *linear < source.len())
.ok_or(crate::Error::validation(
OP,
tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch {
expected,
actual: source.len(),
},
))?;
gathered.push(source[linear]);
for (position, current) in index.iter_mut().enumerate() {
*current += 1;
if *current < layout.shape()[position] {
break;
}
*current = 0;
}
}
Ok(Box::new(Payload::from_host_vec_col_major(
layout.shape().to_vec(),
gathered,
)?))
}
}
pub struct ErasedHostTensor {
payload: Arc<dyn ErasedPayload>,
type_id: TypeId,
element: TypeId,
layout: TensorLayout,
payload_elements: usize,
dense: bool,
}
impl Clone for ErasedHostTensor {
fn clone(&self) -> Self {
Self {
payload: Arc::clone(&self.payload),
type_id: self.type_id,
element: self.element,
layout: self.layout.clone(),
payload_elements: self.payload_elements,
dense: self.dense,
}
}
}
impl core::fmt::Debug for ErasedHostTensor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ErasedHostTensor")
.field("type_id", &self.type_id)
.field("shape", &self.layout.shape())
.field("strides", &self.layout.strides())
.field("offset", &self.layout.offset())
.finish()
}
}
impl ErasedHostTensor {
pub fn new<T: Scalar>(value: TypedTensor<T, DynRank, Host>) -> Self {
let layout = value.layout();
let payload_elements = element_count(&layout);
Self {
payload: Arc::new(value),
type_id: TypeId::of::<Payload<T>>(),
element: TypeId::of::<T>(),
dense: true,
layout,
payload_elements,
}
}
#[must_use]
pub fn type_id(&self) -> TypeId {
self.type_id
}
#[must_use]
pub fn element_type_id(&self) -> TypeId {
self.element
}
#[must_use]
pub fn shape(&self) -> &[usize] {
self.layout.shape()
}
#[must_use]
pub fn strides(&self) -> &[isize] {
self.layout.strides()
}
#[must_use]
pub fn offset(&self) -> isize {
self.layout.offset()
}
#[must_use]
pub fn is_contiguous(&self) -> bool {
self.dense
}
#[must_use]
pub fn element_count(&self) -> usize {
element_count(&self.layout)
}
#[must_use]
pub fn shares_payload_with(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.payload, &other.payload)
}
#[must_use]
pub fn duplicate(&self) -> Self {
Self {
payload: Arc::from(self.payload.clone_payload()),
type_id: self.type_id,
element: self.element,
layout: self.layout.clone(),
payload_elements: self.payload_elements,
dense: self.dense,
}
}
pub fn permuted(&self, axes: &[usize]) -> crate::Result<Self> {
let layout = self
.layout
.transpose_view(axes)
.map_err(|source| crate::Error::validation("ErasedHostTensor::permuted", source))?;
let dense = is_dense(&layout);
Ok(Self {
layout,
dense,
..self.clone()
})
}
pub fn to_contiguous(&self) -> crate::Result<Self> {
let payload = self.payload.gather(&self.layout)?;
let layout = TensorLayout::compact(self.layout.shape().into()).map_err(|source| {
crate::Error::validation("ErasedHostTensor::to_contiguous", source)
})?;
let payload_elements = element_count(&layout);
Ok(Self {
payload: Arc::from(payload),
type_id: self.type_id,
element: self.element,
dense: true,
layout,
payload_elements,
})
}
#[must_use]
pub fn is<T: Scalar>(&self) -> bool {
self.payload.as_any().is::<Payload<T>>()
}
#[must_use]
pub fn downcast_ref<T: Scalar>(&self) -> Option<&Payload<T>> {
if !self.is_contiguous() {
return None;
}
self.payload.as_any().downcast_ref::<Payload<T>>()
}
pub fn downcast_mut<T: Scalar>(&mut self) -> Option<&mut Payload<T>> {
if !self.is_contiguous() {
return None;
}
Arc::get_mut(&mut self.payload)?
.as_any_mut()
.downcast_mut::<Payload<T>>()
}
#[must_use]
pub fn into_typed<T: Scalar>(mut self) -> Option<Payload<T>> {
if !self.dense {
return None;
}
let payload = Arc::get_mut(&mut self.payload)?;
let tensor = payload.as_any_mut().downcast_mut::<Payload<T>>()?;
let empty = Payload::<T>::from_host_vec_col_major(vec![0], Vec::new()).ok()?;
Some(core::mem::replace(tensor, empty))
}
#[must_use]
pub fn payload_element_count(&self) -> usize {
self.payload_elements
}
#[must_use]
pub fn as_dense<T: Scalar>(&self) -> Option<(&[T], &[usize])> {
if !self.is_contiguous() {
return None;
}
let payload = self.payload.as_any().downcast_ref::<Payload<T>>()?;
Some((payload.as_slice(), payload.shape()))
}
#[must_use]
pub fn element_at<T: Scalar>(&self, index: &[usize]) -> Option<&T> {
let payload = self.payload.as_any().downcast_ref::<Payload<T>>()?;
let linear = usize::try_from(linear_index(&self.layout, index)?).ok()?;
payload.as_slice().get(linear)
}
pub fn element_at_mut<T: Scalar>(&mut self, index: &[usize]) -> Option<&mut T> {
let layout = &self.layout;
let linear = usize::try_from(linear_index(layout, index)?).ok()?;
let payload = Arc::get_mut(&mut self.payload)?;
let payload = payload.as_any_mut().downcast_mut::<Payload<T>>()?;
payload.host_data_mut().get_mut(linear)
}
}
#[cfg(test)]
mod tests;