1+ from src .wrappers .OsipiBase import OsipiBase
2+ from super_ivim_dc .source .Classsic_ivim_fit import IVIM_fit_sls
3+ import numpy as np
4+
5+ class TCML_TechnionIIT_SLS (OsipiBase ):
6+ """
7+ TCML_TechnionIIT_lsqBOBYQA fitting algorithm by Moti Freiman and Noam Korngut, TechnionIIT
8+ """
9+
10+ # I'm thinking that we define default attributes for each submission like this
11+ # And in __init__, we can call the OsipiBase control functions to check whether
12+ # the user inputs fulfil the requirements
13+
14+ # Some basic stuff that identifies the algorithm
15+ id_author = "Moti Freiman and Noam Korngut, TechnIIT"
16+ id_algorithm_type = "Bi-exponential fit, segmented fitting"
17+ id_return_parameters = "f, D*, D, S0"
18+ id_units = "seconds per milli metre squared or milliseconds per micro metre squared"
19+
20+ # Algorithm requirements
21+ required_bvalues = 4
22+ required_thresholds = [0 ,0 ] # Interval from "at least" to "at most", in case submissions allow a custom number of thresholds
23+ required_bounds = False
24+ required_bounds_optional = False # Bounds may not be required but are optional
25+ required_initial_guess = False
26+ required_initial_guess_optional = True
27+ accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most?
28+
29+
30+ # Supported inputs in the standardized class
31+ supported_bounds = False
32+ supported_initial_guess = True
33+ supported_thresholds = True
34+
35+ def __init__ (self , bvalues = None , thresholds = None , bounds = None , initial_guess = None , fitS0 = True ):
36+ """
37+ Everything this algorithm requires should be implemented here.
38+ Number of segmentation thresholds, bounds, etc.
39+
40+ Our OsipiBase object could contain functions that compare the inputs with
41+ the requirements.
42+ """
43+ super (TCML_TechnionIIT_SLS , self ).__init__ (bvalues = bvalues , bounds = bounds , initial_guess = initial_guess )
44+ self .fit_least_squares = IVIM_fit_sls
45+ self .fitS0 = fitS0
46+ self .initialize (bounds , initial_guess , fitS0 ,thresholds )
47+
48+ def initialize (self , bounds , initial_guess , fitS0 ,thresholds ):
49+ if bounds is None :
50+ print ('warning, only D* is bounded between 0.001 and 0.5)' )
51+ self .bounds = ([0.0003 , 0.001 , 0.009 , 0 ],[0.008 , 0.5 ,0.04 , 3 ])
52+ else :
53+ print ('warning, although bounds are given, only D* is bounded)' )
54+ bounds = bounds
55+ self .bounds = bounds
56+ if initial_guess is None :
57+ print ('warning, no initial guesses were defined, so default bounds are used of [0.001, 0.1, 0.01, 1]' )
58+ self .initial_guess = [0.001 , 0.1 , 0.01 , 1 ] # D, Dp, f, S0
59+ else :
60+ self .initial_guess = initial_guess
61+ self .use_initial_guess = True
62+ self .fitS0 = fitS0
63+ self .use_initial_guess = True
64+ self .use_bounds = True
65+ if thresholds is None :
66+ self .thresholds = 150
67+ print ('warning, no thresholds were defined, so default bounds are used of 150' )
68+ else :
69+ self .thresholds = thresholds
70+
71+ def ivim_fit (self , signals , bvalues , ** kwargs ):
72+ """Perform the IVIM fit
73+
74+ Args:
75+ signals (array-like)
76+ bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None.
77+
78+ Returns:
79+ _type_: _description_
80+ """
81+
82+ bvalues = np .array (bvalues )
83+ bounds = np .array (self .bounds )
84+ bounds = [bounds [0 ][[0 , 2 , 1 , 3 ]], bounds [1 ][[0 , 2 , 1 , 3 ]]]
85+ initial_guess = np .array (self .initial_guess )
86+ initial_guess = initial_guess [[0 , 2 , 1 , 3 ]]
87+
88+
89+ fit_results = self .fit_least_squares (1 ,np .array (signals )[:,np .newaxis ],bvalues , bounds ,initial_guess ,self .thresholds )
90+
91+ results = {}
92+ results ["D" ] = fit_results [0 ]
93+ results ["f" ] = fit_results [2 ]
94+ results ["Dp" ] = fit_results [1 ]
95+
96+ return results
0 commit comments