Conversation
schaubh
left a comment
There was a problem hiding this comment.
The seed-width diagnosis is correct for the current uint32_t sensor callers. Please address the two inline findings: preserve the public utility's distinct-stream guarantee for 64-bit input seeds, and qualify the release note's portability and compatibility claims.
Validation on the PR head: 7 GaussMarkov tests and 63 sensor tests passed. The 64-bit collision was reproduced on macOS with an explicit equivalent 64-bit linear congruential engine, rather than a native Linux build.
schaubh
left a comment
There was a problem hiding this comment.
Good catch. Found small edge case and documentation update that is needed.
8e12415 to
6bc9f45
Compare
Check secondary seeds against both 32-bit and 64-bit primary engine states while preserving primary seeding and secondary seed portability. Add regression coverage for wide-seed collisions and zero/one normalization. Update API documentation, known issues, and release notes. Validation: 8 GaussMarkov tests and 63 sensor tests passed.
Limit the portability claim to raw engine sequences and explain that sensor outputs can still differ across standard libraries. Document changes to existing secondary sequences on builds with a 64-bit engine result type. Update the matching known-issue entry. Validation: Sphinx rendering and pre-commit checks passed.
|
@robotrocketscience , I had addressed the two PR issues on this branch. Please let me know if you agree, I can then approve and merge this branch. |
|
@robotrocketscience , are you good with my edits? |
deriveSecondarySeed XORs the base seed with a 64-bit discriminator, so the returned value always carries bits above 32. Every consumer narrows it with static_cast<std::minstd_rand::result_type>, and that type is std::uint_fast32_t, whose width is implementation defined -- 32 bit with libc++, 64 bit with libstdc++ on LP64. The secondary engine therefore seeded differently per platform, contradicting the function's own contract of a repeatable stream. Mask both seeds to 32 bits so the narrowing casts are no-ops everywhere.
Asserts the derived seed for the default RNGSeed equals 0x64e7b6c4 and that its first draw is 128424993, and that no base seed -- including one that fills all 64 bits -- yields a derived seed wider than 32 bits. The exact-value checks fail on every platform without the mask, since the unmasked candidate is returned in full.
Check secondary seeds against both 32-bit and 64-bit primary engine states while preserving primary seeding and secondary seed portability. Add regression coverage for wide-seed collisions and zero/one normalization. Update API documentation, known issues, and release notes. Validation: 8 GaussMarkov tests and 63 sensor tests passed.
Limit the portability claim to raw engine sequences and explain that sensor outputs can still differ across standard libraries. Document changes to existing secondary sequences on builds with a 64-bit engine result type. Update the matching known-issue entry. Validation: Sphinx rendering and pre-commit checks passed.
6bc9f45 to
81d8b08
Compare
Check secondary seeds against both 32-bit and 64-bit primary engine states while preserving primary seeding and secondary seed portability. Add regression coverage for wide-seed collisions and zero/one normalization. Update API documentation, known issues, and release notes. Validation: 8 GaussMarkov tests and 63 sensor tests passed.
Description
GaussMarkov::deriveSecondarySeed()(added in #1526, commit 8c0f4ba) XORs the base seed with a 64-bit discriminator:SysModel::RNGSeedisuint32_t(sys_model.h:50), so the returned value always carries0x9E3779B9in its upper word. Every consumer then narrows it withstatic_cast<std::minstd_rand::result_type>, and that type isstd::uint_fast32_t, whose width is implementation defined. The secondary engine therefore seeds differently depending on the standard library Basilisk was built against:sizeof(std::minstd_rand::result_type)RNGSeed0x0000000064e7b6c40x9e3779b964e7b6c4That contradicts the function's own contract at
gauss_markov.h:54("Seed for a distinct, repeatable secondary random stream"). It affects the four derived streams: CSS fault noise (coarseSunSensor.cpp:40), IMU gyro error (imuSensor.cpp:36), and the magnetometer and tempMeasurement spike generators.The fix masks both seeds to 32 bits so the narrowing casts are no-ops on every platform. The primary stream is untouched — it is seeded directly from the 32-bit
RNGSeed.Scope note
This is not a regression in observable noise values. The consuming distributions are implementation-defined regardless: from an identical engine state,
std::normal_distributionyields -0.078154 on libc++ against -0.077279 on libstdc++, so per-platform noise output already differed and still will. What changes is that the engine seed is now derived identically everywhere, which is what the function documents.A related pre-existing case is left alone deliberately:
setRNGSeed()andinitializeRNG()narrow auint64_tseed the same way, so a caller passing a seed above 2^32 directly still gets platform-dependent behavior. That predates #1526 and changing it would alter existing results, so it seemed better raised separately than folded in here.Verification
Built and run on Linux (GCC 16.2.1, Release):
test_gaussMarkov: 7/7 pass, including the newsecondarySeedIsPlatformIndependent.coarseSunSensor,magnetometer,imuSensor,starTrackerandtempMeasurementunit tests.src/utilities/MonteCarlo/_UnitTests: 36 pass.test_saturate,test_discretize: pass.pre-commiton all four touched files: pass.🤖 Generated with Claude Code
https://claude.ai/code/session_012CrRpn7XEWBLLHagNPzQxP