diff --git a/src/original/PV_MUMC/triexp_fitting_algorithms.py b/src/original/PV_MUMC/triexp_fitting_algorithms.py index 196757a5..c918e997 100644 --- a/src/original/PV_MUMC/triexp_fitting_algorithms.py +++ b/src/original/PV_MUMC/triexp_fitting_algorithms.py @@ -88,9 +88,9 @@ def monofit(bvalues, Dpar, Fp): Dpar1 = params[0] if IR: if not fitS0: - bounds = ([bounds[0][1] , bounds[0][2] , bounds[0][3] , bounds[0][4] , bounds[0][5] ], + newbounds = ([bounds[0][1] , bounds[0][2] , bounds[0][3] , bounds[0][4] , bounds[0][5] ], [Dpar1 , bounds[1][2] , bounds[1][3] , bounds[1][4] , bounds[1][5] ]) - params, _ = curve_fit(tri_expN_noS0_IR, bvalues, dw_data, p0=[Dpar1, 0.0, (bounds[0][3]+bounds[1][3])/2, 0.05, (bounds[0][5]+bounds[1][5])/2], bounds=bounds) + params, _ = curve_fit(tri_expN_noS0_IR, bvalues, dw_data, p0=[Dpar1, 0.0, (bounds[0][3]+bounds[1][3])/2, 0.05, (bounds[0][5]+bounds[1][5])/2], bounds=newbounds) Dpar, Fint, Dint, Fmv, Dmv = params[0], params[1], params[2], params[3], params[4] #when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN) @@ -104,9 +104,9 @@ def monofit(bvalues, Dpar, Fp): else: if not fitS0: - bounds = ([bounds[0][1] , bounds[0][2] , bounds[0][3] , bounds[0][4] , bounds[0][5] ], + newbounds = ([bounds[0][1] , bounds[0][2] , bounds[0][3] , bounds[0][4] , bounds[0][5] ], [Dpar1 , bounds[1][2] , bounds[1][3] , bounds[1][4] , bounds[1][5] ]) - params, _ = curve_fit(tri_expN_noS0, bvalues, dw_data, p0=[Dpar1, 0.0, (bounds[0][3]+bounds[1][3])/2, 0.05, (bounds[0][5]+bounds[1][5])/2], bounds=bounds) + params, _ = curve_fit(tri_expN_noS0, bvalues, dw_data, p0=[Dpar1, 0.0, (bounds[0][3]+bounds[1][3])/2, 0.05, (bounds[0][5]+bounds[1][5])/2], bounds=newbounds) Dpar, Fint, Dint, Fmv, Dmv = params[0], params[1], params[2], params[3], params[4] #when the fraction of a compartment equals zero (or very very small), the corresponding diffusivity is non-existing (=NaN) diff --git a/src/standardized wip/PV_MUMC_triexp.py b/src/standardized wip/PV_MUMC_triexp.py new file mode 100644 index 00000000..ec7f3fee --- /dev/null +++ b/src/standardized wip/PV_MUMC_triexp.py @@ -0,0 +1,53 @@ +import numpy as np +from src.wrappers.OsipiBase import OsipiBase +from src.original.PV_MUMC.triexp_fitting_algorithms import fit_least_squares_tri_exp, fit_NNLS + + +class PV_MUMC_triexp(OsipiBase): + """ + Tri-exponential least squares fitting algorithm by Paulien Voorter, Maastricht University + """ + + # Some basic stuff that identifies the algorithm + id_author = "Paulien Voorter MUMC" + id_algorithm_type = "Tri-exponential fit" + id_return_parameters = "Dpar, Fint, Dint, Fmv, Dmv" + id_units = "seconds per milli metre squared or milliseconds per micro metre squared" + + # Algorithm requirements + required_bvalues = 4 + required_thresholds = [0, 0] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds + required_bounds = False + required_bounds_optional = True # Bounds may not be required but are optional + required_initial_guess = False + required_initial_guess_optional = True + accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most? + + def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, weighting=None, stats=False): + """ + Everything this algorithm requires should be implemented here. + Number of segmentation thresholds, bounds, etc. + + Our OsipiBase object could contain functions that compare the inputs with + the requirements. + """ + super(PV_MUMC_triexp, self).__init__(bvalues, None, bounds, None) + self.PV_algorithm = fit_least_squares_tri_exp + + + def ivim_fit(self, signals, bvalues=None): + """Perform the IVIM fit + + Args: + signals (array-like) + bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None. + + Returns: + _type_: _description_ + """ + + + fit_results = self.PV_algorithm(bvalues, signals) + Dpar, Fint, Dint, Fmv, Dmv = fit_results + + return Dpar, Fint, Dint, Fmv, Dmv diff --git a/src/standardized/PV_MUMC_triexp.py b/src/standardized/PV_MUMC_triexp.py new file mode 100644 index 00000000..b86e87ba --- /dev/null +++ b/src/standardized/PV_MUMC_triexp.py @@ -0,0 +1,58 @@ +import numpy as np +from src.wrappers.OsipiBase import OsipiBase +from src.original.PV_MUMC.triexp_fitting_algorithms import fit_least_squares_tri_exp, fit_NNLS + + +class PV_MUMC_triexp(OsipiBase): + """ + Tri-exponential least squares fitting algorithm by Paulien Voorter, Maastricht University + """ + + # Some basic stuff that identifies the algorithm + id_author = "Paulien Voorter MUMC" + id_algorithm_type = "Tri-exponential fit" + id_return_parameters = "Dpar, Fint, Dint, Fmv, Dmv" + id_units = "seconds per milli metre squared or milliseconds per micro metre squared" + + # Algorithm requirements + required_bvalues = 4 + required_thresholds = [0, 0] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds + required_bounds = False + required_bounds_optional = True # Bounds may not be required but are optional + required_initial_guess = False + required_initial_guess_optional = True + result_keys = ["Dpar", "Fint", "Dint", "Fmv", "Dmv"] + accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most? + + def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, weighting=None, stats=False): + """ + Everything this algorithm requires should be implemented here. + Number of segmentation thresholds, bounds, etc. + + Our OsipiBase object could contain functions that compare the inputs with + the requirements. + """ + super(PV_MUMC_triexp, self).__init__(bvalues, None, bounds, None) + self.PV_algorithm = fit_least_squares_tri_exp + + + def ivim_fit(self, signals, bvalues=None): + """Perform the IVIM fit + + Args: + signals (array-like) + bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None. + + Returns: + _type_: _description_ + """ + + fit_results = self.PV_algorithm(bvalues, signals) + results = {} + results["Dpar"] = fit_results[0] + results["Fint"] = fit_results[1] + results["Dint"] = fit_results[2] + results["Fmv"] = fit_results[3] + results["Dmv"] = fit_results[4] + + return results diff --git a/tests/IVIMmodels/unit_tests/algorithms.json b/tests/IVIMmodels/unit_tests/algorithms.json index 09e2062a..f5ca881a 100644 --- a/tests/IVIMmodels/unit_tests/algorithms.json +++ b/tests/IVIMmodels/unit_tests/algorithms.json @@ -11,6 +11,7 @@ "OGC_AmsterdamUMC_biexp_segmented", "OGC_AmsterdamUMC_biexp", "PV_MUMC_biexp", + "PV_MUMC_triexp", "PvH_KB_NKI_IVIMfit", "OJ_GU_seg" ], @@ -163,5 +164,23 @@ "Dp": 1e-1 } } + }, + "PV_MUMC_triexp": { + "tolerances": { + "rtol": { + "Dpar": 5, + "Fint": 5, + "Dint": 25, + "Fmv": 25, + "Dmv": 25 + }, + "atol": { + "Dpar": 1e-2, + "Fint": 1e-2, + "Dint": 1e-1, + "Fmv": 1e-1, + "Dmv": 1e-1 + } + } } }