Skip to content

No dim/rank validation on tensor add / inner_product construction; dot_product precondition uses || and is assert-only #360

Description

@petlenz

Summary

Shape errors construct silently instead of throwing; the malformed nodes later hit the evaluator with mismatched buffer sizes (out-of-bounds reads / garbage sums in tensor_data_add/tensor_data_inner_product, which trust the parent's dim()/rank()). Reproduced at 0f6c0b9 (construction side):

  • X(3D, rank2) + C(3D, rank4) accepted — node claims rank 4 with mixed-rank children (tensor_simplifier_add.h:36-41 builds tensor_add(m_lhs.dim(), m_rhs.rank()) — mixed provenance, no equality check).
  • X(3D) + W(2D) accepted, claiming dim 3.
  • inner_product(X_3D, {2}, W_2D, {1}) accepted — the dim check in the inner_product_wrapper ctor is commented out (tensor/wrappers/inner_product_wrapper.h:19-40); index bounds/pairing arity not validated either.
  • dot_product's precondition (src/numsim_cas/tensor_to_scalar/tensor_to_scalar_functions.cpp:20-21) uses || where a scalar result requires && — and it's a debug-only assert (NDEBUG-elided). dot(u) accepts any rank but dcontract_self_op only evaluates rank 2, so dot(u) on a vector constructs fine and fails at evaluation.

Severity: medium (needs a user shape error, but then silently wrong / UB). This is the remaining half of the closed #53, the same assert-vs-throw pattern as #295/#292.

Fix

  • Tensor add_fn/sub_fn tag_invokes: throw invalid_expression_error on dim or rank mismatch (one check each).
  • inner_product_wrapper ctor: restore the dim check; validate lhs_indices.size() == rhs_indices.size() and index bounds (mirror permute_indices).
  • dot_product: change ||&& and make it a thrown invalid_expression_error (not assert); gate dot()'s self-contraction to ranks its evaluator supports, or extend the evaluator.

Tests (lock-in)

TEST(ShapeValidation, MismatchedAddThrows) {
  auto [X] = make_tensor_variable(std::tuple{"X", 3, 2});
  auto [C] = make_tensor_variable(std::tuple{"C", 3, 4});
  auto [W] = make_tensor_variable(std::tuple{"W", 2, 2});
  EXPECT_THROW(X + C, invalid_expression_error);
  EXPECT_THROW(X + W, invalid_expression_error);
}

TEST(ShapeValidation, InnerProductDimAndIndices) {
  auto [X] = make_tensor_variable(std::tuple{"X", 3, 2});
  auto [W] = make_tensor_variable(std::tuple{"W", 2, 2});
  EXPECT_THROW(inner_product(X, sequence{2}, W, sequence{1}), invalid_expression_error);
  EXPECT_THROW(inner_product(X, sequence{1,2}, X, sequence{1}), invalid_expression_error); // arity
}

TEST(ShapeValidation, DotProduct) {
  auto [u] = make_tensor_variable(std::tuple{"u", 3, 1});
  // release-build behavior must match debug: mismatched sequence sizes throw
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions