research: noise recovery notebooks with physical ground truth#249
Merged
KedoKudo merged 12 commits intofeature/172-2d-imagingfrom Feb 26, 2026
Merged
research: noise recovery notebooks with physical ground truth#249KedoKudo merged 12 commits intofeature/172-2d-imagingfrom
KedoKudo merged 12 commits intofeature/172-2d-imagingfrom
Conversation
Add denoise_nmf parameter to PhysicsRecovery.recover_image() and analyze_imaging() that applies NMF low-rank denoising before per-pixel NNLS recovery. This exploits spatial redundancy across all pixels to filter spectral noise while preserving resonance structure, combining NMF's noise robustness with NNLS's physical accuracy from SAMMY reference spectra. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ill timed-out slots Two bugs in _collect_results_with_timeout: 1. The stall_rounds bailout cancelled all pending futures after 2 timeout windows without progress, even when no futures had started running yet. During slow ProcessPoolExecutor startup, this killed pixels that were never attempted. Fix: only increment stall_rounds when at least one future is in running_start_times. 2. Timed-out futures were removed from pending but submit_fn() was never called to replace the freed slot. With bounded submission, each timeout permanently shrank the in-flight window, causing remaining pixels to never be submitted. Fix: call submit_fn() after each timed-out future, mirroring the existing refill logic for completed futures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lock queue When all running futures time out, their worker processes keep running (ProcessPoolExecutor cannot kill in-flight tasks). Replacement futures stay queued forever because no worker slot is available. The previous fix (only advance stall_rounds when futures are currently running) prevented this case from ever bailing out, causing fit_pixels() to hang. Fix: track whether any worker has ever been observed running. Advance the stall counter when workers_have_started is True even if no pending future is currently running — this correctly identifies zombie-occupied pools while still protecting against spurious cancellation during genuine slow pool startup (where workers_have_started remains False). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NB0 (pleiades_noise_recovery_research.ipynb): Documents progressive failure of NNLS, SG denoising, blind NMF, and Semi-NMF at low photon counts. Establishes binning sweep, SAMMY strided fitting, and representative pixel benchmarks as the testing framework. NB1 (pleiades_noise_recovery_nb1_transmission_domain.ipynb): Tests fitting in transmission domain to avoid log-clipping catastrophe, plus TV spatial regularization. Key findings: - T-domain+TV(w=0.1) achieves MAE 0.108 at n=2 (30% better than raw NNLS) - T-domain has systematic bias vs NNLS on clean data (MAE 0.036) - Iterative alternating provides no benefit (converges in 1 iteration) - At n=2, per-pixel information is fundamentally inadequate; methods that borrow across similar spatial regions are needed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…PM visualization
Add configurable vary flags (normalization, background, TZERO, thickness)
to ImagingConfig and InpDatasetMetadata, allowing users to fix nuisance
parameters for clean synthetic data while keeping them free for real data.
Defaults are True (vary all) for backwards compatibility.
Add units parameter ("fraction", "percent", "ppm") to all
AbundanceMapVisualizer plot methods, enabling instrument scientists to
view composition maps in parts-per-million as requested by VENUS team.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onclusions Switch all composition metrics from fractional (0-1) to ppm scale. Normalize SAMMY validation tables. Add verified conclusions based on actual notebook output: T-domain raw is the clear win for visualization (19-43% at n=10-50), TV regularization is questionable due to blurring, and SAMMY is robust to noise — preprocessing hurts characterization by erasing resonance fine structure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NB2 tests Gaussian, Non-Local Means, and Wavelet BayesShrink spatial denoising on the raw transmission cube before T-domain fitting. All three methods reduce composition map MAE by 27-29% at n=2. NLM wins composition maps (59K ppm at n=10, +27%), Wavelet wins SAMMY fidelity (mean |δ|=116K ppm). T-domain reconstruction universally hurts SAMMY regardless of prior denoising — confirmed across all experiments. Adds pywavelets dependency required by skimage wavelet denoising. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3 research notebooks (NB0-baseline, NB1-tdomain, NB2-spatial) with 2 consolidated notebooks using correct physical ground truth from sample metadata instead of NNLS-on-clean reference. NB0 (ground truth): Derives truth from calculate_number_density(), discovers 4 spectral classes, quantifies NNLS normalization artifact (~250K ppm crosstalk), characterizes clipping catastrophe, and maps the accuracy-resolution trade-off via binning sweep. NB1 (methods): Compares T-domain fitting, spatial denoising (NLM, wavelet, Gaussian), NMF neighborhood prototype, and hierarchical subdivision against physical truth. Includes honest SAMMY assessment. Key finding: blind NMF does not beat informed NNLS; spatial denoising helps ~10-15% at moderate noise; hierarchical bin=2 is optimal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tightened all observation and conclusion cells against executed output: - NB0 §3: precise crosstalk range (63-218K ppm) - NB0 §5: per-class gate check numbers, model bias range (100-335K ppm) - NB0 §6: binning only helps at n=5-10, not n>=50 - NB1 §2: exact spatial denoising improvement percentages - NB1 §7: re-ranked methods table by n=10 avg MAE (Wavelet 1st, Hier 2nd) - NB1 recap: updated to match verified NB0 findings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
Author
Note: Poisson noise should be added to counts, not to transmissionThe noise simulation in these notebooks adds Poisson noise directly to the transmission value T: sigma = np.sqrt(T / n_incident)
T_noisy = T + noise # or similarThis is physically incorrect. Transmission is not the Poisson random variable — raw counts are. The correct simulation is: counts = rng.poisson(n_incident * T_true) # counts ~ Poisson(I₀ · T)
T_noisy = counts / n_incident # normalize back to [0, 1]
sigma = np.sqrt(np.maximum(counts, 1)) / n_incident # propagated uncertaintyWhy it matters:
The distinction also matters for fitting: the correct For reference, NEREIDS uses the count-domain simulation for its noise sensitivity analysis in |
Address PR #249 comment: the noise simulation already uses the physically correct count-domain approach (DataDegrader generates Poisson counts then normalizes to T), but the notebook text was misleading. Changes: - NB0 §4: explicitly document count-domain Poisson model in code comments - NB0 §4: reframe "clipping catastrophe" as "boundary values" — T=0 and T=1 are real Poisson outcomes, not simulation artifacts - NB0: fix column headers and variable names (unphysical → boundary) - NB1: update compute_poisson_sigma docstring to show equivalence: σ_T = √(counts)/I₀ = √(T_obs/I₀) - Both: add DataDegrader source reference in code comments Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Key findings for March 4th presentation:
Test plan
pixi run jupyter nbconvert --execute --to notebook examples/Notebooks/pleiades_noise_recovery_nb0_ground_truth.ipynbruns without errorpixi run jupyter nbconvert --execute --to notebook examples/Notebooks/pleiades_noise_recovery_nb1_methods.ipynbruns without error_debug_images/NB0_*.pngandNB1_*.pngfor reasonable plots🤖 Generated with Claude Code