diff --git a/src/original/IAR_LundUniversity/ivim_fit_method_modified_mix.py b/src/original/IAR_LundUniversity/ivim_fit_method_modified_mix.py index 1dde9a08..2a383c9c 100644 --- a/src/original/IAR_LundUniversity/ivim_fit_method_modified_mix.py +++ b/src/original/IAR_LundUniversity/ivim_fit_method_modified_mix.py @@ -86,9 +86,9 @@ def __init__(self, gtab, bounds=None, maxiter=10, xtol=1e-8, rescale_units=False (bounds[0][1]*1000, bounds[1][1]*1000), \ (bounds[0][2]*1000, bounds[1][2]*1000)]) else: # Finally, if units if µm2/ms are already used - self.bounds = np.array([(bounds[0][0], bounds[1][0], \ + self.bounds = np.array([(bounds[0][0], bounds[1][0]), \ (bounds[0][1], bounds[1][1]), \ - (bounds[0][2], bounds[1][2]))]) + (bounds[0][2], bounds[1][2])]) @multi_voxel_fit def fit(self, data, bounds_de=None): diff --git a/src/original/IAR_LundUniversity/ivim_fit_method_modified_topopro.py b/src/original/IAR_LundUniversity/ivim_fit_method_modified_topopro.py index 7e0816d1..460227e3 100644 --- a/src/original/IAR_LundUniversity/ivim_fit_method_modified_topopro.py +++ b/src/original/IAR_LundUniversity/ivim_fit_method_modified_topopro.py @@ -84,9 +84,9 @@ def __init__(self, gtab, bounds=[[0, 0.005, 1e-5], [1, 0.1, 0.004]], \ (bounds[0][1]*1000, bounds[1][1]*1000), \ (bounds[0][2]*1000, bounds[1][2]*1000)]) else: # Finally, if units if µm2/ms are already used - self.bounds = np.array([(bounds[0][0], bounds[1][0], \ + self.bounds = np.array([(bounds[0][0], bounds[1][0]), \ (bounds[0][1], bounds[1][1]), \ - (bounds[0][2], bounds[1][2]))]) + (bounds[0][2], bounds[1][2])]) @multi_voxel_fit def fit(self, data): diff --git a/src/standardized/ASD_MemorialSloanKettering_QAMPER_IVIM.py b/src/standardized/ASD_MemorialSloanKettering_QAMPER_IVIM.py index 81244445..5e67c263 100644 --- a/src/standardized/ASD_MemorialSloanKettering_QAMPER_IVIM.py +++ b/src/standardized/ASD_MemorialSloanKettering_QAMPER_IVIM.py @@ -64,16 +64,17 @@ def algorithm(self,dwi_arr, bval_arr, LB0, UB0, x0in): return D_arr/1000, f_arr, Dx_arr/1000, s0_arr def initialize(self, bounds, initial_guess): - if bounds is None: + if self.bounds is None: print('warning, no bounds were defined, so algorithm-specific default bounds are used') self.bounds=([1e-6, 0, 0.004, 0],[0.003, 1.0, 0.2, 5]) else: - self.bounds=bounds - if initial_guess is None: + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) + if self.initial_guess is None: print('warning, no initial guesses were defined, so algorithm-specific default initial guess is used') self.initial_guess = [0.001, 0.2, 0.01, 1] else: - self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.use_initial_guess = True self.use_bounds = True diff --git a/src/standardized/IAR_LU_biexp.py b/src/standardized/IAR_LU_biexp.py index ff54311c..116085a9 100644 --- a/src/standardized/IAR_LU_biexp.py +++ b/src/standardized/IAR_LU_biexp.py @@ -45,10 +45,19 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_biexp, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + if initial_guess is None: + self.use_initial_guess = False + # Check the inputs + # Adapt the standardized bounds to the format of the specific algorithm + self.bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]], + [self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]] + # Adapt the standardized initial guess to the format of the specific algorithm + self.initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]] + # Initialize the algorithm if self.bvalues is not None: bvec = np.zeros((self.bvalues.size, 3)) diff --git a/src/standardized/IAR_LU_modified_mix.py b/src/standardized/IAR_LU_modified_mix.py index 87898c5c..f2191360 100644 --- a/src/standardized/IAR_LU_modified_mix.py +++ b/src/standardized/IAR_LU_modified_mix.py @@ -46,21 +46,26 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_modified_mix, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + self.use_initial_guess = False # This algorithm does not use initial guesses # Additional options self.stochastic = True # Check the inputs - + # Adapt the standardized bounds to the format of the specific algorithm + self.bounds = [[self.bounds["f"][0], self.bounds["Dp"][0]*1000, self.bounds["D"][0]*1000], + [self.bounds["f"][1], self.bounds["Dp"][1]*1000, self.bounds["D"][1]*1000]] + #self.bounds = [[bounds["f"][0], bounds["f"][1]], [bounds["Dp"][0]*1000, bounds["Dp"][1]*1000], [bounds["D"][0]*1000, bounds["D"][1]*1000]] + # Initialize the algorithm if self.bvalues is not None: bvec = np.zeros((self.bvalues.size, 3)) bvec[:,2] = 1 gtab = gradient_table(self.bvalues, bvec, b0_threshold=0) - self.IAR_algorithm = IvimModelVP(gtab, bounds=self.bounds, rescale_results_to_mm2_s=True) + self.IAR_algorithm = IvimModelVP(gtab, bounds=self.bounds, rescale_units=False, rescale_results_to_mm2_s=True) else: self.IAR_algorithm = None diff --git a/src/standardized/IAR_LU_modified_topopro.py b/src/standardized/IAR_LU_modified_topopro.py index a9cc7ed8..731801db 100644 --- a/src/standardized/IAR_LU_modified_topopro.py +++ b/src/standardized/IAR_LU_modified_topopro.py @@ -45,8 +45,20 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_modified_topopro, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + self.use_initial_guess = False # This algorithm does not use initial guesses + + # Additional options + self.stochastic = True + + # Check the inputs + # Adapt the standardized bounds to the format of the specific algorithm + if self.bounds["Dp"][0] == self.bounds["D"][1]: + print('warning, bounds for D* and D are equal, this will likely cause fitting errors. Setting D_upper to 99 percent of D_upper') + self.bounds["D"][1] = self.bounds["D"][1]*0.99 + self.bounds = [[self.bounds["f"][0], self.bounds["Dp"][0]*1000, self.bounds["D"][0]*1000], + [self.bounds["f"][1], self.bounds["Dp"][1]*1000, self.bounds["D"][1]*1000]] # Check the inputs # Initialize the algorithm diff --git a/src/standardized/IAR_LU_segmented_2step.py b/src/standardized/IAR_LU_segmented_2step.py index c5be9b45..30be3a17 100644 --- a/src/standardized/IAR_LU_segmented_2step.py +++ b/src/standardized/IAR_LU_segmented_2step.py @@ -45,16 +45,25 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_segmented_2step, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + if initial_guess is None: + self.use_initial_guess = False # Check the inputs + + # Adapt the bounds to the format needed for the algorithm + self.bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]], \ + [self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]] + + # Adapt the initial guess to the format needed for the algorithm + self.initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]] # Initialize the algorithm if self.bvalues is not None: bvec = np.zeros((self.bvalues.size, 3)) bvec[:,2] = 1 gtab = gradient_table(self.bvalues, bvec, b0_threshold=0) - + self.IAR_algorithm = IvimModelSegmented2Step(gtab, bounds=self.bounds, initial_guess=self.initial_guess, b_threshold=self.thresholds) else: self.IAR_algorithm = None diff --git a/src/standardized/IAR_LU_segmented_3step.py b/src/standardized/IAR_LU_segmented_3step.py index 73aeb42b..84f4790e 100644 --- a/src/standardized/IAR_LU_segmented_3step.py +++ b/src/standardized/IAR_LU_segmented_3step.py @@ -45,9 +45,18 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_segmented_3step, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + if initial_guess is None: + self.use_initial_guess = False # Check the inputs + + # Adapt the bounds to the format needed for the algorithm + self.bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]], \ + [self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]] + + # Adapt the initial guess to the format needed for the algorithm + self.initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]] # Initialize the algorithm if self.bvalues is not None: diff --git a/src/standardized/IAR_LU_subtracted.py b/src/standardized/IAR_LU_subtracted.py index abd38d63..b4da98d9 100644 --- a/src/standardized/IAR_LU_subtracted.py +++ b/src/standardized/IAR_LU_subtracted.py @@ -45,9 +45,18 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non super(IAR_LU_subtracted, self).__init__(bvalues, thresholds, bounds, initial_guess) if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False - self.use_initial_guess = False + if bounds is None: + self.use_bounds = False + if initial_guess is None: + self.use_initial_guess = False # Check the inputs + + # Adapt the bounds to the format needed for the algorithm + self.bounds = [[self.bounds["S0"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["D"][0]], \ + [self.bounds["S0"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["D"][1]]] + + # Adapt the initial guess to the format needed for the algorithm + self.initial_guess = [self.initial_guess["S0"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["D"]] # Initialize the algorithm if self.bvalues is not None: diff --git a/src/standardized/IVIM_NEToptim.py b/src/standardized/IVIM_NEToptim.py index ca109721..13cf6433 100644 --- a/src/standardized/IVIM_NEToptim.py +++ b/src/standardized/IVIM_NEToptim.py @@ -67,8 +67,9 @@ def initialize(self, bounds, initial_guess, fitS0, traindata, SNR, n): self.training_data(self.bvalues,n=n,SNR=SNR) self.arg=Arg() if bounds is not None: - self.arg.net_pars.cons_min = bounds[0] # Dt, Fp, Ds, S0 - self.arg.net_pars.cons_max = bounds[1] # Dt, Fp, Ds, S0 + self.arg.net_pars.cons_min = np.array([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]])#bounds[0] # Dt, Fp, Ds, S0 + self.arg.net_pars.cons_max = np.array([self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]])#bounds[1] # Dt, Fp, Ds, S0 + self.use_bounds = True if traindata is None: self.net = deep.learn_IVIM(self.train_data['data'], self.bvalues, self.arg) else: diff --git a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py index 0a94dd27..1db05bd1 100644 --- a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py @@ -63,12 +63,13 @@ def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None, print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') self.initial_guess = [0.001, 0.001, 0.01, 1] else: - self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.use_bounds = True if thresholds is None: diff --git a/src/standardized/OGC_AmsterdamUMC_biexp.py b/src/standardized/OGC_AmsterdamUMC_biexp.py index 58f892de..d2c92fc7 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp.py @@ -54,12 +54,13 @@ def initialize(self, bounds, initial_guess, fitS0): print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') self.initial_guess = [0.001, 0.1, 0.01, 1] else: - self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.fitS0=fitS0 self.use_initial_guess = True diff --git a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py index aebf8224..5d92a14d 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py @@ -53,12 +53,13 @@ def initialize(self, bounds, initial_guess, thresholds): print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') self.initial_guess = [0.001, 0.001, 0.01, 1] else: - self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.use_bounds = True if thresholds is None: diff --git a/src/standardized/OJ_GU_bayesMATLAB.py b/src/standardized/OJ_GU_bayesMATLAB.py index c9c008e3..19da2d21 100644 --- a/src/standardized/OJ_GU_bayesMATLAB.py +++ b/src/standardized/OJ_GU_bayesMATLAB.py @@ -68,12 +68,13 @@ def initialize(self, bounds,initial_guess): print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') self.bounds=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') self.initial_guess = [0.001, 0.1, 0.01, 1] else: - self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.use_initial_guess = True self.use_bounds = True diff --git a/src/standardized/OJ_GU_segMATLAB.py b/src/standardized/OJ_GU_segMATLAB.py index c48a231a..976a387d 100644 --- a/src/standardized/OJ_GU_segMATLAB.py +++ b/src/standardized/OJ_GU_segMATLAB.py @@ -67,7 +67,8 @@ def initialize(self, bounds, thresholds): print('warning, no bounds were defined, so algorithm-specific default bounds are used') self.bounds=([1e-6, 0, 0.003, 0],[0.003, 1.0, 0.2, 5]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) self.use_bounds = True if thresholds is None: self.thresholds = 200 diff --git a/src/standardized/PV_MUMC_biexp.py b/src/standardized/PV_MUMC_biexp.py index 21e2d4a7..c223681e 100644 --- a/src/standardized/PV_MUMC_biexp.py +++ b/src/standardized/PV_MUMC_biexp.py @@ -41,7 +41,11 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non self.PV_algorithm = fit_least_squares if bounds is not None: print('warning, bounds from wrapper are not (yet) used in this algorithm') - self.use_bounds = False + + self.bounds = ([self.bounds["S0"][0], self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0]], + [self.bounds["S0"][1], self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1]]) + + self.use_bounds = True self.use_initial_guess = False diff --git a/src/standardized/TCML_TechnionIIT_SLS.py b/src/standardized/TCML_TechnionIIT_SLS.py index 7e9cc590..c99e1f4f 100644 --- a/src/standardized/TCML_TechnionIIT_SLS.py +++ b/src/standardized/TCML_TechnionIIT_SLS.py @@ -51,10 +51,12 @@ def initialize(self, bounds, fitS0,thresholds): self.bounds = ([0.0003, 0.001, 0.009, 0],[0.008, 0.5,0.04, 3]) else: print('warning, although bounds are given, only D* is bounded)') - bounds=bounds - self.bounds = bounds + #bounds=bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) self.fitS0=fitS0 - self.use_bounds = False + self.use_bounds = True if thresholds is None: self.thresholds = 150 print('warning, no thresholds were defined, so default bounds are used of 150') diff --git a/src/standardized/TCML_TechnionIIT_lsqBOBYQA.py b/src/standardized/TCML_TechnionIIT_lsqBOBYQA.py index 0bfbcd89..724fe719 100644 --- a/src/standardized/TCML_TechnionIIT_lsqBOBYQA.py +++ b/src/standardized/TCML_TechnionIIT_lsqBOBYQA.py @@ -51,13 +51,16 @@ def initialize(self, bounds, initial_guess, fitS0): print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') self.bounds = ([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) else: - bounds=bounds - self.bounds = bounds + #bounds=bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') self.initial_guess = [0.001, 0.1, 0.02, 1] # D, Dp, f, S0 else: - self.initial_guess = initial_guess + #self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.fitS0=fitS0 self.use_initial_guess = True diff --git a/src/standardized/TCML_TechnionIIT_lsq_sls_BOBYQA.py b/src/standardized/TCML_TechnionIIT_lsq_sls_BOBYQA.py index e444dd06..f9d2b6af 100644 --- a/src/standardized/TCML_TechnionIIT_lsq_sls_BOBYQA.py +++ b/src/standardized/TCML_TechnionIIT_lsq_sls_BOBYQA.py @@ -51,15 +51,17 @@ def initialize(self, bounds, fitS0, thresholds): print('warning, no bounds were defined, so default bounds are used of ([0.0003, 0.001, 0.009, 0],[0.008, 1.0,0.04, 3])') self.bounds = ([0.0003, 0.001, 0.009, 0],[0.008, 1.0 ,0.04, 3]) else: - bounds=bounds - self.bounds = bounds + #bounds=bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if thresholds is None: self.thresholds = 200 print('warning, no thresholds were defined, so default bounds are used of 200') else: self.thresholds = thresholds self.fitS0=fitS0 - self.use_bounds = False + self.use_bounds = True self.use_initial_guess = False def ivim_fit(self, signals, bvalues, **kwargs): diff --git a/src/standardized/TCML_TechnionIIT_lsq_sls_lm.py b/src/standardized/TCML_TechnionIIT_lsq_sls_lm.py index d6621c51..b48e0daf 100644 --- a/src/standardized/TCML_TechnionIIT_lsq_sls_lm.py +++ b/src/standardized/TCML_TechnionIIT_lsq_sls_lm.py @@ -52,15 +52,17 @@ def initialize(self, bounds, fitS0,thresholds): 'warning, no bounds were defined, so default bounds are used of ([0.0003, 0.001, 0.009, 0],[0.008, 0.5,0.04, 3])') self.bounds = ([0.0003, 0.001, 0.009, 0], [0.008, 0.5, 0.04, 3]) else: - bounds = bounds - self.bounds = bounds + #bounds = bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if thresholds is None: self.thresholds = 150 print('warning, no thresholds were defined, so default bounds are used of 150') else: self.thresholds = thresholds self.fitS0=fitS0 - self.use_bounds = False + self.use_bounds = True self.use_initial_guess = False def ivim_fit(self, signals, bvalues, **kwargs): diff --git a/src/standardized/TCML_TechnionIIT_lsq_sls_trf.py b/src/standardized/TCML_TechnionIIT_lsq_sls_trf.py index 1d2fe6a5..cb44e779 100644 --- a/src/standardized/TCML_TechnionIIT_lsq_sls_trf.py +++ b/src/standardized/TCML_TechnionIIT_lsq_sls_trf.py @@ -51,15 +51,17 @@ def initialize(self, bounds, fitS0, thresholds): print('warning, no bounds were defined, so default bounds are used of ([0.0003, 0.001, 0.009, 0],[0.008, 1.0,0.04, 3])') self.bounds = ([0.0003, 0.001, 0.009, 0],[0.008, 1.0,0.04, 3]) else: - bounds=bounds - self.bounds = bounds + #bounds=bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if thresholds is None: self.thresholds = 200 print('warning, no thresholds were defined, so default bounds are used of 200') else: self.thresholds = thresholds self.fitS0=fitS0 - self.use_bounds = False + self.use_bounds = True self.use_initial_guess = False def ivim_fit(self, signals, bvalues, **kwargs): diff --git a/src/standardized/TCML_TechnionIIT_lsqlm.py b/src/standardized/TCML_TechnionIIT_lsqlm.py index b4d4a642..fbcabae5 100644 --- a/src/standardized/TCML_TechnionIIT_lsqlm.py +++ b/src/standardized/TCML_TechnionIIT_lsqlm.py @@ -51,7 +51,8 @@ def initialize(self, bounds, initial_guess, fitS0): print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.1, 0.01, 1]') self.initial_guess = [0.001, 0.1, 0.01, 1] else: - self.initial_guess = initial_guess + #self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.fitS0=fitS0 self.use_initial_guess = True diff --git a/src/standardized/TCML_TechnionIIT_lsqtrf.py b/src/standardized/TCML_TechnionIIT_lsqtrf.py index b0740cce..ae1c43c0 100644 --- a/src/standardized/TCML_TechnionIIT_lsqtrf.py +++ b/src/standardized/TCML_TechnionIIT_lsqtrf.py @@ -51,13 +51,16 @@ def initialize(self, bounds, initial_guess, fitS0): print('warning, no bounds were defined, so default bounds are used of ([0.0003, 0.001, 0.009, 0],[0.008, 0.5,0.04, 3])') self.bounds = ([0.0003, 0.001, 0.009, 0],[0.008, 0.5,0.04, 3]) else: - bounds=bounds - self.bounds = bounds + #bounds=bounds + #self.bounds = bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) if initial_guess is None: print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.1, 0.01, 1]') self.initial_guess = [0.001, 0.1, 0.01, 1] # D, Dp, f, S0 else: - self.initial_guess = initial_guess + #self.initial_guess = initial_guess + self.initial_guess = [self.initial_guess["D"], self.initial_guess["f"], self.initial_guess["Dp"], self.initial_guess["S0"]] self.use_initial_guess = True self.fitS0=fitS0 self.use_initial_guess = True diff --git a/src/standardized/TF_reference_IVIMfit.py b/src/standardized/TF_reference_IVIMfit.py index 6bf355fc..e39ee8e6 100644 --- a/src/standardized/TF_reference_IVIMfit.py +++ b/src/standardized/TF_reference_IVIMfit.py @@ -51,7 +51,8 @@ def initialize(self, bounds, thresholds): print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005],[0.005, 1.0, 0.2]') self.bounds=([0, 0, 0.005, 0.8],[0.005, 1.0, 0.2, 1.2]) else: - self.bounds=bounds + self.bounds = ([self.bounds["D"][0], self.bounds["f"][0], self.bounds["Dp"][0], self.bounds["S0"][0]], + [self.bounds["D"][1], self.bounds["f"][1], self.bounds["Dp"][1], self.bounds["S0"][1]]) self.use_bounds = True if thresholds is None: self.thresholds = 200 diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index 2ad0c30a..285b0444 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -25,14 +25,24 @@ class OsipiBase: Diffusion b-values (s/mm²) matching the last dimension of the input data. thresholds : array-like, optional Thresholds used by specific algorithms (e.g., signal cutoffs). - bounds : array-like, optional - Parameter bounds for constrained optimization. - initial_guess : array-like, optional - Initial parameter estimates for the IVIM fit. + bounds : dict, optional + Parameter bounds for constrained optimization. Should be a dict with keys + like "S0", "f", "Dp", "D" and values as [lower, upper] lists or arrays. + E.g. {"S0" : [0.7, 1.3], "f" : [0, 1], "Dp" : [0.005, 0.2], "D" : [0, 0.005]}. + initial_guess : dict, optional + Initial parameter estimates for the IVIM fit. Should be a dict with keys + like "S0", "f", "Dp", "D" and float values. + E.g. {"S0" : 1, "f" : 0.1, "Dp" : 0.01, "D" : 0.001}. algorithm : str, optional Name of an algorithm module in ``src/standardized`` to load dynamically. If supplied, the instance is immediately converted to that algorithm’s subclass via :meth:`osipi_initiate_algorithm`. + force_default_settings : bool, optional + If bounds and initial guesses are not provided, the wrapper will set + them to reasonable physical ones, i.e. {"S0":[0.7, 1.3], "f":[0, 1], + "Dp":[0.005, 0.2], "D":[0, 0.005]}. + To prevent this, set this bool to False. Default initial guess + {"S0" : 1, "f": 0.1, "Dp": 0.01, "D": 0.001}. **kwargs Additional keyword arguments forwarded to the selected algorithm’s initializer if ``algorithm`` is provided. @@ -92,21 +102,38 @@ class OsipiBase: f_map = results["f"] """ - def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, **kwargs): + def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, force_default_settings=True, **kwargs): # Define the attributes as numpy arrays only if they are not None self.bvalues = np.asarray(bvalues) if bvalues is not None else None self.thresholds = np.asarray(thresholds) if thresholds is not None else None - self.bounds = np.asarray(bounds) if bounds is not None else None - self.initial_guess = np.asarray(initial_guess) if initial_guess is not None else None + self.bounds = bounds if bounds is not None else None + self.initial_guess = initial_guess if initial_guess is not None else None self.use_bounds = True self.use_initial_guess = True self.deep_learning = False self.supervised = False self.stochastic = False + + if force_default_settings: + if self.bounds is None: + print('warning, no bounds were defined, so default bounds are used of [0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]') + self.bounds = {"S0" : [0.7, 1.3], "f" : [0, 1.0], "Dp" : [0.005, 0.2], "D" : [0, 0.005]} # These are defined as [lower, upper] + self.forced_default_bounds = True + self.use_bounds = True + + if self.initial_guess is None: + print('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.001, 0.01, 1]') + self.initial_guess = {"S0" : 1, "f" : 0.1, "Dp" : 0.01, "D" : 0.001} + self.forced_default_initial_guess = True + self.use_initial_guess = True + + self.osipi_bounds = self.bounds # self.bounds will change form, store it in the osipi format here + self.osipi_initial_guess = self.initial_guess # self.initial_guess will change form, store it in the osipi format here + # If the user inputs an algorithm to OsipiBase, it is intereprete as initiating # an algorithm object with that name. if algorithm: - self.osipi_initiate_algorithm(algorithm, bvalues=bvalues, thresholds=thresholds, bounds=bounds, initial_guess=initial_guess, **kwargs) + self.osipi_initiate_algorithm(algorithm, bvalues=self.bvalues, thresholds=self.thresholds, bounds=self.bounds, initial_guess=self.initial_guess, **kwargs) def osipi_initiate_algorithm(self, algorithm, **kwargs): """Turns the class into a specified one by the input. diff --git a/tests/IVIMmodels/unit_tests/simple_test_run_of_algorithm.py b/tests/IVIMmodels/unit_tests/simple_test_run_of_algorithm.py index 0761284d..6df9cbbb 100644 --- a/tests/IVIMmodels/unit_tests/simple_test_run_of_algorithm.py +++ b/tests/IVIMmodels/unit_tests/simple_test_run_of_algorithm.py @@ -17,7 +17,7 @@ ## Simple test code... # Used to just do a test run of an algorithm during development def dev_test_run(model, **kwargs): - bvalues = np.array([0, 50, 100, 150, 200, 500, 800]) + bvalues = np.array([0, 50, 0, 100, 0, 150, 200, 500, 0, 800]) def ivim_model(b, S0=1, f=0.1, Dstar=0.01, D=0.001): return S0*(f*np.exp(-b*Dstar) + (1-f)*np.exp(-b*D)) @@ -29,14 +29,14 @@ def ivim_model(b, S0=1, f=0.1, Dstar=0.01, D=0.001): #model = ETP_SRI_LinearFitting(thresholds=[200]) if kwargs: - results = model.osipi_fit_full_volume(signals, bvalues, **kwargs) + results = model.osipi_fit(signals, bvalues, **kwargs) else: - results = model.osipi_fit_full_volume(signals, bvalues) + results = model.osipi_fit(signals, bvalues) print(results) #test = model.osipi_simple_bias_and_RMSE_test(SNR=20, bvalues=bvalues, f=0.1, Dstar=0.03, D=0.001, noise_realizations=10) #model1 = ETP_SRI_LinearFitting(thresholds=[200]) -model2 = IAR_LU_biexp(bounds=([0,0,0,0], [1,1,1,1])) +model2 = IAR_LU_biexp() #model2 = IAR_LU_modified_mix() #model2 = OGC_AmsterdamUMC_biexp() diff --git a/tests/IVIMmodels/unit_tests/test_ivim_fit.py b/tests/IVIMmodels/unit_tests/test_ivim_fit.py index ded61d36..c0ea9c03 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_fit.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_fit.py @@ -47,9 +47,8 @@ def test_ivim_fit_saved(data_ivim_fit_saved, eng, request, record_property): request.node.add_marker(mark) signal = signal_helper(data["data"]) tolerances = tolerances_helper(tolerances, data) - fit = OsipiBase(algorithm=algorithm, **kwargs) - if fit.use_bounds: - fit.bounds = ([0, 0, 0.005, 0.7], [0.005, 1.0, 0.2, 1.3]) + test_bounds = {"S0" : [0.7, 1.3], "f" : [0, 1.0], "Dp" : [0.005, 0.2], "D" : [0, 0.005]} + fit = OsipiBase(algorithm=algorithm, bounds=test_bounds, **kwargs) start_time = time.time() # Record the start time fit_result = fit.osipi_fit(signal, bvals) elapsed_time = time.time() - start_time # Calculate elapsed time @@ -94,20 +93,20 @@ def test_default_bounds_and_initial_guesses(algorithmlist,eng): #assert fit.bounds is not None, f"For {algorithm}, there is no default fit boundary" #assert fit.initial_guess is not None, f"For {algorithm}, there is no default fit initial guess" if fit.use_bounds: - assert 0 <= fit.bounds[0][0] <= 0.003, f"For {algorithm}, the default lower bound of D {fit.bounds[0][0]} is unrealistic" - assert 0 <= fit.bounds[1][0] <= 0.01, f"For {algorithm}, the default upper bound of D {fit.bounds[1][0]} is unrealistic" - assert 0 <= fit.bounds[0][1] <= 1, f"For {algorithm}, the default lower bound of f {fit.bounds[0][1]} is unrealistic" - assert 0 <= fit.bounds[1][1] <= 1, f"For {algorithm}, the default upper bound of f {fit.bounds[1][1]} is unrealistic" - assert 0.003 <= fit.bounds[0][2] <= 0.05, f"For {algorithm}, the default lower bound of Ds {fit.bounds[0][2]} is unrealistic" - assert 0.003 <= fit.bounds[1][2] <= 0.5, f"For {algorithm}, the default upper bound of Ds {fit.bounds[1][2]} is unrealistic" - assert 0 <= fit.bounds[0][3] <= 1, f"For {algorithm}, the default lower bound of S {fit.bounds[0][3]} is unrealistic; note data is normaized" - assert 1 <= fit.bounds[1][3] <= 1000, f"For {algorithm}, the default upper bound of S {fit.bounds[1][3]} is unrealistic; note data is normaized" - assert fit.bounds[1][0] <= fit.bounds[0][2], f"For {algorithm}, the default upper bound of D {fit.bounds[1][0]} is higher than lower bound of D* {fit.bounds[0][2]}" + assert 0 <= fit.osipi_bounds["D"][0] <= 0.003, f"For {algorithm}, the default lower bound of D {fit.osipi_bounds['D'][0]} is unrealistic" + assert 0 <= fit.osipi_bounds["D"][1] <= 0.01, f"For {algorithm}, the default upper bound of D {fit.osipi_bounds['D'][1]} is unrealistic" + assert 0 <= fit.osipi_bounds["f"][0] <= 1, f"For {algorithm}, the default lower bound of f {fit.osipi_bounds['f'][0]} is unrealistic" + assert 0 <= fit.osipi_bounds["f"][1] <= 1, f"For {algorithm}, the default upper bound of f {fit.osipi_bounds['f'][1]} is unrealistic" + assert 0.003 <= fit.osipi_bounds["Dp"][0] <= 0.05, f"For {algorithm}, the default lower bound of Dp {fit.osipi_bounds['Dp'][0]} is unrealistic" + assert 0.003 <= fit.osipi_bounds["Dp"][1] <= 0.5, f"For {algorithm}, the default upper bound of Dp {fit.osipi_bounds['Dp'][1]} is unrealistic" + assert 0 <= fit.osipi_bounds["S0"][0] <= 1, f"For {algorithm}, the default lower bound of S0 {fit.osipi_bounds['S0'][0]} is unrealistic; note data is normaized" + assert 1 <= fit.osipi_bounds["S0"][1] <= 1000, f"For {algorithm}, the default upper bound of S0 {fit.osipi_bounds['S0'][1]} is unrealistic; note data is normaized" + assert fit.osipi_bounds["D"][1] <= fit.osipi_bounds["Dp"][0], f"For {algorithm}, the default upper bound of D {fit.osipi_bounds['D'][1]} is higher than lower bound of Dp {fit.osipi_bounds['Dp'][0]}" if fit.use_initial_guess: - assert 0.0008 <= fit.initial_guess[0] <= 0.002, f"For {algorithm}, the default initial guess for D {fit.initial_guess[0]} is unrealistic" - assert 0 <= fit.initial_guess[1] <= 0.5, f"For {algorithm}, the default initial guess for f {fit.initial_guess[1]} is unrealistic" - assert 0.003 <= fit.initial_guess[2] <= 0.1, f"For {algorithm}, the default initial guess for Ds {fit.initial_guess[2]} is unrealistic" - assert 0.9 <= fit.initial_guess[3] <= 1.1, f"For {algorithm}, the default initial guess for S {fit.initial_guess[3]} is unrealistic; note signal is normalized" + assert 0.0008 <= fit.osipi_initial_guess["D"] <= 0.002, f"For {algorithm}, the default initial guess for D {fit.osipi_initial_guess['D']} is unrealistic" + assert 0 <= fit.osipi_initial_guess["f"] <= 0.5, f"For {algorithm}, the default initial guess for f {fit.osipi_initial_guess['f']} is unrealistic" + assert 0.003 <= fit.osipi_initial_guess["Dp"] <= 0.1, f"For {algorithm}, the default initial guess for Dp {fit.osipi_initial_guess['Dp']} is unrealistic" + assert 0.9 <= fit.osipi_initial_guess["S0"] <= 1.1, f"For {algorithm}, the default initial guess for S0 {fit.osipi_initial_guess['S0']} is unrealistic; note signal is normalized" def test_bounds(bound_input, eng): @@ -117,16 +116,17 @@ def test_bounds(bound_input, eng): pytest.skip(reason="Running without matlab; if Matlab is available please run pytest --withmatlab") else: kwargs = {**kwargs, 'eng': eng} - bounds = ([0.0008, 0.2, 0.01, 1.1], [0.0012, 0.3, 0.02, 1.3]) + #bounds = ([0.0008, 0.2, 0.01, 1.1], [0.0012, 0.3, 0.02, 1.3]) + bounds = {"S0" : [1.1, 1.3], "f" : [0.2, 0.3], "Dp" : [0.01, 0.02], "D" : [0.0008, 0.0012]} # deliberately have silly bounds to see whether they are used - fit = OsipiBase(algorithm=algorithm, bounds=bounds, initial_guess = [0.001, 0.25, 0.015, 1.2], **kwargs) + fit = OsipiBase(algorithm=algorithm, bounds=bounds, initial_guess={"S0" : 1.2, "f" : 0.25, "Dp" : 0.015, "D" : 0.001}, **kwargs) if fit.use_bounds: signal = signal_helper(data["data"]) fit_result = fit.osipi_fit(signal, bvals) - assert bounds[0][0] <= fit_result['D'] <= bounds[1][0], f"Result {fit_result['D']} out of bounds for data: {name}" - assert bounds[0][1] <= fit_result['f'] <= bounds[1][1], f"Result {fit_result['f']} out of bounds for data: {name}" - assert bounds[0][2] <= fit_result['Dp'] <= bounds[1][2], f"Result {fit_result['Dp']} out of bounds for data: {name}" + assert bounds["D"][0] <= fit_result['D'] <= bounds["D"][1], f"Result {fit_result['D']} out of bounds for data: {name}" + assert bounds["f"][0] <= fit_result['f'] <= bounds["f"][1], f"Result {fit_result['f']} out of bounds for data: {name}" + assert bounds["Dp"][0] <= fit_result['Dp'] <= bounds["Dp"][1], f"Result {fit_result['Dp']} out of bounds for data: {name}" # S0 is not returned as argument... #assert bounds[0][3] <= fit_result['S0'] <= bounds[1][3], f"Result {fit_result['S0']} out of bounds for data: {name}" '''if fit.use_initial_guess: