Skip to content

Commit

Permalink
Continued work on conformance.
Browse files Browse the repository at this point in the history
Added algorithm defining attributes to the algorithm-specific class.
Added simple RMSE calculation for single parameter set to OsipiBase.
  • Loading branch information
IvanARashid committed Aug 3, 2023
1 parent aae8cc0 commit 10cb57a
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 12 deletions.
Binary file modified src/original/ETP_SRI/__pycache__/LinearFitting.cpython-39.pyc
Binary file not shown.
51 changes: 46 additions & 5 deletions src/standardized/ETP_SRI_LinearFitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,50 @@ class ETP_SRI_LinearFitting(OsipiBase):
Implementation and execution of the submitted algorithm
"""

def ivim_fit(self, signals, b_bvalues):
ETP_object = LinearFit(self.thresholds[0])
# I'm thinking that we define default attributes for each submission like this
# And in __init__, we can call the OsipiBase control functions to check whether
# the user inputs fulfil the requirements

# Some basic stuff that identifies the algorithm
author = "Eric T. Peterson, SRI"
algorithm_type = "Linear fit"
return_parameters = "f, D*, D"
units = "seconds per milli metre squared"

# Algorithm requirements
b_values_required = 3
thresholds_required = [1,1] # Interval from 1 to 1, in case submissions allow a custom number of thresholds
bounds_required = False
accepted_dimensions = 1

def __init__(self, thresholds, weighting=None, stats=False):
"""
Everything this method requires should be implemented here.
Number of segmentation thresholds, bounds, etc.
Our OsipiBase object could contain functions compare the inputs with
the requirements.
"""
self.thresholds = thresholds
# Could be a good idea to have all the submission-specfic variable be
# defined with initials?
self.ETP_weighting = weighting

# Check the inputs
self.check_thresholds_required_osipi()


def ivim_fit(self, signals, b_values):
"""
We may want this function to be as simple as
data and b-values in -> parameter estimates out.
Everything else such as segmentation thresholds, bounds, etc. should
be object attributes.
This makes the execution of submissions easy and predictable.
All execution stepts requires should be performed here.
"""
ETP_object = LinearFit(self.thresholds[0])
f, D, Dstar = ETP_object.ivim_fit(b_values, signals)


Expand All @@ -27,6 +68,6 @@ def ivim_model(b, S0=1, f=0.1, Dstar=0.03, D=0.001):

signals = ivim_model(b_values)

model = ETP_SRI_LinearFitting()
model.thresholds = [200]
results = model.ivim_fit(b_values, signals)
model = ETP_SRI_LinearFitting([200])
results = model.ivim_fit(signals, b_values)
test = model.simple_bias_and_RMSE_test(SNR=20, b_values=b_values, f=0.1, Dstar=0.03, D=0.001, noise_realizations=10)
50 changes: 43 additions & 7 deletions src/wrappers/OsipiBase.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

import numpy as np
from scipy.stats import norm


class OsipiBase:
Expand Down Expand Up @@ -29,22 +30,57 @@ def accepts_dimension_osipi(self, dim):
return False
return accepted[dim]

def thresholds_required_osipi():
def check_thresholds_required_osipi(self):
"""How many segmentation thresholds does it require?"""
return 0
if (len(self.thresholds) < self.thresholds_required[0]) or (len(self.thresholds) > self.thresholds_required[1]):
print("Conformance error: Number of thresholds.")
return False
return True

def guess_required_osipi():
def check_guess_required_osipi():
"""Does it require an initial guess?"""
return False

def bounds_required_osipi():
def check_bounds_required_osipi():
"""Does it require bounds?"""
return False

def check_b_values_required_osipi():
"""Minimum number of b-values required"""
pass

def author_osipi():
"""Author identification"""
return ''

def simple_test():
pass
def simple_bias_and_RMSE_test(self, SNR, b_values, f, Dstar, D, noise_realizations=100):
# Generate signal
b_values = np.asarray(b_values)
signals = f*np.exp(-b_values*Dstar) + (1-f)*np.exp(-b_values*D)

f_estimates = np.zeros(noise_realizations)
Dstar_estimates = np.zeros(noise_realizations)
D_estimates = np.zeros(noise_realizations)
for i in range(noise_realizations):
# Add some noise
sigma = signals[0]/SNR
noised_signal = np.array([norm.rvs(signal, sigma) for signal in signals])

# Perform fit with the noised signal
f_estimates[i], Dstar_estimates[i], D_estimates[i] = self.ivim_fit(noised_signal, b_values)

# Calculate bias
f_bias = np.mean(f_estimates) - f
Dstar_bias = np.mean(Dstar_estimates) - Dstar
D_bias = np.mean(D_estimates) - D

# Calculate RMSE
f_RMSE = np.sqrt(np.var(f_estimates) + f_bias**2)
Dstar_RMSE = np.sqrt(np.var(Dstar_estimates) + Dstar_bias**2)
D_RMSE = np.sqrt(np.var(D_estimates) + D_bias**2)

print(f"f bias:\t{f_bias}\nf RMSE:\t{f_RMSE}")
print(f"Dstar bias:\t{Dstar_bias}\nDstar RMSE:\t{Dstar_RMSE}")
print(f"D bias:\t{D_bias}\nD RMSE:\t{D_RMSE}")


Binary file modified src/wrappers/__pycache__/OsipiBase.cpython-39.pyc
Binary file not shown.

0 comments on commit 10cb57a

Please sign in to comment.