Fix #86: Correct bounds/initial_guess passing in 5 wrapped algorithms#142
Open
Devguru-codes wants to merge 1 commit intoOSIPI:mainfrom
Open
Fix #86: Correct bounds/initial_guess passing in 5 wrapped algorithms#142Devguru-codes wants to merge 1 commit intoOSIPI:mainfrom
Devguru-codes wants to merge 1 commit intoOSIPI:mainfrom
Conversation
…ithms
Two classes of bugs caused wrapped algorithms to crash or produce nonsensical results on real-world data:
1. PV_MUMC_biexp.py (UnboundLocalError): ivim_fit() had an if/else where the local �ounds variable was only assigned in the else branch. When OsipiBase provided default bounds (always, via force_default_settings), the local variable was undefined, causing an immediate crash. Fixed by always building �ounds from self.bounds dict.
2. IAR_LU_biexp, IAR_LU_segmented_2step, IAR_LU_segmented_3step, IAR_LU_subtracted (dict-vs-list mismatch): When bvalues were provided at __init__ time, these wrappers passed self.bounds (a Python dict) directly to the underlying dipy IvimModel constructors. Those models call np.array([bounds[0], bounds[1]]) internally — indexing a dict by 0 and 1 returns the first two *keys* ('S0', 'f'), not the numeric lower/upper bound arrays. This silently produced garbage fitting constraints. Fixed by pre-converting the dicts to [lower_list, upper_list] format before passing.
Also adds test_init_bvalues() regression test to prevent future regressions of both bug classes.
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.
Fix #86: Correct bounds/initial_guess passing in 5 wrapped algorithms
Description
This PR addresses Issue #86, where several wrapped IVIM algorithms crashed or returned mathematically incorrect results when processing real-world MRI data (which, unlike simulated test data, heavily tests boundary validation and fallback paths).
Root Causes & Bug Fixes
During my investigation of the algorithms acting irregularly on real data, I discovered two distinct classes of Python bugs in the wrappers:
PV_MUMC_biexp.py —
UnboundLocalErrorCrashif/elseblock where the local bounds variable was only assigned in theelsebranch. UnderOsipiBase,self.boundsdefaults to a Python dictionary, meaning the code executed the if branch, never created the bounds variable, and crashed.self.boundsdictionary values into the ([S0_min, D_min...], [S0_max, D_max...]) array formats expected by the underlying fit_least_squares algorithm.4
IAR_LU_*Algorithms — Invalid Constraints (Dict vs List-of-Lists)self.boundsPython dictionary directly to the dipyIvimModel*constructors. Internally, DIPY expects arrays and indexes them viabounds[0]andbounds[1]. Indexing a dictionary by integer yields its keys ("S0","f"), completely breaking the optimizer constraints and silently producing garbage results.[[lower...], [upper...]]array structure whenever theIAR_algorithmis initialized.Edge Case Hardening Added
To ensure these algorithms robustly handle diverse real-world data and usage patterns, I added several layers of error handling and edge-case protection:
D_and_Ds_swap: Added the missing parameter order swap check to IAR_LU_segmented_2step.bvalues=NoneGuards: All 5 algorithms now properly fall back toself.bvalues, raising a clear error if neither is available.scipyoptimization calls in robusttry/exceptblocks to print a warning and return default initial guesses (fail gracefully) if max evaluations are exceeded or optimization fails.Testing & Validation
osipi_fitcrash path, result key validity, and the stale-model guard.1148 passed, 0 failures(Verified manually through full suite execution natively). Note:TCML_TechnionIIT_lsqBOBYQAbounds x-fails are pre-existing, tolerated algorithm limitations (strict=False) and are completely unrelated to these fixes.