tenferro-tensor 0.3.0

Dense runtime tensors, views, backend traits, and backend-independent contracts for tenferro.
Documentation
use std::fs;
use std::path::Path;

use crate::{
    DynRank, TensorRank, TensorScalar, TensorViewCanonicalization, TypedTensor, TypedTensorView,
    TypedTensorViewMut,
};

struct CopyContractBackend;

impl TensorViewCanonicalization<i32, DynRank> for CopyContractBackend {
    fn to_contiguous(
        &mut self,
        _view: &TypedTensorView<'_, i32>,
    ) -> crate::Result<TypedTensor<i32>> {
        Err(crate::Error::backend_failure(
            "test",
            "materialization is not exercised by this copy contract test",
        ))
    }

    fn copy_into(
        &mut self,
        _src: &TypedTensorView<'_, i32>,
        _dst: &mut TypedTensorViewMut<'_, i32>,
    ) -> crate::Result<()> {
        Ok(())
    }
}

fn copy_between_views<T, R, B>(
    backend: &mut B,
    src: &TypedTensorView<'_, T, R>,
    dst: &mut TypedTensorViewMut<'_, T, R>,
) -> crate::Result<()>
where
    T: TensorScalar,
    R: TensorRank,
    B: TensorViewCanonicalization<T, R>,
{
    backend.copy_into(src, dst)
}

#[test]
fn typed_tensor_storage_fields_are_accessor_based() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");

    assert!(
        !source.contains("pub buffer: StorageBuffer<T>"),
        "TypedTensor storage must not expose a public buffer field"
    );
    assert!(
        !source.contains("pub placement: Placement"),
        "TypedTensor placement must not expose a public field"
    );
    assert!(
        !source.contains("pub id: u64"),
        "BackendStorageHandle ids must remain opaque"
    );
    assert!(
        source.contains("pub fn buffer(&self)"),
        "TypedTensor should expose read-only buffer inspection through an accessor"
    );
    assert!(
        source.contains("pub fn placement(&self)"),
        "TypedTensor should expose placement inspection through an accessor"
    );
}

#[test]
fn typed_tensor_uses_one_typed_group_owner() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");
    let typed_tensor = source
        .split_once("pub struct TypedTensor<T, R: TensorRank = DynRank>")
        .expect("TypedTensor definition must exist")
        .1
        .split_once("/// The sole owner handle")
        .expect("TypedTensor definition must precede OwnedTensorGroup")
        .0;

    assert!(
        !typed_tensor.contains("buffer: StorageBuffer<T>"),
        "TypedTensor must not retain a second physical buffer owner"
    );
    assert!(
        !typed_tensor.contains("group: Option<OwnedTensorGroup<R>>"),
        "TypedTensor must always carry its typed owner group"
    );
    assert!(
        typed_tensor.contains("group: OwnedTensorGroup<R>"),
        "TypedTensor must own one typed allocation group"
    );
    assert!(
        !source.contains("slot: Option<DescriptorSlot>"),
        "the single-owner tensor group must always retain its descriptor slot"
    );
    assert!(
        !source.contains("from_backend_buffer_untyped"),
        "backend construction must not create an untyped group fallback"
    );
}

#[test]
fn tensor_views_do_not_expose_legacy_physical_slice_names() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");

    assert!(
        !source.contains("as_physical_slice"),
        "typed tensor views must use explicit host_storage accessors, not legacy physical-slice names"
    );
}

#[test]
fn tensor_types_do_not_expose_row_major_compatibility_apis() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");

    assert!(
        !source.contains("from_vec_row_major") && !source.contains("into_vec_row_major"),
        "tensor public API must stay column-major only; row-major conversion belongs outside tenferro"
    );
}

