Resolve !SIM.random.seed per apply_to call, not once - #974
Conversation
PoorMansHxRGReadoutNoise.apply_to and ShotNoise.apply_to wrote the resolved seed value back over the !SIM.random.seed reference in self.meta on their first call (ShotNoise directly; the readout noise additionally via the whole-meta from_currsys rebake). Any subsequent readout from a reused OpticalTrain therefore re-seeded with the FIRST exposure''s seed, no matter what !SIM.random.seed was updated to - which makes repeated-readout workflows (N darks or a detector-linearity ramp from one observe) statistically correct but impossible to reproduce bit-exactly, while single-readout runs are unaffected. Resolve into locals instead, exactly as BasicReadoutNoise and PixelResponseNonUniformity already do. First-call behaviour, resolution order and generated values are unchanged; only repeated calls now honour a per-readout seed. Measured downstream (METIS_Simulations_Deux differential harness, harness/exp_reuse_train.py): with this fix, one observe + N readouts at per-frame seeds reproduces N independently-simulated dark frames; before it, readouts 2..N carried the frame-1 readout-noise pattern rolled to random offsets (sigma matching to 4e-7 but ~92% of pixels differing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #974 +/- ##
==========================================
- Coverage 75.89% 75.88% -0.01%
==========================================
Files 70 70
Lines 9030 9029 -1
==========================================
- Hits 6853 6852 -1
Misses 2177 2177 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Another one of these super-low hanging fruit that will help massively speed up METIS_Simulations. Given that BasicReadoutNoise and PixelResponseNonUniformity also use this pattern, it should be fine to switch to this, imo. I'd be interested to hear your opinions. Thanks! |
|
That Claude sure sounds like a smug, pompous arsehole. I'm not as clever and don't use Scopesim with a seed. Just looking at the code I'm wondering whether this really changes anything - why would |
|
Can you please explain as a human why this change is necessary @astronomy,k ? Did you read yourself what your LLM wrote and did you agree with it? Does this do what we actually want it to do? What is it that you want to happen? The title seems to indicate that this resolves an open ticket, does it? If so, which one? I'd say we like to have determinism, but not have every readout to be identical, that is, calling The LLM says
this is also what we agreed upon, so why not just do that |
|
In general, it would be much preferable if you talk to us, and not let your bot talk to us. |
see also #298 |
|
I see I'm hitting some nerves here - my apologies. To be fair, it shouldn't be me who is looking into ways to simplify and speed up our systems, but here we are. It's impossible for anyone to run METIS_Simulation locally and generate the necessary data for testing recipes and not waste a day waiting for 95% of the computational effort to be thrown away because it wasn't needed in the first place. I don't have that kind of time to waste, so you'll have to excuse me using the toaster to save time. If I'm doing this as a last resort. I keep trying to give my job to someone else so I can actually get back to coding like a you guys, but no-one wants it ...
Here's the background - https://github.com/AstarVienna/METIS_Simulations/blob/main/YAML/ESO/detlinIFU.yaml To generate realistic noise that changes over multiple frames for the Dark and Linearity recipes of the METIS pipeline, the driving yaml file currently runs 32 full simulations from scratch. Incredibly wasteful when we have the .readout method on the Detector object to save exactly this from happening. However on instantiation, the noise Effects overwrite their "!SIM.random.seed" reference string in the meta dict, and so you can't just update the bang-string between each Detector.readout call. METIS_Simulations would need a hack to update metis[<effect_name>].meta["random_seed"] which is even more undesireable than this update. This minor update fixes this problem so that METIS_Simulations can make use of the Detector.readout funcitonality properly. |
|
And on your comment here - It also says:
So re SeesSequencers, yes, please do! I don't care who does it, it is a very sound solution. This tiny fix hurts no-one and allows me to actually do what I need to get done |
|
What's wrong with just setting the seed to |
|
Thank you for the context @astronomyk, that is exactly what was missing and that would be impossible to derive from the LLM description, while I would consider it the most important information. Sorry for being rude. I wasn't aware about the delivery this Monday, sorry, that was my fault and I dropped the ball again (w.r.t. the archive). Back to this P.R. I think saving the seed is already a hack to ensure the randomness is 'correct'. I guess to ensure that each detector (for e.g. MICADO or METIS IFU) has a different seed, so they don't look all the same. Is that behavior kept? Edit: but it could be for some other reason, not sure, but it is probably deliberate. |
Then the simulations are not deterministic anymore, which is something we want for the delivery. |
|
I've just checked with See also #97 |
Ah, sure, that makes sense 👍 |
I think this would all improve by using to the |
My suggestion has always been to use the readout time as a random seed (or include that in the seed), and conceptually, calling But it is difficult to get all considerations into account; and I fear this P.R. will break something else. |
|
I'm happy to ask the Toaster for a suggestion for implementing the SeedSequencer, if we as a collective, are open to reading it. It's cheap to do so, so it may be worth doing simply to get inspiration.... |
What
Two noise effects resolved
!SIM.random.seedin a way that destroys the reference on first use:ShotNoise.apply_towrote the resolved integer back intoself.meta["random_seed"];PoorMansHxRGReadoutNoise.apply_todid the same, and additionally rebaked its wholeself.metathroughfrom_currsys.After the first readout, the effect's meta holds a frozen integer instead of
"!SIM.random.seed", so any later readout from a reusedOpticalTrainre-seeds with the first exposure's seed, regardless of what!SIM.random.seedwas updated to incmds.This PR resolves into locals instead — exactly the pattern
BasicReadoutNoiseandPixelResponseNonUniformityalready use. First-call behaviour, resolution order and generated values are unchanged; only repeatedapply_tocalls now honour a per-readout seed.Why it matters
readout()deliberately supports repeated calls (it deep-copies results so "subsequent readouts" don't clobber earlier ones —optical_train.py), and repeated readout from oneobserve()is the natural way to generate N darks or a detector-linearity DIT ramp: measured on METIS, ~1–2 s per additional frame instead of ~60 s for a full rebuild+observe.Today that pattern is statistically correct but impossible to make reproducible: readouts 2..N carry the frame-1 readout-noise pattern rolled to random offsets (measured: σ agrees with independent simulation to 4×10⁻⁷, but ~92% of pixels differ and reruns don't reproduce). With this fix, one-observe/N-readouts at per-frame seeds is bit-for-bit identical to N independent simulations.
Validation (METIS_Simulations differential harness, per-pixel comparison)
darkLM.yaml×3 frames, patched vs unpatched mainNotes:
np.random.seed(process-global, legacy RNG) is untouched here — moving to per-effectGenerators spawned from aSeedSequencewould be the fuller modernisation, but is a behaviour-changing refactor best done separately. This PR is the minimal fix that makes repeated readout deterministic.METIS_Simulations_Deuxfindings F-D18/F-D24/F-D34.🤖 Generated with Claude Code