Skip to content

31 nondeterministic rng seed in tests makes troubleshooting impossible should use c rng everywhere#33

Merged
evaleev merged 24 commits intomainfrom
31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere
Jan 26, 2026
Merged

31 nondeterministic rng seed in tests makes troubleshooting impossible should use c rng everywhere#33
evaleev merged 24 commits intomainfrom
31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere

Conversation

@SpaceyLake
Copy link
Collaborator

The main thing I did to solve the issue was to rewrite how extents are assigned. While doing that, I found more ways of improving the code, such as templating functions. I also rewrote the code for deciding the index distribution and index assignment. The code now also completely uses the new way of randomizing things. I am unsure about the Valgrind failure. I don't understand where those two possibly lost blocks originate.

evaleev and others added 2 commits January 15, 2026 10:22
change constexpr flag use_cpp_rng to tru to enable ... doing so reveals more issues in test.cc
@evaleev
Copy link
Contributor

evaleev commented Jan 22, 2026

Looks good, thanks!

I am unsure about the Valgrind failure. I don't understand where those two possibly lost blocks originate.

Seems to be some internal scratch allocated in TBLIS? @devinamatthews https://github.com/TAPPorg/reference-implementation/actions/runs/21250446136/job/61150346309?pr=33#step:12:66

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request addresses issue #31 by replacing the nondeterministic RNG system with C++'s <random> library to enable reproducible test results via seeding. The changes include significant refactoring to use templates for reducing code duplication, rewriting the index distribution and extent assignment logic, and renaming variables for clarity (e.g., "unique_idx" → "isolated_idx", "idx" → "nmode" in test names).

Changes:

  • Implemented seeded RNG using std::mt19937 with command-line configurable seed
  • Templated test helper functions (run_tblis_mult, compare_tensors, generate_pseudorandom_contraction, etc.) to support multiple data types
  • Rewrote index and extent assignment logic with new helper functions (generate_unique_indices, assign_indices, assign_extents, generate_index_extent_map)

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated 17 comments.

File Description
test/test.h Updated function declarations to use templates; renamed functions for clarity (unique_idx → isolated_idx, same_idx → same_nmode); added new helper function declarations
test/test.cpp Implemented seeded RNG engine; templated helper functions; rewrote random contraction generation logic; updated all test functions to use new templated interfaces

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

test/test.cpp Outdated
}
std::shuffle(unique_indices, unique_indices + total_unique_indices, std::default_random_engine()); // Shuffle the unique indices
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shuffle operations use std::default_random_engine() without a seed, which creates a new unseeded engine each time. This defeats the purpose of using a seeded RNG for reproducibility. These should use rand_engine() instead to ensure deterministic behavior. This issue occurs on lines 731, 770, 786, and 787.

