Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/numsim_cas/tensor/simplifier/tensor_simplifier_sub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,23 @@ sub_base::expr_holder_t sub_base::dispatch(tensor const &) {
return _rhs.accept(visitor);
}

// 0 - expr
// 0 - expr (operator- so -0/-(-x) normalize — round-7 parity, issue #422:
// the round-7 fix covered scalar + t2s and missed this domain, so
// 0 - (-A) built negative(negative(A)))
sub_base::expr_holder_t sub_base::dispatch(tensor_zero const &) {
if (is_same<tensor_zero>(m_rhs))
return make_expression<tensor_zero>(m_rhs.get().dim(), m_rhs.get().rank());
return make_expression<tensor_negative>(std::move(m_rhs));
return -std::move(m_rhs);
}

// - expr_lhs - expr_rhs --> -(expr_lhs+expr_rhs)
// operator-, not a raw negative node (round-7 parity, issue #422): the sum
// may itself normalize to a negative or to zero, and wrapping either mints
// the nested-negative / negative(zero) shapes round-7 eliminated. The
// is_valid guard is tensor-specific (tensor add can yield an invalid
// holder on full cancellation) and stays.
sub_base::expr_holder_t sub_base::dispatch(tensor_negative const &lhs) {
auto expr{lhs.expr() + std::move(m_rhs)};
if (expr.is_valid()) {
return make_expression<tensor_negative>(expr);
return -std::move(expr);
}
return make_expression<tensor_zero>(lhs.dim(), lhs.rank());
}
Expand Down
25 changes: 25 additions & 0 deletions tests/CoreBugFixTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,31 @@ TEST(RoundSevenReview, AddCancelsAgainstNegativeChild) {
EXPECT_TRUE(*f == *(trace(A) + w(c4))) << to_string(f);
}

// R7 tensor parity (issue #422): the round-7 fix covered scalar + t2s and
// missed the tensor domain — 0 - (-A) built negative(negative(A)) (found
// downstream: numsim-codegen's emitter turned it into invalid `--A`), and
// a negative-lhs sub whose sum normalizes could re-wrap into the nested
// shapes round-7 eliminated. Tensor sub_base now routes dispatch(zero) and
// dispatch(negative) through operator- like the other two domains.
TEST(RoundSevenReview, TensorSubNormalizesLikeScalarAndT2s) {
auto [A, B] =
make_tensor_variable(std::tuple{"A", std::size_t{3}, std::size_t{2}},
std::tuple{"B", std::size_t{3}, std::size_t{2}});
auto zero = make_expression<tensor_zero>(3, 2);
// 0 - (-A) --> A (was negative(negative(A)))
auto e1 = zero - (-A);
EXPECT_TRUE(*e1 == *A) << to_string(e1);
// 0 - 0 --> 0 (previous special case preserved through operator-)
auto e0 = zero - make_expression<tensor_zero>(3, 2);
EXPECT_TRUE(is_same<tensor_zero>(e0)) << to_string(e0);
// (-A) - (-A) --> 0, not negative(zero)
auto e2 = (-A) - (-A);
EXPECT_TRUE(is_same<tensor_zero>(e2)) << to_string(e2);
// (-A) - B --> -(A + B): unchanged for the ordinary case
auto e3 = (-A) - B;
EXPECT_TRUE(*e3 == *(-(A + B))) << to_string(e3);
}

// Round-8 review: regressions from the round-7 negation probe.

// R8-1: merge_add consumed the same rhs child twice when the lhs held an
Expand Down
Loading