From fb2bf95cbb54d3a924337483bb53bc8feadfb126 Mon Sep 17 00:00:00 2001 From: Daan Date: Fri, 19 Jul 2024 14:22:55 +0200 Subject: [PATCH 1/4] inital commit triexp PV MUMC --- src/standardized wip/PV_MUMC_triexp.py | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/standardized wip/PV_MUMC_triexp.py 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 From ef5033906e4ad49eb6e677dca268980cbfaded35 Mon Sep 17 00:00:00 2001 From: Daan Kuppens <113351633+DKuppens@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:53:07 +0200 Subject: [PATCH 2/4] Create PV_MUMC_triexp.py --- src/standardized wip/PV_MUMC_triexp.py | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/standardized wip/PV_MUMC_triexp.py 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 From 206d92071e3d6135f1afdf8625f8d4da96949e28 Mon Sep 17 00:00:00 2001 From: Daan Date: Fri, 18 Oct 2024 13:32:49 +0200 Subject: [PATCH 3/4] setup triexp testing step 1: adding algorithm to json and slightly changing the code to work with bounds --- .../PV_MUMC/triexp_fitting_algorithms.py | 4 ++-- .../PV_MUMC_triexp.py | 1 - tests/IVIMmodels/unit_tests/algorithms.json | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) rename src/{standardized wip => standardized}/PV_MUMC_triexp.py (99%) diff --git a/src/original/PV_MUMC/triexp_fitting_algorithms.py b/src/original/PV_MUMC/triexp_fitting_algorithms.py index 196757a5..d84f41a5 100644 --- a/src/original/PV_MUMC/triexp_fitting_algorithms.py +++ b/src/original/PV_MUMC/triexp_fitting_algorithms.py @@ -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/PV_MUMC_triexp.py similarity index 99% rename from src/standardized wip/PV_MUMC_triexp.py rename to src/standardized/PV_MUMC_triexp.py index ec7f3fee..8755c9db 100644 --- a/src/standardized wip/PV_MUMC_triexp.py +++ b/src/standardized/PV_MUMC_triexp.py @@ -45,7 +45,6 @@ def ivim_fit(self, signals, bvalues=None): Returns: _type_: _description_ """ - fit_results = self.PV_algorithm(bvalues, signals) Dpar, Fint, Dint, Fmv, Dmv = fit_results diff --git a/tests/IVIMmodels/unit_tests/algorithms.json b/tests/IVIMmodels/unit_tests/algorithms.json index e4320f59..e4327707 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" ], "IAR_LU_biexp": { @@ -162,5 +163,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 + } + } } } From 61a0ef325e5c8cca1da019b23f5bb8db242dd98e Mon Sep 17 00:00:00 2001 From: Paulien Voorter <91143388+paulienvoorter@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:25:22 +0200 Subject: [PATCH 4/4] Update triexp_fitting_algorithms.py Similar changes that Daan made to line 107-109 --- src/original/PV_MUMC/triexp_fitting_algorithms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/original/PV_MUMC/triexp_fitting_algorithms.py b/src/original/PV_MUMC/triexp_fitting_algorithms.py index d84f41a5..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)