Suggested change
std::shuffle(unique_indices, unique_indices + total_unique_indices, std::default_random_engine()); // Shuffle the unique indices
std::shuffle(unique_indices, unique_indices + total_unique_indices, rand_engine()); // Shuffle the unique indices using the seeded engine

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
{
idx_B[i + contractions] = 'a' + nmode_A - repeated_idx_A + i;
}
std::shuffle(idx_D, idx_D + (free_indices_A + hadamard_indices + free_indices_B), std::default_random_engine()); // Shuffle indices for D
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shuffle operations use std::default_random_engine() without a seed, which creates a new unseeded engine each time. This defeats the purpose of using a seeded RNG for reproducibility. These should use rand_engine() instead to ensure deterministic behavior.

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
Comment on lines 495 to 499
int max_contracted_indices = (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2;
}
else
{
int max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable shadowing issue: int max_contracted_indices is declared inside an if block at line 495, then referenced outside that block at line 499. However, there's another declaration of int max_contracted_indices at line 492 that is not used. The variable at line 495 should be an assignment, not a declaration. The current code will not compile or will have undefined behavior.

Suggested change
int max_contracted_indices = (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2;
}
else
{
int max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;
max_contracted_indices = (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2;
}
else
{
max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
}

if (nmode_B > 0)
// TODO: When repeated indices are enabled the tensors need at least one other index. This is not yet ensured.
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO comment indicates that when repeated indices are enabled, tensors need at least one other index, but this constraint is not yet enforced. This could lead to invalid tensor configurations. Consider implementing this validation or removing the feature flag until the constraint can be properly enforced.

Copilot uses AI. Check for mistakes.
test/test.h Outdated
Comment on lines 118 to 119
bool test_subtensor_same_nmode();
bool test_subtensor_lower_nmode();
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming: The function parameter names use "nmode" (e.g., subtensor_on_nmode at line 57), but the actual variable being modified/used is often the number of modes. However, the test function names were changed from "test_subtensor_same_idx" to "test_subtensor_same_nmode" (line 118), but these functions test whether subtensors have the same number of modes as their parent tensors, not whether indices are the same. The naming change improves clarity, but "nmode" typically refers to "number of modes", so "same_nmode" might be better expressed as "matching_nmode" or "unchanged_nmode" for better readability.

Copilot uses AI. Check for mistakes.
bool test_contraction_double_precision();
bool test_contraction_complex();
bool test_contraction_complex_double_precision();
bool test_zero_stride();
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name changed from "test_unique_idx" to "test_isolated_idx" (line 130), but the PR description mentions "rewriting code for deciding the index distribution and index assignment" without explaining the semantic difference between "unique" and "isolated" indices. The function implementation suggests these are indices that appear in only one tensor. Consider adding a comment explaining what "isolated indices" means in this context.

Suggested change
bool test_zero_stride();
bool test_zero_stride();
// "Isolated indices" are indices that appear in exactly one tensor (i.e., they are not shared or contracted with any other tensor).

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
isolated_indices_A, isolated_indices_B,
repeated_indices_A, repeated_indices_B);

std::unordered_map<int, int64_t> index_extent_map = generate_index_extent_map(min_extent, 4, total_unique_indices, unique_indices);
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In generate_pseudorandom_contraction, the equal_extents_only parameter is passed in but never used within the function. It's passed to generate_index_extent_map at line 301, but that function doesn't accept such a parameter. The equal_extents_only logic needs to be implemented, likely in the generate_index_extent_map function or before calling it.

Suggested change
std::unordered_map<int, int64_t> index_extent_map = generate_index_extent_map(min_extent, 4, total_unique_indices, unique_indices);
std::unordered_map<int, int64_t> index_extent_map;
if (equal_extents_only) {
// Assign the same extent to all unique indices
int64_t common_extent = rand(min_extent, 4);
for (int64_t i = 0; i < total_unique_indices; ++i) {
index_extent_map[unique_indices[i]] = common_extent;
}
} else {
index_extent_map = generate_index_extent_map(min_extent, 4, total_unique_indices, unique_indices);
}

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
Comment on lines 1060 to 1062
static_assert(std::is_same_v<T, void>,
"rand<T>: unsupported type");
}
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static_assert at line 1060 uses std::is_same_v<T, void> as a condition that will always be false (since T is the actual type passed to the template). This appears to be an intentional way to generate a compile-time error for unsupported types, but the error message could be misleading. A better approach would be to use a false-valued type trait or simply false with a clearer error message like "Unsupported type for rand function".