#[test]
fn elementwise_fusion_ir_is_not_top_level_raw_api() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let lib = fs::read_to_string(crate_dir.join("src/lib.rs"))
        .expect("tenferro-tensor lib source must be readable");
    let backend = fs::read_to_string(crate_dir.join("src/backend.rs"))
        .expect("tenferro-tensor backend source must be readable");

    assert!(
        !lib.contains("ElementwiseFusionInst, ElementwiseFusionOp, ElementwiseFusionPlan"),
        "elementwise fusion IR must not be re-exported as a top-level public tensor API"
    );
    assert!(
        !backend.contains("pub dtype: crate::DType"),
        "ElementwiseFusionPlan dtype storage must not be a public field"
    );
    assert!(
        !backend.contains("pub input_count: usize"),
        "ElementwiseFusionPlan input_count storage must not be a public field"
    );
    assert!(
        !backend.contains("pub outputs: Vec<usize>"),
        "ElementwiseFusionPlan output storage must not be a public field"
    );
    assert!(
        !backend.contains("pub ops: Vec<ElementwiseFusionInst>"),
        "ElementwiseFusionPlan op storage must not be a public field"
    );
    assert!(
        !backend.contains("pub op: ElementwiseFusionOp"),
        "ElementwiseFusionInst op storage must not be a public field"
    );
    assert!(
        !backend.contains("pub inputs: Vec<usize>"),
        "ElementwiseFusionInst input storage must not be a public field"
    );
}

#[test]
fn tensor_scalar_helpers_do_not_expose_cpu_conjugation_hook() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");

    assert!(
        !source.contains("pub trait ConjElem"),
        "CPU conjugation helpers must not be part of the public tensor scalar API"
    );
}

#[test]
fn crate_root_reexports_are_explicit() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/lib.rs"))
        .expect("tenferro-tensor lib source must be readable");

    for forbidden in [
        "pub use config::*;",
        "pub use error::*;",
        "pub use types::*;",
        "pub use tenferro_tensor_core::*;",
    ] {
        assert!(
            !source.contains(forbidden),
            "crate-root public API must use deliberate explicit re-exports: {forbidden}"
        );
    }
}

#[test]
fn view_canonicalization_uses_symmetric_copy_into_contract() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let source = fs::read_to_string(crate_dir.join("src/backend.rs"))
        .expect("tenferro-tensor backend source must be readable");
    let trait_body = source
        .split_once("pub trait TensorViewCanonicalization")
        .expect("TensorViewCanonicalization trait must exist")
        .1
        .split_once("/// Optional elementwise fusion execution.")
        .expect("TensorViewCanonicalization trait must precede TensorFusion")
        .0;

    assert!(
        source.contains("TensorViewCanonicalization<T: TensorScalar, R: TensorRank>"),
        "view canonicalization must use the execution scalar contract"
    );
    assert!(
        trait_body.contains("fn copy_into(")
            && trait_body.contains("src: &TypedTensorView<'_, T, R>")
            && trait_body.contains("dst: &mut TypedTensorViewMut<'_, T, R>"),
        "copy_into must accept readable and writable views with the trait rank"
    );
    assert!(
        !trait_body.contains("copy_from_contiguous"),
        "the asymmetric copy_from_contiguous method must leave the backend trait"
    );

    let src = TypedTensor::<i32>::from_vec_col_major(vec![1], vec![1]).unwrap();
    let mut dst = TypedTensor::<i32>::from_vec_col_major(vec![1], vec![0]).unwrap();
    copy_between_views(
        &mut CopyContractBackend,
        &src.as_view(),
        &mut dst.as_view_mut(),
    )
    .unwrap();
}

#[test]
fn structural_runtime_materialization_is_erased_and_context_free_copies_are_removed() {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let backend = fs::read_to_string(crate_dir.join("src/backend.rs"))
        .expect("tenferro-tensor backend source must be readable");
    let structural = backend
        .split_once("pub trait TensorStructural")
        .expect("TensorStructural trait must exist")
        .1
        .split_once("/// Reduction operations.")
        .expect("TensorStructural must precede TensorReduction")
        .0;

    assert!(
        structural.contains(
            "fn to_contiguous_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor>"
        ),
        "runtime materialization must use the erased TensorRead/Tensor result surface"
    );
    assert!(
        structural.contains("fn copy_read_into(")
            && structural.contains("src: TensorRead<'_>")
            && structural.contains("dst: TensorWrite<'_>"),
        "runtime copy must use erased read/write values"
    );
    let types = fs::read_to_string(crate_dir.join("src/types.rs"))
        .expect("tenferro-tensor types source must be readable");
    for removed in [
        "pub fn to_contiguous(&self)",
        "pub fn copy_from_contiguous(",
        "pub fn to_tensor(&self) -> crate::Result<Tensor>",
        "materialize_view_buffer_col_major",
        "materialize_typed_view_col_major",
    ] {
        assert!(
            !types.contains(removed),
            "context-free tensor movement must be absent: {removed}"
        );
    }
}