Summary
permute_indices_wrapper (include/numsim_cas/tensor/wrappers/permute_indices_wrapper.h:20-57) stores its permutation in m_indices but inherits unary_op::update_hash_value (core/unary_op.h:52) and unary_op::operator== (core/unary_op.h:85), neither of which sees the sequence. Two different permutations of the same tensor hash equal and compare equal:
// rank-3 T
auto p1 = permute_indices(T, sequence{2,1,3});
auto p2 = permute_indices(T, sequence{1,3,2});
*p1 == *p2; // true — WRONG
to_string(p1 - p2); // "0{3}" — WRONG (different tensors)
to_string(p1 + p2); // "2*permute_indices(T,{1,3,2})" — WRONG
Reproduced at 0f6c0b9. Also poisons substitute (matches the wrong permutation) and n-ary map keying. This is exactly the bug class fixed for inner_product_wrapper in #266; this node was missed.
Severity: critical for rank≥3 users; rank-2 mostly shielded (only {2,1} builds a wrapper).
Fix
Override update_hash_value() in permute_indices_wrapper to hash_combine each entry of m_indices (copy the #266 pattern from inner_product_wrapper.h:59-67), and add operator==/operator< that compare m_indices before delegating to the child comparison.
Tests (lock-in)
TEST(PermuteIdentity, DifferentPermutationsDistinct) {
auto [T] = make_tensor_variable(std::tuple{"T", 3, 3});
auto p1 = permute_indices(T, sequence{2, 1, 3});
auto p2 = permute_indices(T, sequence{1, 3, 2});
EXPECT_FALSE(*p1 == *p2);
EXPECT_NE(p1.get().hash_value(), p2.get().hash_value());
EXPECT_NE(to_string(p1 - p2), "0{3}");
// same permutation still folds
auto q = permute_indices(T, sequence{2, 1, 3});
EXPECT_EQ(to_string(p1 - q), "0{3}");
}
Summary
permute_indices_wrapper(include/numsim_cas/tensor/wrappers/permute_indices_wrapper.h:20-57) stores its permutation inm_indicesbut inheritsunary_op::update_hash_value(core/unary_op.h:52) andunary_op::operator==(core/unary_op.h:85), neither of which sees the sequence. Two different permutations of the same tensor hash equal and compare equal:Reproduced at 0f6c0b9. Also poisons
substitute(matches the wrong permutation) and n-ary map keying. This is exactly the bug class fixed forinner_product_wrapperin #266; this node was missed.Severity: critical for rank≥3 users; rank-2 mostly shielded (only
{2,1}builds a wrapper).Fix
Override
update_hash_value()inpermute_indices_wrappertohash_combineeach entry ofm_indices(copy the #266 pattern frominner_product_wrapper.h:59-67), and addoperator==/operator<that comparem_indicesbefore delegating to the child comparison.Tests (lock-in)