Copilot uses AI. Check for mistakes.
test/test.cpp Outdated
Comment on lines 495 to 499
int max_contracted_indices = (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2;
}
else
{
int max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calculation for max_contracted_indices appears incorrect. The expression (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2 divides the modulo result by 2. Since modulo 2 can only be 0 or 1, dividing by 2 will always yield 0 (integer division). This doesn't seem like the intended logic for calculating the maximum number of contracted indices. The formula should likely be ((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))/2 without the modulo operation.

Suggested change
int max_contracted_indices = (((nmode_B - hadamard_indices) + (nmode_A - hadamard_indices) - (nmode_D - hadamard_indices))%2)/2;
}
else
{
int max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;
max_contracted_indices = ((nmode_B - hadamard_indices)
+ (nmode_A - hadamard_indices)
- (nmode_D - hadamard_indices)) / 2;
}
else
{
max_contracted_indices = std::min(nmode_A, nmode_B) - hadamard_indices;

Copilot uses AI. Check for mistakes.
Comment on lines 1066 to 1069
T rand()
{
return std::complex<float>(rand_s(), rand_s());
return rand<T>(-RAND_MAX, RAND_MAX);
}
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the rand template function, when the input types are integers or floating-point values, the function uses RAND_MAX for the default range. However, RAND_MAX is a macro from the C standard library (typically 32767 or 2147483647) and may not be appropriate for all template types T. For example, if T is int64_t and RAND_MAX is 32767, the range would be limited. Consider using std::numeric_limits<T>::max() instead for better type safety.

Copilot uses AI. Check for mistakes.
@evaleev
Copy link
Contributor

evaleev commented Jan 22, 2026

@SpaceyLake some of the copilot comments look useful ...

@evaleev
Copy link
Contributor

evaleev commented Jan 23, 2026

Looks good, thanks!

I am unsure about the Valgrind failure. I don't understand where those two possibly lost blocks originate.

Seems to be some internal scratch allocated in TBLIS? @devinamatthews https://github.com/TAPPorg/reference-implementation/actions/runs/21250446136/job/61150346309?pr=33#step:12:66

@SpaceyLake #34 implements a workaround this issue, rebase on master so that valgrind test can pass also ...

@devinamatthews
Copy link
Contributor

You would need to call bli_finalize to clean those up.

evaleev and others added 14 commits January 23, 2026 18:38
change constexpr flag use_cpp_rng to tru to enable ... doing so reveals more issues in test.cc
…ting-impossible-should-use-c-rng-everywhere' of github.com:TAPPorg/reference-implementation into 31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere
change constexpr flag use_cpp_rng to tru to enable ... doing so reveals more issues in test.cc
…ting-impossible-should-use-c-rng-everywhere' of github.com:TAPPorg/reference-implementation into 31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere
@evaleev
Copy link
Contributor

evaleev commented Jan 23, 2026

You would need to call bli_finalize to clean those up.

unfortunately can't include <blis.h> alongside <tblis.h> to be able to call it safely:

image

@devinamatthews
Copy link
Contributor

devinamatthews commented Jan 23, 2026 via email

evaleev and others added 2 commits January 23, 2026 14:15
The packm_bsmtc functions in TBLIS/BLIS trigger "Conditional jump
depends on uninitialised value" errors that appear to be false
positives in architecture-specific packing code. The suppression
uses wildcards to match any architecture variant (zen3, haswell, etc.).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ting-impossible-should-use-c-rng-everywhere' of github.com:TAPPorg/reference-implementation into 31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere
@evaleev
Copy link
Contributor

evaleev commented Jan 23, 2026

@SpaceyLake finally, CI passes. Let's merge (unless copilot has something useful to say) and lock the master for CI-breaking merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 4 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +18
unsigned int current_rand_seed = 0;
auto& rand_engine() {
static std::mt19937 engine(current_rand_seed);
return engine;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title mentions using a C RNG everywhere, but the updated tests now use std::mt19937/<random> instead. If the intent is truly to use the C RNG, this is a mismatch; otherwise consider updating the PR title/description to reflect the switch to the C++ random engine.

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +110
if constexpr (std::is_same_v<T, float>)
{
tblis_init_tensor_scaled_s(&tblis_A, alpha, nmode_A, tblis_len_A, A, tblis_stride_A);
tblis_init_tensor_s(&tblis_B, nmode_B, tblis_len_B, B, tblis_stride_B);
tblis_init_tensor_scaled_s(&tblis_C, beta, nmode_C, tblis_len_C, C, tblis_stride_C);
tblis_init_tensor_scaled_s(&tblis_D, 0, nmode_D, tblis_len_D, D, tblis_stride_D);
}
else if constexpr (std::is_same_v<T, double>)
{
tblis_init_tensor_scaled_d(&tblis_A, alpha, nmode_A, tblis_len_A, A, tblis_stride_A);
tblis_init_tensor_d(&tblis_B, nmode_B, tblis_len_B, B, tblis_stride_B);
tblis_init_tensor_scaled_d(&tblis_C, beta, nmode_C, tblis_len_C, C, tblis_stride_C);
tblis_init_tensor_scaled_d(&tblis_D, 0, nmode_D, tblis_len_D, D, tblis_stride_D);
}
else if constexpr (is_complex_v<T>)
{
using value_type = typename T::value_type;
if constexpr (std::is_same_v<value_type, float>)
{
tblis_init_tensor_scaled_c(&tblis_A, alpha, nmode_A, tblis_len_A, A, tblis_stride_A);
tblis_init_tensor_c(&tblis_B, nmode_B, tblis_len_B, B, tblis_stride_B);
tblis_init_tensor_scaled_c(&tblis_C, beta, nmode_C, tblis_len_C, C, tblis_stride_C);
tblis_init_tensor_scaled_c(&tblis_D, 0, nmode_D, tblis_len_D, D, tblis_stride_D);
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_tblis_mult takes op_A/op_B/op_C/op_D but never applies them to the created tblis_tensor objects (e.g., setting .conj). This means the reference TBLIS computation ignores conjugation for complex tensors, so test_contraction_complex* can produce incorrect comparisons. Apply the op flags to the appropriate tblis_* tensors (or drop these parameters if they are intentionally unsupported).

Copilot uses AI. Check for mistakes.
…ting-impossible-should-use-c-rng-everywhere' of github.com:TAPPorg/reference-implementation into 31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere
@SpaceyLake
Copy link
Collaborator Author

Did some fixes on copilots comments. The one about the op parameters, I feel, can be fixed later. No test tests the elemental operations anyway, right now. If I were to change the call to TBLIS, I would like to do a proper mapping to TAPP anyway. I believe we can merge this to main now.

@evaleev evaleev merged commit ce1a4f7 into main Jan 26, 2026
12 checks passed
@evaleev evaleev deleted the 31-nondeterministic-rng-seed-in-tests-makes-troubleshooting-impossible-should-use-c-rng-everywhere branch January 26, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nondeterministic RNG seed in tests makes troubleshooting impossible, should use C++ RNG everywhere

3 participants