From 34404162f4be19bdbeae578e8d57b9946d5c6d2d Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion Date: Thu, 19 Jun 2025 16:47:18 +0200 Subject: [PATCH 1/9] ivimenet added --- requirements.txt | 1 + .../OGC_AUMC_IVIMNET/Example_1_simple_map.py | 63 ++++++++ .../OGC_AUMC_IVIMNET/Example_2_simulations.py | 48 ++++++ .../OGC_AUMC_IVIMNET/Example_3_volunteer.py | 108 +++++++++++++ src/original/OGC_AUMC_IVIMNET/hyperparams.py | 145 ++++++++++++++++++ 5 files changed, 365 insertions(+) create mode 100644 src/original/OGC_AUMC_IVIMNET/Example_1_simple_map.py create mode 100644 src/original/OGC_AUMC_IVIMNET/Example_2_simulations.py create mode 100644 src/original/OGC_AUMC_IVIMNET/Example_3_volunteer.py create mode 100644 src/original/OGC_AUMC_IVIMNET/hyperparams.py diff --git a/requirements.txt b/requirements.txt index 29742e3..cc94a5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ pandas sphinx sphinx_rtd_theme pytest-json-report +ivimnet \ No newline at end of file diff --git a/src/original/OGC_AUMC_IVIMNET/Example_1_simple_map.py b/src/original/OGC_AUMC_IVIMNET/Example_1_simple_map.py new file mode 100644 index 0000000..5f55818 --- /dev/null +++ b/src/original/OGC_AUMC_IVIMNET/Example_1_simple_map.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +September 2020 by Oliver Gurney-Champion & Misha Kaandorp +oliver.gurney.champion@gmail.com / o.j.gurney-champion@amsterdamumc.nl +https://www.github.com/ochampion + +Code is uploaded as part of our publication in MRM (Kaandorp et al. Improved unsupervised physics-informed deep learning for intravoxel-incoherent motion modeling and evaluation in pancreatic cancer patients. MRM 2021) + +requirements: +numpy +torch +tqdm +matplotlib +scipy +joblib +""" +import IVIMNET.simulations as sim +from hyperparams import hyperparams as hp_example_1 +import IVIMNET.deep as deep +import time +import torch +import IVIMNET.fitting_algorithms as fit + +# Import parameters +arg = hp_example_1() +arg = deep.checkarg(arg) +print(arg.save_name) +for SNR in arg.sim.SNR: + # this simulates the signal + IVIM_signal_noisy, D, f, Dp = sim.sim_signal(SNR, arg.sim.bvalues, sims=arg.sim.sims, Dmin=arg.sim.range[0][0], + Dmax=arg.sim.range[1][0], fmin=arg.sim.range[0][1], + fmax=arg.sim.range[1][1], Dsmin=arg.sim.range[0][2], + Dsmax=arg.sim.range[1][2], rician=arg.sim.rician) + + start_time = time.time() + # train network + net = deep.learn_IVIM(IVIM_signal_noisy, arg.sim.bvalues, arg) + elapsed_time = time.time() - start_time + print('\ntime elapsed for training: {}\n'.format(elapsed_time)) + + # simulate IVIM signal for prediction + [dwi_image_long, Dt_truth, Fp_truth, Dp_truth] = sim.sim_signal_predict(arg, SNR) + + # predict + start_time = time.time() + paramsNN = deep.predict_IVIM(dwi_image_long, arg.sim.bvalues, net, arg) + elapsed_time = time.time() - start_time + print('\ntime elapsed for inference: {}\n'.format(elapsed_time)) + # remove network to save memory + del net + if arg.train_pars.use_cuda: + torch.cuda.empty_cache() + + start_time = time.time() + # all fitting is done in the fit.fit_dats for the other fitting algorithms (lsq, segmented and Baysesian) + paramsf = fit.fit_dats(arg.sim.bvalues, dwi_image_long, arg.fit) + elapsed_time = time.time() - start_time + print('\ntime elapsed for lsqfit: {}\n'.format(elapsed_time)) + print('results for lsqfit') + + # plot values predict and truth + sim.plot_example1(paramsNN, paramsf, Dt_truth, Fp_truth, Dp_truth, arg, SNR) diff --git a/src/original/OGC_AUMC_IVIMNET/Example_2_simulations.py b/src/original/OGC_AUMC_IVIMNET/Example_2_simulations.py new file mode 100644 index 0000000..815e4ba --- /dev/null +++ b/src/original/OGC_AUMC_IVIMNET/Example_2_simulations.py @@ -0,0 +1,48 @@ +""" +September 2020 by Oliver Gurney-Champion & Misha Kaandorp +oliver.gurney.champion@gmail.com / o.j.gurney-champion@amsterdamumc.nl +https://www.github.com/ochampion + +Code is uploaded as part of our publication in MRM (Kaandorp et al. Improved unsupervised physics-informed deep learning for intravoxel-incoherent motion modeling and evaluation in pancreatic cancer patients. MRM 2021) + +requirements: +numpy +torch +tqdm +matplotlib +scipy +joblib +""" + +# import +import numpy as np +import IVIMNET.simulations as sim +import IVIMNET.deep as deep +from hyperparams import hyperparams as hp_example + +# load hyperparameter +arg = hp_example() +arg = deep.checkarg(arg) + +matlsq = np.zeros([len(arg.sim.SNR), 3, 3]) +matNN = np.zeros([len(arg.sim.SNR), 3, 3]) +stability = np.zeros([len(arg.sim.SNR), 3]) +a = 0 + +for SNR in arg.sim.SNR: + print('\n simulation at SNR of {snr}\n'.format(snr=SNR)) + if arg.fit.do_fit: + matlsq[a, :, :], matNN[a, :, :], stability[a, :] = sim.sim(SNR, arg) + print('\nresults from lsq:') + print(matlsq) + else: + matNN[a, :, :], stability[a, :] = sim.sim(SNR, arg) + a = a + 1 + print('\nresults from NN: columns show themean, the RMSE/mean and the Spearman coef [DvDp,Dvf,fvDp] \n' + 'the rows show D, f and D*\n' + 'and the different matixes repressent the different SNR levels {}:'.format(arg.sim.SNR)) + print(matNN) + # if repeat is higher than 1, then print stability (CVNET) + if arg.sim.repeats > 1: + print('\nstability of NN for D, f and D* at different SNR levels:') + print(stability) diff --git a/src/original/OGC_AUMC_IVIMNET/Example_3_volunteer.py b/src/original/OGC_AUMC_IVIMNET/Example_3_volunteer.py new file mode 100644 index 0000000..e2e49ea --- /dev/null +++ b/src/original/OGC_AUMC_IVIMNET/Example_3_volunteer.py @@ -0,0 +1,108 @@ +""" +September 2020 by Oliver Gurney-Champion & Misha Kaandorp +oliver.gurney.champion@gmail.com / o.j.gurney-champion@amsterdamumc.nl +https://www.github.com/ochampion + +Code is uploaded as part of our publication in MRM (Kaandorp et al. Improved unsupervised physics-informed deep learning for intravoxel-incoherent motion modeling and evaluation in pancreatic cancer patients. MRM 2021) + +requirements: +numpy +torch +tqdm +matplotlib +scipy +joblib +""" + +# this loads all patient data and evaluates it all. +import os +import time +import nibabel as nib +import numpy as np +import IVIMNET.deep as deep +import torch +from IVIMNET.fitting_algorithms import fit_dats +from hyperparams import hyperparams as hp + +arg = hp() +arg = deep.checkarg(arg) + +testdata = False + +### folder patient data +folder = 'example_data' + +### load patient data +print('Load patient data \n') +# load and init b-values +text_file = np.genfromtxt('{folder}/bvalues.bval'.format(folder=folder)) +bvalues = np.array(text_file) +selsb = np.array(bvalues) == 0 +# load nifti +data = nib.load('{folder}/data.nii.gz'.format(folder=folder)) +datas = data.get_fdata() +# reshape image for fitting +sx, sy, sz, n_b_values = datas.shape +X_dw = np.reshape(datas, (sx * sy * sz, n_b_values)) + +### select only relevant values, delete background and noise, and normalise data +S0 = np.nanmean(X_dw[:, selsb], axis=1) +S0[S0 != S0] = 0 +S0 = np.squeeze(S0) +valid_id = (S0 > (0.5 * np.median(S0[S0 > 0]))) +datatot = X_dw[valid_id, :] +# normalise data +S0 = np.nanmean(datatot[:, selsb], axis=1).astype(' Date: Mon, 23 Jun 2025 11:29:22 +0200 Subject: [PATCH 2/9] Built wrapper and unit_testing for DL algorithms I built a wrapper in which DL algorithm train at initialisation - If self-supervised, one can give data to the algorithm to train from. - If supervised, or if no data is given, data is simulated - For IVIM-NET this happens within the wrapper - For Super IVIM DC this happens in the package (as I did not see an option to give training data) Then testing occurs. For speed, I give all testing data in 1 go. Also, as deep learning is known to predominantly work better than LSQ in noisy data, and actually do a poor job in noise-less data, I made a second DL-specific dataset which contains much more noise. Also, I made DL-specific boundaries for passing unit testing. --- conftest.py | 68 +- src/standardized/IVIM_NEToptim.py | 135 +++ src/standardized/Super_IVIM_DC.py | 110 +++ src/wrappers/OsipiBase.py | 34 +- tests/IVIMmodels/unit_tests/algorithms.json | 10 +- tests/IVIMmodels/unit_tests/generic_DL.json | 782 ++++++++++++++++++ .../unit_tests/models/super_ivim_dc.pt | Bin 0 -> 81581 bytes .../unit_tests/models/super_ivim_dc_NRMSE.csv | 3 + .../unit_tests/models/super_ivim_dc_init.json | 20 + tests/IVIMmodels/unit_tests/test_ivim_fit.py | 54 +- utilities/data_simulation/GenerateData.py | 27 + 11 files changed, 1215 insertions(+), 28 deletions(-) create mode 100644 src/standardized/IVIM_NEToptim.py create mode 100644 src/standardized/Super_IVIM_DC.py create mode 100644 tests/IVIMmodels/unit_tests/generic_DL.json create mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt create mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv create mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json diff --git a/conftest.py b/conftest.py index 3c33bda..1cbbe35 100644 --- a/conftest.py +++ b/conftest.py @@ -37,6 +37,12 @@ def pytest_addoption(parser): type=str, help="Default data file name", ) + parser.addoption( + "--dataFileDL", + default="tests/IVIMmodels/unit_tests/generic_DL.json", + type=str, + help="Default data file name", + ) parser.addoption( "--saveFileName", default="", @@ -179,6 +185,10 @@ def pytest_generate_tests(metafunc): if "bound_input" in metafunc.fixturenames: args = bound_input(metafunc.config.getoption("dataFile"),metafunc.config.getoption("algorithmFile")) metafunc.parametrize("bound_input", args) + if "deep_learning_algorithms" in metafunc.fixturenames: + args = deep_learning_algorithms(metafunc.config.getoption("dataFileDL"),metafunc.config.getoption("algorithmFile")) + metafunc.parametrize("deep_learning_algorithms", args) + def data_list(filename): @@ -210,17 +220,18 @@ def data_ivim_fit_saved(datafile, algorithmFile): first = True for name, data in all_data.items(): algorithm_dict = algorithm_information.get(algorithm, {}) - xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}), - "strict": algorithm_dict.get("xfail_names", {}).get(name, True)} - kwargs = algorithm_dict.get("options", {}) - tolerances = algorithm_dict.get("tolerances", {}) - skiptime=False - if first: - if algorithm_dict.get("fail_first_time", False): - skiptime = True - first = False - requires_matlab = algorithm_dict.get("requires_matlab", False) - yield name, bvals, data, algorithm, xfail, kwargs, tolerances, skiptime, requires_matlab + if not algorithm_dict.get('deep_learning',False): + xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}), + "strict": algorithm_dict.get("xfail_names", {}).get(name, True)} + kwargs = algorithm_dict.get("options", {}) + tolerances = algorithm_dict.get("tolerances", {}) + skiptime=False + if first: + if algorithm_dict.get("fail_first_time", False): + skiptime = True + first = False + requires_matlab = algorithm_dict.get("requires_matlab", False) + yield name, bvals, data, algorithm, xfail, kwargs, tolerances, skiptime, requires_matlab def algorithmlist(algorithmFile): # Find the algorithms from algorithms.json @@ -232,8 +243,9 @@ def algorithmlist(algorithmFile): algorithms = algorithm_information["algorithms"] for algorithm in algorithms: algorithm_dict = algorithm_information.get(algorithm, {}) - requires_matlab = algorithm_dict.get("requires_matlab", False) - yield algorithm, requires_matlab + if not algorithm_dict.get('deep_learning', False): + requires_matlab = algorithm_dict.get("requires_matlab", False) + yield algorithm, requires_matlab def bound_input(datafile,algorithmFile): # Find the algorithms from algorithms.json @@ -251,9 +263,31 @@ def bound_input(datafile,algorithmFile): for name, data in all_data.items(): for algorithm in algorithms: algorithm_dict = algorithm_information.get(algorithm, {}) - xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}), - "strict": algorithm_dict.get("xfail_names", {}).get(name, True)} + if not algorithm_dict.get('deep_learning',False): + xfail = {"xfail": name in algorithm_dict.get("xfail_names", {}), + "strict": algorithm_dict.get("xfail_names", {}).get(name, True)} + kwargs = algorithm_dict.get("options", {}) + tolerances = algorithm_dict.get("tolerances", {}) + requires_matlab = algorithm_dict.get("requires_matlab", False) + yield name, bvals, data, algorithm, xfail, kwargs, tolerances, requires_matlab + +def deep_learning_algorithms(datafile,algorithmFile): + # Find the algorithms from algorithms.json + current_folder = pathlib.Path.cwd() + algorithm_path = current_folder / algorithmFile + with algorithm_path.open() as f: + algorithm_information = json.load(f) + # Load generic test data generated from the included phantom: phantoms/MR_XCAT_qMRI + generic = current_folder / datafile + with generic.open() as f: + all_data = json.load(f) + algorithms = algorithm_information["algorithms"] + bvals = all_data.pop('config') + bvals = bvals['bvalues'] + for algorithm in algorithms: + algorithm_dict = algorithm_information.get(algorithm, {}) + if algorithm_dict.get('deep_learning',False): kwargs = algorithm_dict.get("options", {}) - tolerances = algorithm_dict.get("tolerances", {}) requires_matlab = algorithm_dict.get("requires_matlab", False) - yield name, bvals, data, algorithm, xfail, kwargs, tolerances, requires_matlab \ No newline at end of file + tolerances = algorithm_dict.get("tolerances", {"atol":{"f": 2e-1, "D": 8e-4, "Dp": 6e-2},"rtol":{"f": 0.2, "D": 0.3, "Dp": 0.3}}) + yield algorithm, all_data, bvals, kwargs, requires_matlab, tolerances \ No newline at end of file diff --git a/src/standardized/IVIM_NEToptim.py b/src/standardized/IVIM_NEToptim.py new file mode 100644 index 0000000..ab6f674 --- /dev/null +++ b/src/standardized/IVIM_NEToptim.py @@ -0,0 +1,135 @@ +from src.wrappers.OsipiBase import OsipiBase +import numpy as np +import IVIMNET.deep as deep +import torch +import warnings + +class IVIM_NEToptim(OsipiBase): + """ + Bi-exponential fitting algorithm by Oliver Gurney-Champion, Amsterdam UMC + """ + + # 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 + id_author = "Oliver Gurney Champion, Amsterdam UMC" + id_algorithm_type = "Deep learnt bi-exponential fit" + id_return_parameters = "f, D*, D, S0" + 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 = False + accepted_dimensions = 1 # Not sure how to define this for the number of accepted dimensions. Perhaps like the thresholds, at least and at most? + + + # Supported inputs in the standardized class + supported_bounds = True + supported_initial_guess = False + supported_thresholds = False + + def __init__(self, SNR=None, bvalues=None, thresholds=None, bounds=None, initial_guess=None, fitS0=True, traindata=None): + """ + 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. + """ + if bvalues == None: + raise ValueError("for deep learning models, bvalues need defining at initiaition") + #super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds, initial_guess, fitS0) + super(IVIM_NEToptim, self).__init__(bvalues=bvalues, bounds=bounds, initial_guess=initial_guess) + self.fitS0=fitS0 + self.bvalues=np.array(bvalues) + self.initialize(bounds, initial_guess, fitS0, traindata, SNR) + + def initialize(self, bounds, initial_guess, fitS0, traindata, SNR): + self.fitS0=fitS0 + self.deep_learning = True + self.supervised = False + if traindata is None: + warnings.warn('no training data provided (traindata = None). Training data will be simulated') + if SNR is None: + warnings.warn('No SNR indicated. Data simulated with SNR = (5-1000)') + SNR = (5, 1000) + self.training_data(self.bvalues,n=1000000,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 + if traindata is None: + self.net = deep.learn_IVIM(self.train_data['data'], self.bvalues, self.arg) + else: + self.net = deep.learn_IVIM(traindata, self.bvalues, self.arg) + self.algorithm =lambda data: deep.predict_IVIM(data, self.bvalues, self.net, self.arg) + + + def ivim_fit(self, signals, bvalues, **kwargs): + """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_ + """ + if not np.array_equal(bvalues, self.bvalues): + raise ValueError("bvalue list at fitting must be identical as the one at initiation, otherwise it will not run") + + paramsNN = deep.predict_IVIM(signals, self.bvalues, self.net, self.arg) + + results = {} + results["D"] = paramsNN[0] + results["f"] = paramsNN[1] + results["Dp"] = paramsNN[2] + + return results + +class NetArgs: + def __init__(self): + self.optim = 'adam' # these are the optimisers implementd. Choices are: 'sgd'; 'sgdr'; 'adagrad' adam + self.lr = 0.00003 # this is the learning rate. + self.patience = 10 # this is the number of epochs without improvement that the network waits untill determining it found its optimum + self.batch_size = 128 # number of datasets taken along per iteration + self.maxit = 500 # max iterations per epoch + self.split = 0.9 # split of test and validation data + self.load_nn = False # load the neural network instead of retraining + self.loss_fun = 'rms' # what is the loss used for the model. rms is root mean square (linear regression-like); L1 is L1 normalisation (less focus on outliers) + self.skip_net = False # skip the network training and evaluation + self.scheduler = False # as discussed in the article, LR is important. This approach allows to reduce the LR itteratively when there is no improvement throughout an 5 consecutive epochs + # use GPU if available + self.use_cuda = torch.cuda.is_available() + self.device = torch.device("cuda:0" if self.use_cuda else "cpu") + self.select_best = False + # the optimized network settings + +class NetPars: + def __init__(self): + self.dropout = 0.1 # 0.0/0.1 chose how much dropout one likes. 0=no dropout; internet says roughly 20% (0.20) is good, although it also states that smaller networks might desire smaller amount of dropout + self.batch_norm = True # False/True turns on batch normalistion + self.parallel = 'parallel' # defines whether the network exstimates each parameter seperately (each parameter has its own network) or whether 1 shared network is used instead + self.con = 'sigmoid' # defines the constraint function; 'sigmoid' gives a sigmoid function giving the max/min; 'abs' gives the absolute of the output, 'none' does not constrain the output + self.tri_exp = False + #### only if sigmoid constraint is used! + self.cons_min = [0, 0, 0.005, 0] # Dt, Fp, Ds, S0 + self.cons_max = [0.005, 0.8, 0.2, 2.0] # Dt, Fp, Ds, S0 + #### + self.fitS0 = True # indicates whether to fit S0 (True) or fix it to 1 (for normalised signals); I prefer fitting S0 as it takes along the potential error is S0. + self.depth = 2 # number of layers + self.width = 0 # new option that determines network width. Putting to 0 makes it as wide as the number of b-values + boundsrange = 0.3 * (np.array(self.cons_max)-np.array(self.cons_min)) # ensure that we are on the most lineair bit of the sigmoid function + self.cons_min = np.array(self.cons_min) - boundsrange + self.cons_max = np.array(self.cons_max) + boundsrange +class Arg: + def __init__(self): + self.train_pars = NetArgs() + self.net_pars = NetPars() \ No newline at end of file diff --git a/src/standardized/Super_IVIM_DC.py b/src/standardized/Super_IVIM_DC.py new file mode 100644 index 0000000..278923f --- /dev/null +++ b/src/standardized/Super_IVIM_DC.py @@ -0,0 +1,110 @@ +from src.wrappers.OsipiBase import OsipiBase +import numpy as np +import os +from super_ivim_dc.train import train +from pathlib import Path +from super_ivim_dc.infer import infer_from_signal +import warnings + + +class Super_IVIM_DC(OsipiBase): + """ + Bi-exponential fitting algorithm by Oliver Gurney-Champion, Amsterdam UMC + """ + + # 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 + id_author = "" + id_algorithm_type = "Supervised Deep learnt bi-exponential fit with data consistency" + id_return_parameters = "f, D*, D, S0" + 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? + + + # Supported inputs in the standardized class + supported_bounds = True + supported_initial_guess = True + supported_thresholds = False + + def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, fitS0=True, SNR = None): + """ + 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. + """ + if bvalues == None: + raise ValueError("for deep learning models, bvalues need defining at initiaition") + super(Super_IVIM_DC, self).__init__(bvalues=bvalues, bounds=bounds, initial_guess=initial_guess) + self.fitS0=fitS0 + self.bvalues=np.array(bvalues) + self.initialize(bounds, initial_guess, fitS0, SNR) + + def initialize(self, bounds, initial_guess, fitS0, SNR, working_dir=os.getcwd(),ivimnet_filename='ivimnet',super_ivim_dc_filename='super_ivim_dc'): + if SNR is None: + warnings.warn('No SNR indicated. Data simulated with SNR = 100') + SNR=100 + self.fitS0=fitS0 + self.use_initial_guess = False + self.use_bounds = False + self.deep_learning = True + self.supervised = True + modeldir = Path(working_dir) # Ensure it's a Path object + modeldir = modeldir / "models" + modeldir.mkdir(parents=True, exist_ok=True) + working_dir: str = str(modeldir) + super_ivim_dc_filename: str = super_ivim_dc_filename # do not include .pt + ivimnet_filename: str = ivimnet_filename # do not include .pt + self.super_ivim_dc_filename=super_ivim_dc_filename + self.ivimnet_filename=ivimnet_filename + self.working_dir=working_dir + train( + SNR=SNR, + bvalues=self.bvalues, + super_ivim_dc=True, + work_dir=self.working_dir, + super_ivim_dc_filename=self.super_ivim_dc_filename, + ivimnet_filename=ivimnet_filename, + verbose=False, + ivimnet=False + ) + + + def ivim_fit(self, signals, bvalues, **kwargs): + """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: + results: a dictionary containing "d", "f", and "Dp". + """ + if not np.array_equal(bvalues, self.bvalues): + raise ValueError("bvalue list at fitting must be identical as the one at initiation, otherwise it will not run") + + Dp, Dt, f, S0_superivimdc = infer_from_signal( + signal=signals, + bvalues=self.bvalues, + model_path=f"{self.working_dir}/{self.super_ivim_dc_filename}.pt", + ) + + results = {} + results["D"] = Dt + results["f"] = f + results["Dp"] = Dp + + return results diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index 6c5d380..3438d3b 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -4,6 +4,8 @@ import pathlib import sys from tqdm import tqdm +from utilities.data_simulation.GenerateData import GenerateData + class OsipiBase: """The base class for OSIPI IVIM fitting""" @@ -16,6 +18,8 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non self.initial_guess = np.asarray(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 # If the user inputs an algorithm to OsipiBase, it is intereprete as initiating # an algorithm object with that name. if algorithm: @@ -103,13 +107,15 @@ def osipi_fit(self, data, bvalues=None, **kwargs): #args = [data[ijk], use_bvalues] #fit = list(self.ivim_fit(*args, **kwargs)) #results[ijk] = fit - - for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): - args = [data[ijk], use_bvalues] - fit = self.ivim_fit(*args, **kwargs) # For single voxel fits, we assume this is a dict with a float value per key. - for key in list(fit.keys()): - results[key][ijk] = fit[key] - + if not self.deep_learning: + for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): + args = [data[ijk], use_bvalues] + fit = self.ivim_fit(*args, **kwargs) # For single voxel fits, we assume this is a dict with a float value per key. + for key in list(fit.keys()): + results[key][ijk] = fit[key] + else: + args = [data, use_bvalues] + results = self.ivim_fit(*args,**kwargs) # For single voxel fits, we assume this is a dict with a float value per key. #self.parameter_estimates = self.ivim_fit(data, bvalues) return results @@ -297,5 +303,15 @@ def osipi_simple_bias_and_RMSE_test(self, SNR, bvalues, f, Dstar, D, noise_reali 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}") - - + + def training_data(self, bvalues, data=None, SNR=(5,1000), n=1000000,Drange=(0.0005,0.0034),frange=(0,1),Dprange=(0.005,0.1),rician_noise=False): + rng = np.random.RandomState(42) + if data is None: + gen = GenerateData(rng=rng) + data, D, f, Dp = gen.simulate_training_data(bvalues, SNR=SNR, n=n,Drange=Drange,frange=frange,Dprange=Dprange,rician_noise=rician_noise) + if self.supervised: + self.train_data = {'data':data,'D':D,'f':f,'Dp':Dp} + else: + self.train_data = {'data': data} + + diff --git a/tests/IVIMmodels/unit_tests/algorithms.json b/tests/IVIMmodels/unit_tests/algorithms.json index fa90ce5..1a8cd31 100644 --- a/tests/IVIMmodels/unit_tests/algorithms.json +++ b/tests/IVIMmodels/unit_tests/algorithms.json @@ -13,8 +13,16 @@ "OGC_AmsterdamUMC_biexp", "PV_MUMC_biexp", "PvH_KB_NKI_IVIMfit", - "OJ_GU_seg" + "OJ_GU_seg", + "IVIM_NEToptim", + "Super_IVIM_DC" ], + "IVIM_NEToptim": { + "deep_learning": true + }, + "Super_IVIM_DC": { + "deep_learning": true + }, "ASD_MemorialSloanKettering_QAMPER_IVIM": { "requires_matlab": true }, diff --git a/tests/IVIMmodels/unit_tests/generic_DL.json b/tests/IVIMmodels/unit_tests/generic_DL.json new file mode 100644 index 0000000..289392e --- /dev/null +++ b/tests/IVIMmodels/unit_tests/generic_DL.json @@ -0,0 +1,782 @@ +{ + "Myocardium LV": { + "noise": 0.05, + "D": 0.0024, + "f": 0.15, + "Dp": 0.08, + "data": [ + 1.0414761561278763, + 0.9543261077240296, + 0.8973202278041915, + 0.875673251659577, + 0.8280418658198557, + 0.8958022300221469, + 0.9173950534039884, + 0.8317113405445333, + 0.6706905989242123, + 0.6921684393805962, + 0.6088101133967462, + 0.4832140883185282, + 0.4323792867026822, + 0.38563566932033716, + 0.2987971934002683, + 0.12065701822058425, + 0.06375659797908062, + 0.08780731396391583, + 0.03806733579088041, + -0.007727071899961364 + ] + }, + "myocardium RV": { + "noise": 0.05, + "D": 0.0024, + "f": 0.15, + "Dp": 0.08, + "data": [ + 1.018130474834411, + 0.9609171021077032, + 0.8649652714635861, + 0.9112196676382909, + 0.8501302652858714, + 0.7921253678406212, + 0.7426742053880208, + 0.8216937258798647, + 0.6826547057806759, + 0.6202693700942281, + 0.5354165262437048, + 0.4593879049073265, + 0.4256689200493212, + 0.386303095977355, + 0.15180872294197184, + 0.22166125931683023, + 0.09303039386870172, + 0.04854864403792119, + 0.06178473487961477, + 0.02793297584570685 + ] + }, + "myocardium ra": { + "noise": 0.05, + "D": 0.0015, + "f": 0.07, + "Dp": 0.07, + "data": [ + 1.0115104179794072, + 0.9592284470237417, + 0.9907407481807518, + 0.9701958502235285, + 0.9832619611247679, + 0.9226084706447879, + 0.8964681257799296, + 0.8058241769813856, + 0.7855605950094182, + 0.8640343886934098, + 0.71113579042937, + 0.6738151374761032, + 0.4970519703012802, + 0.39624232626712863, + 0.47767832760684903, + 0.33172877824247354, + 0.22624097472696142, + 0.24171216114003355, + 0.1445681631577838, + 0.20377619373869593 + ] + }, + "Blood RV": { + "noise": 0.05, + "D": 0.003, + "f": 1.0, + "Dp": 0.1, + "data": [ + 1.004117274360202, + 0.9001750154762196, + 0.8262144668987947, + 0.6256094182787147, + 0.36251995439688267, + 0.10262795002111597, + 0.08148909014667471, + -0.011005970525050387, + -0.01930670067328502, + 0.04446958565879572, + -0.07617803967310156, + 0.03357843546126774, + 0.022008550569263697, + -0.08500549539941143, + -0.0026807799758221383, + -0.05816063921732141, + 0.009761582528698201, + 0.005422145900150086, + -0.02790361453161008, + -0.01117137079931383 + ] + }, + "Blood RA": { + "noise": 0.05, + "D": 0.003, + "f": 1.0, + "Dp": 0.1, + "data": [ + 1.0489290319928273, + 0.8909263540876897, + 0.8655462087617176, + 0.5691970073196508, + 0.31800967828120386, + 0.11538210717002553, + -0.004187481545519553, + 0.05057059190130978, + 0.08920896827058303, + 0.03940492488165636, + -0.026116311696022677, + 0.05885164807268148, + 0.03428622678823648, + 0.027706227111570707, + -0.023770390365566028, + -0.029424468316703662, + 0.03086054658282188, + 0.016178689173731928, + 0.05617024240504107, + 0.06103537430395393 + ] + }, + "muscle": { + "noise": 0.05, + "D": 0.00137, + "f": 0.1, + "Dp": 0.0263, + "data": [ + 0.9714276818954892, + 0.9802637566544565, + 0.9334162015399322, + 1.0101339767413773, + 0.9038053357932732, + 0.9441339822995856, + 0.9811635020543724, + 0.8917657398829961, + 0.7957125159315066, + 0.729721241740571, + 0.7160232331367825, + 0.6294350916311617, + 0.5570356038224215, + 0.5073972425411085, + 0.4002331624994297, + 0.2701546943856365, + 0.22489459396945471, + 0.22238175420842007, + 0.1661025732510989, + 0.15528542071965007 + ] + }, + "Liver": { + "noise": 0.05, + "D": 0.0015, + "f": 0.11, + "Dp": 0.1, + "data": [ + 1.005697694414328, + 1.0296553455912854, + 0.9673997721515275, + 0.9923036065618027, + 0.958322841487569, + 0.9001047809063214, + 0.8816045574359181, + 0.8344130032658189, + 0.7167065156804507, + 0.7460590218679478, + 0.7265372617550753, + 0.5369587818053495, + 0.49133492872023193, + 0.4612159410743326, + 0.4046612458419037, + 0.29009641079868914, + 0.2642319987283172, + 0.21112638380969095, + 0.12385698121445748, + 0.11935684135378682 + ] + }, + "gall bladder": { + "noise": 0.05, + "D": 0.003, + "f": 0.0, + "Dp": 0.1, + "data": [ + 0.9523815647424912, + 1.032757861693169, + 1.0208923239324414, + 0.9960713724470585, + 1.0954743459226557, + 0.9269415183478232, + 0.9821839240559127, + 0.7854672286362608, + 0.8145990550130255, + 0.7171481283888134, + 0.5586465363670376, + 0.42636915882044135, + 0.31931668796316237, + 0.3142373711268758, + 0.04276652367211642, + 0.15297036815364162, + 0.13165739182159109, + 0.024464542568317357, + -0.03978115454201203, + 0.03958370667582257 + ] + }, + "esophagus": { + "noise": 0.05, + "D": 0.00167, + "f": 0.32, + "Dp": 0.03, + "data": [ + 0.9700230451815722, + 0.9640836332018894, + 0.9268778941200213, + 0.9431218226292617, + 0.895234170014858, + 0.9026509322829899, + 0.8262225731133575, + 0.6525695561966889, + 0.6226416509744647, + 0.5911441734045834, + 0.4482699307573094, + 0.41847261713492356, + 0.35140906022062113, + 0.2685256769581542, + 0.3325457936770929, + 0.23403766328303385, + 0.1192979773913282, + 0.1454474011064396, + 0.05253676969682378, + 0.08764435184661902 + ] + }, + "esophagus cont": { + "noise": 0.05, + "D": 0.00167, + "f": 0.32, + "Dp": 0.03, + "data": [ + 1.0064916942930082, + 1.0898158301770804, + 0.9700968827320784, + 0.92753661283127, + 0.8348341943602735, + 0.8807606299497848, + 0.7459175246364695, + 0.752690755777689, + 0.5975529317710861, + 0.5100753987968977, + 0.5016446634790717, + 0.44244890513467694, + 0.3999848072343228, + 0.41426923770210267, + 0.33641183391601337, + 0.2106493086629893, + 0.14637861880972128, + 0.1183822606096154, + 0.060343581730889766, + 0.07437812310993061 + ] + }, + "st wall": { + "noise": 0.05, + "D": 0.0015, + "f": 0.3, + "Dp": 0.012, + "data": [ + 0.9614650464095889, + 1.0146466845776958, + 1.0251119839509732, + 0.9301147156388705, + 0.8407853159831342, + 0.9746737421761303, + 0.8288441114092242, + 0.8072852067771582, + 0.7134443480016787, + 0.7037478594648261, + 0.5630620532604259, + 0.5282708628352321, + 0.4123946518270492, + 0.4160974562797989, + 0.3453001870835757, + 0.27648779815187785, + 0.2188296362651662, + 0.10763272028333518, + 0.16260410856774776, + 0.11105320684887388 + ] + }, + "Stomach Contents": { + "noise": 0.05, + "D": 0.003, + "f": 0.0, + "Dp": 0.0, + "data": [ + 0.9310843015691552, + 0.9619695749273509, + 0.9023065970929444, + 0.9851165418446419, + 0.9521433939573646, + 0.9599509082487774, + 0.8686295937425018, + 0.8635615283375762, + 0.7532782265539696, + 0.7567248617550365, + 0.6078580802195307, + 0.48916900427956644, + 0.3285596647668797, + 0.2982203304014941, + 0.13991589714962996, + 0.10432550191849926, + 0.04837910531117902, + 0.06807960933657477, + 0.030689883066384722, + 0.03144188538636681 + ] + }, + "pancreas": { + "noise": 0.05, + "D": 0.0013, + "f": 0.15, + "Dp": 0.01, + "data": [ + 0.9928999447173146, + 0.9525884401862714, + 0.9544025739942831, + 1.019780145611553, + 0.961400430143548, + 0.9569039948062483, + 0.9661430640694069, + 0.8583162595452268, + 0.8680011464621598, + 0.7917372192063143, + 0.6403280950200478, + 0.6088869053903841, + 0.46771540523097155, + 0.6232680672291963, + 0.4029692054952672, + 0.2779379656422561, + 0.2655335879894387, + 0.24431552892980105, + 0.18473475287844374, + 0.14312294681217042 + ] + }, + "Right kydney cortex": { + "noise": 0.05, + "D": 0.00212, + "f": 0.097, + "Dp": 0.02, + "data": [ + 0.9447164616388711, + 1.0253524557069253, + 0.9468030021740546, + 1.1027993540032097, + 1.0112607157637477, + 0.9170695922780615, + 0.9318788149355747, + 0.8925299262472647, + 0.7701581384241947, + 0.6793398491842773, + 0.6572998748193475, + 0.5157976361332927, + 0.47535603661203346, + 0.3877673061842632, + 0.2628517365408753, + 0.25671357183005467, + 0.08192037711847165, + 0.1315631464014776, + -0.009927392935916046, + 0.10736124706369708 + ] + }, + "right kidney medulla": { + "noise": 0.05, + "D": 0.00209, + "f": 0.158, + "Dp": 0.019, + "data": [ + 0.9939325550509083, + 0.9769465793399255, + 0.965260696936534, + 0.8861841240325065, + 1.0021437148818337, + 0.8581427450798357, + 0.8580008326775601, + 0.7898237498945948, + 0.7294053682674831, + 0.6479355858753167, + 0.5760151477149101, + 0.5706331155943267, + 0.3294110737489627, + 0.3545622889211826, + 0.278112120050124, + 0.13294296073806244, + 0.2190707765277487, + 0.03969232934431753, + -0.036670871081434755, + 0.07399982807913605 + ] + }, + "Left kidney cortex": { + "noise": 0.05, + "D": 0.00212, + "f": 0.097, + "Dp": 0.02, + "data": [ + 1.058610075459223, + 0.9603284679793281, + 0.9518735294413312, + 0.9760242027907666, + 0.9143294993148827, + 0.9345568670493143, + 0.9069139228958475, + 0.8214728744866402, + 0.8207206998561044, + 0.7821725781684973, + 0.7121928931052638, + 0.5352966671483371, + 0.42550424064337955, + 0.40421086397522105, + 0.3087318820412746, + 0.22552935189848855, + 0.17643830523984294, + 0.14364947466713723, + 0.14839305825625088, + 0.06962283503370063 + ] + }, + "left kidney medulla": { + "noise": 0.05, + "D": 0.00209, + "f": 0.158, + "Dp": 0.019, + "data": [ + 1.0696846876257082, + 0.9944476109980003, + 0.9345852013753286, + 0.917965814581272, + 0.9113951980580977, + 0.8562427844533702, + 0.7907698414887543, + 0.7468883137498656, + 0.8500183261263754, + 0.7240914828941581, + 0.6502933564134067, + 0.5873884036622673, + 0.44002324389302944, + 0.422312250329574, + 0.2020018131709333, + 0.196833508107462, + 0.11121199829130335, + 0.12743794653359145, + 0.05793124120030294, + 0.07503516982609999 + ] + }, + "spleen": { + "noise": 0.05, + "D": 0.0013, + "f": 0.2, + "Dp": 0.03, + "data": [ + 1.0406891927127284, + 1.1421805725534993, + 0.9712122549708575, + 0.9328597405180468, + 0.9219674445445825, + 0.8575471432718786, + 0.795853472538119, + 0.8453455611515053, + 0.7236899749139, + 0.6371721300759845, + 0.6466445962936025, + 0.6139781063913334, + 0.5160364863184796, + 0.5226170618192205, + 0.4366671404988213, + 0.40407026774054444, + 0.3471132427761567, + 0.16279312392662906, + 0.25280036274001105, + 0.1507097449302377 + ] + }, + "spinal cord": { + "noise": 0.05, + "D": 0.00041, + "f": 0.178, + "Dp": 0.0289, + "data": [ + 1.0350348261603348, + 0.9881173929532401, + 1.0159094446411976, + 0.9040873399202006, + 0.9804932581335475, + 0.9916144240980125, + 0.851552084185367, + 0.8732141509851978, + 0.8012466746589524, + 0.7262491077666176, + 0.7045544540842098, + 0.7293489316518207, + 0.7218468593284062, + 0.6201918664289697, + 0.6042211809316861, + 0.5775834039668379, + 0.5854901370792571, + 0.6445765726048157, + 0.4825856101525844, + 0.5900286566520527 + ] + }, + "Bone Marrow": { + "noise": 0.05, + "D": 0.00043, + "f": 0.145, + "Dp": 0.05, + "data": [ + 0.9970318173523992, + 1.0006434152520487, + 1.018209085021019, + 0.9563480643325503, + 0.9143856650490984, + 0.8558112415285944, + 0.7935998988418784, + 0.7611400408646187, + 0.9345174322860307, + 0.8048602103030668, + 0.7556633585268748, + 0.7322228326193456, + 0.7222214605640584, + 0.6505000275910844, + 0.6831273225528944, + 0.676502193789096, + 0.617660081757738, + 0.5958910006147835, + 0.5432855457656088, + 0.5349508966811534 + ] + }, + "Artery": { + "noise": 0.05, + "D": 0.003, + "f": 1.0, + "Dp": 0.1, + "data": [ + 1.1366652743929608, + 0.9031078968867813, + 0.8276404785227217, + 0.6703488575617523, + 0.35463043473306477, + 0.17999841083004475, + -0.005301951566434267, + -0.026002339288390332, + -0.025976254242574114, + -0.0314649963825061, + -0.06597097622587501, + -0.03190712544992341, + 0.061423460792576726, + -0.01410326352575495, + 0.06885107149344581, + -0.008697506332233622, + 0.08605639313609106, + 0.00041575373852931327, + 0.01208369365799957, + -0.049815610235614566 + ] + }, + "Vein": { + "noise": 0.05, + "D": 0.003, + "f": 1.0, + "Dp": 0.1, + "data": [ + 1.0246512699008448, + 0.9525140798242245, + 0.6645580509918904, + 0.638261134232097, + 0.36686671463468157, + 0.14487481261837412, + 0.019134419276372692, + -0.12375298340567041, + -0.011689865160312663, + 0.002827581793995449, + -0.12913694494128608, + 0.04388747661411013, + -0.11902684240931445, + -0.041892796222326355, + 0.05622029125932542, + 0.05021727325560413, + -0.07412321200463441, + 0.05842113202675782, + 0.03548103045918604, + -0.00952204490054799 + ] + }, + "asc lower intestine": { + "noise": 0.05, + "D": 0.00131, + "f": 0.69, + "Dp": 0.029, + "data": [ + 1.0310050726425262, + 1.0786855981547856, + 0.905032835711743, + 0.9791870157610291, + 0.9291607613596673, + 0.7301516576604141, + 0.579963207738715, + 0.4685531576016079, + 0.37122838352980947, + 0.32263920108649796, + 0.13979193917260468, + 0.2330337216536861, + 0.17574159838764214, + 0.2577630654754723, + 0.18085525908143332, + 0.17355227443805832, + 0.1230183037281744, + 0.18333043869275675, + 0.14772233622396666, + 0.02088579003900755 + ] + }, + "trans lower intestine": { + "noise": 0.05, + "D": 0.00131, + "f": 0.69, + "Dp": 0.029, + "data": [ + 1.1010910165710657, + 0.9589485258640357, + 0.9460125348317381, + 0.9021826524332424, + 0.8477384509092252, + 0.6261562626372219, + 0.4961681603306774, + 0.46384706442332085, + 0.3800300793387826, + 0.23097018624158372, + 0.30767900657047303, + 0.3433823377021268, + 0.2781958930360044, + 0.1971206257280814, + 0.07308806504798766, + 0.07482710194380694, + 0.11520105129248473, + 0.08399119922734945, + 0.04586663820196364, + 0.07259384018643282 + ] + }, + "desc lower intestine": { + "noise": 0.05, + "D": 0.00131, + "f": 0.69, + "Dp": 0.029, + "data": [ + 1.0325140842231593, + 0.9257803136790758, + 1.0117244258862137, + 0.8643929081857167, + 0.9004162931520309, + 0.606100414631902, + 0.5903776292670289, + 0.5108809559255845, + 0.36796610314533457, + 0.28936305573833354, + 0.22090639026774, + 0.274903578964495, + 0.2353185102438208, + 0.12165334016083454, + 0.08854845711319659, + 0.12526607999630837, + 0.14000920576701195, + -0.0014170609609992502, + 0.016068844967175137, + 0.06689049367488156 + ] + }, + "small intestine": { + "noise": 0.05, + "D": 0.00131, + "f": 0.69, + "Dp": 0.029, + "data": [ + 1.0113923280346593, + 0.9516429649530004, + 1.028263051337911, + 0.8702056497106295, + 0.7882616940484249, + 0.6559200169813942, + 0.5171409903767998, + 0.4689268545793833, + 0.34658802842547975, + 0.3041407511081699, + 0.20505903889702215, + 0.2749110731916181, + 0.18737578729206558, + 0.24810421746997058, + 0.1631729167488409, + 0.00028511829852838344, + 0.13368329347787375, + 0.059266858804970776, + -0.00953865541370462, + 0.023426001677845962 + ] + }, + "pericardium": { + "noise": 0.05, + "D": 0.003, + "f": 0.07, + "Dp": 0.01, + "data": [ + 1.0317736953179435, + 1.021755679310279, + 1.0124932359422896, + 0.919227516752285, + 0.974858852394421, + 0.9059177859197673, + 0.8962201140038372, + 0.8157261967964851, + 0.7299953974218134, + 0.7318032532445874, + 0.6228612099840378, + 0.4905121653825162, + 0.3035470208513579, + 0.27965049598505864, + 0.23695132503732727, + 0.15839380613261925, + 0.03703305780999669, + -0.02262660123747843, + 0.08509977607208054, + 0.04530243888669494 + ] + }, + "config": { + "bvalues": [ + 0.0, + 1.0, + 2.0, + 5.0, + 10.0, + 20.0, + 30.0, + 50.0, + 75.0, + 100.0, + 150.0, + 250.0, + 350.0, + 400.0, + 550.0, + 700.0, + 850.0, + 1000.0, + 1100.0, + 1200.0 + ] + } +} \ No newline at end of file diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt b/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt new file mode 100644 index 0000000000000000000000000000000000000000..4ffd3658ea0c5fedf66376a2e52200a6663b66bc GIT binary patch literal 81581 zcmb?^1$Y!m*L6>V1_-jj-95y0Y68I>8VjyjLeME1w;2fT?(R;o1Tra!uzrN@AK@b)3@%PQ{8=M=60r%tyj}Ig&?F!BmDADHX*&xvsaIB+PYKkPF-7f zXkV^FTdA#kkIr4Hgr*UissC7<+PClCrAv5wsZ;lEJzeV34&gMsL#0mbB^R?$QZyS% zgk^#B-|bs>Y1=QH_H_4f?-SmsV}xXOYAFLs%hPUh0sY{2}Qh2wX z-D&IIUM^K8^fITe zJ;@-M)6}mi)O4%qHo}}1gJFc(Wpo5zQaZ_;UNmP2%jB@GcAeVxG`mK?G$KWWBI;?* z7?HZBn`F);YYjJNu4x!<&Z6l2C7Insb5@7*^Nc^gY&s<`b9O~3hpLp5E9Fv@a!cks zqB*a_iag!r73I_Ec$@PpIt5gnf?TJNqElEh7ZJ@x!*b}(%+sCr>ej7Ow~np5hPUlz zE~eA-F&9_#N~n4zxn3znue4+?Bbv*GWlu)0cUx+9*U9;s%PDdms+=d6^HSuzC9{uc z_6^JLAlI!|*VgUYO6?=Ud$yKn+xDHqJDB|xS@nk3>fWuRcEA1Q`|W2AfD>+WAe>5; zR2+gNbFgSOI@}VU_*+7BYxOsqB9uxAkr&G=rMyxp6%@Hp$y`x1S8}*7KFWQmtP=|` zS5Z#9s(Rwp_=#6nbZSWEnxeUu!?JwimsMM*6lkuaDAiS!>T#v|ijpXq8;ItH4lD9i zR@6w-al#e$^e{J8gqo;AVO*%GBGgPWHy6z<97@GkUn(v2f}ZA9iePJ1uniY%s|dD} z%%vRAn)L~(P@e3QKm+~`5DN@5#sS#Xiq#`v+GLII` zV;t5LsH|zMUdZ1(P7xZf3Qgcb6BVIJl6kUdp5jm)f%@v0suv6}Pg4Y^tAaDQ;7mnu zmSmnSn&&uFM}i=)j=6fhK=V9BZ@#Lxfa@((^cG3x#iDtMLv;iuSshDNU8Op_Jj~0W zi|SYo-T3NQA(>Z-=2c;T>ED{X;_t$0-P*i7&1=;1SSv4%m&ZD#Jk~1<+#s1ZisnsW znH>e?_w;DJoR@jCa;jU@Q{Bo>b(^BJT{6dr=2*w2dB-nphhEFuyi?JNQ?+bdiz!-m z$-GN6?{-|3w^AW{^h!SFy^7L4RcSw0I-n>Wl+1@j^Wm_Z`t$SFm&g&lqObX=qIgVI zJkAwQD2gW~^C{7M+M(t=6BK!koY4#Vna?VM=TyP-T=0S-cu_K663v&x3OFt?ske|T zs<2ul{^qL?Mv+{DaD0(mm&`Xr^G*Go@Ji@tZs`{nV7{#u$sKuhyh!dUMRHGB;eE;c zKr}zp-wrM)7s(^NT%h@}a;{I*bA8Is^_imdTr$59%`Y97rZpq4bXwjX=GThW8&&Hq z*LtUDy_d}Yh~^KDt5VyMk9s9f^Cv~=v#RukD}7azzDefqqWOouNW9z~8j_!SMK3C# zpOZzY0Zh@QPN0yc&KPBNngUFsDS;wQ1=LqgVjENrO^pn_kvC1lW4ynkX~9$@7f^1L z4x?g}9!#PcfFgAT>MM#HB~ei{BQjM(rKr4pXeJ)xbwx9S!B8zNngxdv{R=2kH=yH< zjBjCRR!r(o!}hJp&Xd%-~q}VJTWQ`USJaS28z@N=vc!E zjSX+AsV`FWPJz^q$7CmeFx4pllsg4tRGfmqBpM79sS)T{#tBZmj0rMyMm`?Y#AC8i z2$*VA9+VqZz^E97f=RR@P^6WBj%A!=Z$&F3Q#Dk|*vFGr;W4?4tAfEWzKpBkP@>g= zBCP>*y!Y|#7Oja%{b_j7T0EwdacxlfJa`$`!GTi7b-|Q#sRt&}`aqG2K*s`5`Y_r6 zQ+gY3+K|WOtJMfhy;_Yy`PFKIQE}i884_&@6lpV{vN*KrgMlsX;`rku7eYY&)ux%Ptc%e4=q;; zmH2*_9>S#FBY+;}G1=n?nCfv9lzSY*sCXO)ljsScNKXPC&qC>g=_yR<9RlfT9+Mr; zfT<2=LAk>@jEci~Fo|9Oiu58-UtZo0{V~0S6rGcw2ffT=veOkX)#)lIce;jAak>sB z(HlUK-URC3loLDgH|1N%&>MNu+dL*4-2qdL?t*fodl(g?`(P4%02Jv%p#Dvn8zoi7 zkC3SvDrM~FMIZB+T*gnpU>IM+ot#TL@2M;ib zdICl21#~z*-36I+G0-c6F}SXn0&q3gQ?f611P^<;TRQ* zzriHh5h&6K;4demwaA??t2YUxkvt}wbOuvRx`1+%t{4@QZeSAa4isq*;4kN)w#a78 z=_~>~DCIHPq9>SYA%Sv>UKkaN-e3~#0~BdrpuR=MrE+MI`yoef!J^iE!MD38fb!@yLhC{XS+9HZhi0t}BxKs+J= z^)0e|Qc3j|qmimQD%Bj|O~>$q#JG62IWp$Fe*-4!SHkh#M2Q_|E8SSiPv!qGW14)G?vF?qa9$X(N0iq6o*kUvVr00 z2#BX6p#Dvn8zoi7yO60GDrFq#L3i_*T*iCAU>IMCedR+ksb#+7J%9!pTLyf#*3chG5Knp0#mQn zX;6N(&R|p=&Votw98jd^fsUu5wa6DRsrT@v7kNzfxCEwpTn6PHS1>9bSHUED4Jgv< zK*zICTjU#<(mVLjn>;2v+yYY_Zi8}%I~Wy*yI>N%2Ndajpko;)G_G=Q@c=1$Ctv!I z$7H8RV5-w&Q10{uqvG@w3{OWuJRJcY%Q(S_m+=c^=#Bj7OCFPrUV*7buR*!d8;pw4 zTQEEw0r7MMbS&efTjYO`sT!(f>`y=Nm|Vsm!C)9)#-DH~(a%7UegQh(d#y$Oib?%x z1ki6hrj+q_Q29J~8UMh6QpP{QlyecVVMw9|ph%s7js>8$$j+G3+XT`SJSJbQlwj)B zN(IWVR%(ojLmDuAy95-e3()aYv=%uXCN&RmY~S#p>3L8#$pEIBxPo$%j2IP@Okns% z2`JJmK*!Tid*r_`Ilv^E6DZPLK*vH(XkFz(&W#iY zD=(Ud2W6|gV5(I^JRkubD>=c6S8`!wI5>IJB0MNN6$MkBih**c z;usaD5@2{j0^$h?=vc~0x5=fEsXF@0JI*{kd}tXSlxw*x7#!nk*&T-xEe8~-2TLj^$n(MB{vs=m~$}l zqd`0fgEf4(VbhJ*n0%jdt{Anl;$}SbbRF_Ji+@&%` z#ia_EM5_WtS`GNinW(LDb<8<<1kf5hD0|cdQ$1>da*x^=6^}Y#60HjqX+5C6SH{(H z=#}du$H6O*iaaQLH2_n+8iI1KMi>>Z#$b3@0^(r_==kbM*BB2~3ii`vtZw&Ov$sM~|VGQOxg;83FB zK#~3pls6oOcWd7rn?DnJ<&Kz%KaGe~Y86KyC@-E@aVH!oRU8SXoIqzViFN^sv@0;o z6>o+K+HwVVLtIlh^bel2PnTPW{ip`1(Rq`phzX4Zoyhd+zT_Btg>B`_C`#V z?E}hX`(jjN`+-TcKTxCtfV#D+P4Ga(HBn{nCLM&BDmoaHiw?o4h+4oTY6XgPC@?Hr zq6?tkIzfkFUK3X~a?&WoRPo`UTzmvZMSLWfL`MNdIvN<3HA!(^g<~+SX)C)r=~%>6 z?Qx)7dpt%(djc4~XaeGkCSX`zopw@XHyLTFhkt~!pOa2O3?A{dHWh~wody)?bfE6m zX$|cR%xH^Mw{_B)2+FJFwKfX}O0CTXQ&v0&OrmpvBAo}+-9NR5osYPttnBfm3lLK; z!$MGg85UtwL>GfebO}(TOM$utYi;W?%xJR8hEKX2F;#X2D3@J{QITB*CehVEk*)#i z)~a@@YZ2E(mCc`Y9b&5JdQdL90izS`^aWt2bDH)CECS2lvuEr_Y& zTS2+_HjIk+b})&?07V)L)YV#oIIp!GnAWtFO`&urVybo=DA%@ORJ0kGMD0M4?gHv+ zE$J3?H_}uO{|IG+DBXh?JmPC@FAnki1jO?bPOnf zQ4zffCed3!k=_RCYAvCulWXk`<~4C;TPeMZm@0k`l#AcTsE9uRljuXBNFM=pwU!{x zYwa#&=nJ4oUjlWtmUNH!3Tdi`e}uB#l)gp`9`UvI z28R-T3l!-)pxm^_f6gUzh3_$?tybN1O8-GnUM?@T4>(YY?IW17UrGk{6d6)4h- zKzZ-6c7YwbyG%&X)Rj%DG&5qVdKOTw{uf3?-3?5lS%D(W2GmuvLqC@t^P0G_VU^}U zOcl=w%EfbGRK#%6EN|4By7i}-k6HFh`6ox zpFRl6*PXB47YEAf{lFA)e=vy#07V)IOuYK|t+_M^6PmQLEtdu(CMyr8Mo_+Ff>BX5 zf#JCci03Ate#u$~S^-mcdf7PJ~BG*xB0 zF0GE3s#*h-tJcJ*sMZ3LXli`qKbQ15YmEWuDA|YNqQqaDfu7|K{P#=^Vh!_=v z24E6x2oz}}VB%L#Vgvqu-5B$lei}``3Bsy=7%10oic!&T1}4$wK#{fpCVl~N{iJ%) zmdI0m{3A0dKG>++3Ss!fzlK`lP@-*sB5e!Q-$<<=ZHFms(b;4%{yS;3J>v4JdHHp~ zfl_|qV9Lt>2E$iTKztPi)Zb9GC+&m@O+BA>Ng@$eFG*)meo4AuRD`>NNwgbKq}_q~ zg=>9j4@_y&#WZO%!m2a{<76q2&?)dLAm}YjEeqfFo})u}} zAs)7Xc-R8!Z>QF)PR5kB=vuOvT!&K-msic}a4HUzI-CZktb96{L}vg+IuoeBr)s}C z3lo}pL+z5xMp(Thb3pkenTt^oo(G2Sr-1l=3aDSW*0V0elqTImlU{_dD!mw#OE1Bw zNG}DG=rW*4mjm@{R{Pc!n9zhfXu>NIR)trAa^ck&72!2t5?u=v={lgk4inl-xenJO zK{M#08EimUHP{Hs4K`s^45Gm#x)~_aEkJ!8CK&KK+=_WkU()oqA*||e2j%)P7#00k zFp2H}igYJXUx!KeuyM#!ef%Q_Dn6(~8^Z93uS14IJct4DAO@6wRHt@X^7d8bw|(hu z%xSAmv_p0e;_|Y2A@0S2Qi%J&l%?+n!&f>?wp*?bD!K`wT`!`z#o~z5?RwE1<)w z)t>eOW;Er5ZLk*+R+TS-a^=ey73C{n61@r(={2DIM~3E*u>17}!m7hf zQ0{OGqvCKIOrm#yBE1XLSGz-Rdk+bkLBgij`v|KB4?wxWLyU^SBQS|R28#3vQ2s5x z#HWzd$MaJpsz&~i342|iAq=DVa(s?MJgWimtOj(rr&^Eu3Ul$xR(o6@`kDvj9`_9x zU%uSqzQuu3mG8imQ+N+1(SLv<{Qz{hziN~F5i<@hzVs6h%1!QPFxh1|{Q}A_(pQX% z!#6OAeg}&52QcybWTHL0KQR?=5t(SSu8>l_0sM7kz=3iGPGE|@GnhnE07aS-nE1_7 z+p3!i6PkL$9^KRkt9LgIC|`J5jEb-e7`6ohu`Lj&U%1xlX26stov=066=796BPf^7 zgi(>s3?|VmK#~3h)UR3XcHJ*kPjk zrAC??37SE|M%+9Is|I;Nxj{aRia~xbi538gv>-6?8+>8|{svzN^O}Cb{@cO`tNKMi zxqeZMiheOLi53Tnv;;8m8$8!f>J?QId8&_pWWu)FQV7E*{uNakhY~FV6lqzY{&s2& zuREr+MJL*ATMlt~)w~WpaG=zoCz!HwFEIR~6%fB@1=QbDwc+)}gr=Ub$<_~H^^*95 z@=FqcQ4tOVlV}i7q`^S_!nKCih$&4vVQ(!#Sd}(`a_JC^igbA}Y#9V%%OFs{X0_q1 zhzU(NmHe$V|4qY62&=-CLAh`hjEZnoFo{+JinKaVUxx|(sN87OK!RrAsu|QoST(2x z$_;8`R1E5X;ae{tzV!m?>oCE9*I|9kYx>zVeGy?*zX2%MZ-`ORZv-aM#z2ua0qW~8 z>4rB9d8&_pWIn|QjYd<1;S*nn&2WfEIv^hDfco31HM}h`r7gOMEG9R+tq_-2&Fio= z4wO1<1E#FJEto{x0Y%y#sK2Lb!`lH9ntB=Sl7u6yUXs5-`6cOyQ4x*+!>?cg@hezB z{lc||w=79VQs? zI<#P3)32rJTM<_Ehk|nbVHg$tC@_f*2a0qAP+x~hH@qW}r~3FuHdK62hocaNPkbGY z#vvZ*fOw<>ChjosPOE*LKMr%+s+-Abav_dKTwXRW#0fZ13UMNsvh+z{5}gbb=@ek% z)P49j$}h?cjEeG1Fp16rigY&6Vd+}KI|p-`cBG~~7hzR< z9w^tIk5SQH0EXYr0^+x`fDWrx8{Wm3(UhsCyaZuYc_}DYUWQRoUJfSF6+n@$1SW2H zaXlOw-c`uZ9Qtbxs}WWm)_`({wHOtLbzsHxKfl`&bz?4(i4JOe&K#}eRI^18i;oXNB2N!?3 zp9kfJ_W+peGMpX+;(IuLZ>KYXK9NJT{5w8{S7q&c0l%`fo5Q`ftIo4H1ZKh`_{e@LWHsSJXeqQ+@m+6E@L) zKo~ypuc(hWl;|g*NIwJhw^M6)zhFvRbfUepUlEsA&Fkes9`ycsc}2`6lj&4jQjoEel0XThil{{<#dH=szf z0`+y6(2vTEMm8j91_}FOvm>k;*(>gzD+hPMdvR3HDygk7*j5r$8E9TvkO9_fI1qyy@2 zr`GV6#FVz^M4MkrAug|)*I{WKD0Nr{Oj&tZFp0VYMOqH1zo%-$>wyVPJq5lDR6Z^| z5mqmW7bw3Z-WU~OA25mf0!8Wv)Gu6Xc>OV@NvGGO0}xiF13|fT5Jp8h7)+u@phyW& zzh<@JHDN*%cGHAI5LSiDgL2^t7!~1AF#K%{ApSN6P+x}${is}rm64zs5Fre7OjRlg1>*RP9F(XR)FU-<&!SH6Jy zI!wCZZGb%0$3L=+;)4#OA;R#9ufs+-#3LOLk95Gq9R}WMwT3qgbK0uCWHq_rZHlOI}bDDOjrri-?RXYNdYj?t^Xh(ucv@=koU4Ra&RvX@~n9-DLXv*CX zR+YPha^)Tv6=gG+L@7|DJ%NcEUR)1{hF3y{<{)Ygy%1I%dV_L@J{T2;zF_zh8bJIB z4WPc-6FKl|9DoGPpqXYc5Mk9|5GXeoj8QQd0wz%lP^4C1;)Yi~g`_^7hayom@{eq% z7@-`8Aq=DVa*V| z@uth=loiRR_|TPd%Btj3eCcXAWsR2d%Ub;CS~+E%mh#J5{ONi*Wkd2Q0d%9BvMKqL zKpHKlY}Qh+mh`%-i>=#R#wc+(wn&Q2}o*Y)_& zI623r<@~xHU&`bhd*U3I#OLKjcgY#M6KCko3Y%r<9yw?4Z*n~8J~?N!k#mmzCdZc^lXH%1Ilr#QkDicoPHH*7uE(FAl52Gp;=rcLz`EPQ3=?gjMrIz#Sdi>}sIp?*O z^Xq#2=^Huc?Qe1d=sP**{cmyt=|6JL2QA0(p5g~p9`vJ}^hrx{yr)R=q@U%aFTYFj zqF?2tZ@)|Orr+hHA6ilpYx1E#0gxUw3uJ*AeUuwYgAnKGjNmpk+)LG6*kvKzF zXZRTrO)2N3`b~}}O)cl7(Q+K`s}Jfdt(@bcu++o~UbM8FP)19@G7~3w)3S1cdvXar zw49vakz9f=^^_C5v;>DW_)%{;!ADDQSc5|6ex5Z|3?JG+&S;oyhA(X-XEfF_9M|DTo5&gb*++ZF*PX3DZ7OFpOEx2b zHkUJ6B%2XPTgn-&v<$3+KhMNpbN)8fT25)BrQm|-Q#@%~Ii+3lDPFX_oYEor6mJ?X zr~Iv@{IV7w+EGr4&{BR`i!bdYr$i>7;zv8nDP0n$Bq}s-+Et$KmUuqVrRTj^cR8a+ zvKgM#EN4(HLw|#LAJ$XOkhBbaq46%Pmz>c%*$f}rN6zS*Y=$rGCuj86G91_8M+e9m z1GNmtb@n3fGXm%kIm41{Mj*Ay8AG)U{mqs8u3>UYl$N5uxpLPvTuvF0e2Nzx zDW{A|KE<1kmQ%)PDZi}6hmMs~#%U?Pti_j(ms2JrpW;U+$|;i)rz9#gA39l{pOSb! zQK9i>Y^t0wE!hlDI$h3~p=IcAFmJ?W${Dk?4E+t}ZP;u%V@|ReK6I{}F)!H+UpimT zSfFJ%uEUQmlrt7-8IJ4lr;Fu`CCO$4(4}(5vSc#?>2f(^g_fbexpL#RQchW=rRZ<2 z+;**&Q`RJ(;zifWDeIC?@uutLlnq+SFKh9k8|9QuTFNhL@uksn%I4%#{OA@rWox38 zUOn5n*Q=R2&FlNeD#S(z0{^#Ovi~0>Hz8FL{~x4s9#uk}@P9$t-RM<7w~MZ(YW-TX zlI_Cn8y*g0-x~j9S3C}~$>9Y}P3E_@o#_*4>RA4SZT_&;1KOZ8{+XD8`s_E=LSxTF1S*FKzC{c?Q zu6*BCxLJMj{Lhkd74>8~Z&Dwy)dLgQY7IV&DGw9{Z3#)by}tBUAqFX~^N!i^;$YvzY7K z87!aKm30a)U|ikryz%qb9qjEWT=Od}0t#nH&$Zu@|8Yl~m4I-(|bOY;DWad=6Q;Exl<)bRH>zzf9bCsQoM~&Ew9ki3n48%&uhlJ^)WJ8V`;CIE%hq&kL10_* zYL|;?;RCBF(}aPh%u^c04J-bM6#8D0lpZpZ-K>$#h(7}p7f^qJanbJm^l5ts&GwDeW zR_1$FTZa76WaVY+j&I*On5LAgWb&D9GCB41w0m?&YhP5+)BfSh<)E8W_Q%{lb&&ih z=xNJXt*AZc%apbop;dy zKYbUH=LG~7eD*rocC(kg$I!{-p|~NS%Muq7yX&NFcI$~Yp`mDxu;HHpwYB8^0&5J>AOOq;%$PY)AX=q(k%s5?<2N^e}r`dwS={VDG?TEOg&% zR^-AhTlo!>+4Gt?*j>Xr();cNHo8$STc%wbw_gZvP7bf0!CH)NVcJ`~ge`9TS8{z} zWmCY`=;&vb{`I zQ~7)|Nc4oy!7XyUW}$Beu;wI+Ma34jXFBh0zjt_z|EyH|$UVa>mVTCr%^DbN@93G) zeyCYfQ!&eS7L&!w8sD$U`sUkCOlzx=Csk&VoP+C|;>7RlO4KZ}=E^QIyW})uuHCPL za-=P5-@9*_f8F)HNRx)|Yd*is2WK8XbY}@-PrkJ+3ja9m~W}5~q zBJC!fwQcHL!`5PaM{+6EDH1tj6|4NbrM+tGDb~Pvj-9O@9XqV{AmZ;A&Bk@v6xaMg zUwe`7gMwGP&SZnTr?HpKF`isL9A#>~JJ^(Aa&ddBUFpfBh>XU(t;Vx=HTJXoPTr(r zjz%Qtx|^v?pJH}@pEa@9r!Hr+`yFJdn})F!x3&aN%i7Q6@vOe-S&{Un*A+Ul^CMjC z>mPnHR^IlO4GGU--*7*O&8T0-w0qkk5**!;%&u37MUU;sdd$d7TKHUxectvIxz@(T z*s16m_ObgU`@$2CS>K$cNe;t9+uN?)O?J01Rx^D$Q+~^RQrPHZzyCI)jifAT_Y6*F znqD)7ZOsB_)8F$v?BjE^jceDSU_h$%Rg50P>X4IeBiOu-rO3?u3vD%uZ!l&nzK> zc}?Z>i2Nqr7^RtXKl=g`0XN1Qq<;PO{vY=3EZKzN(WF_~69-S(w!lkEd{ zj`DBYh&u_cC|&-QBt=hnlMlKgM7pQ~AgvPqr~K3Gfs-%sfD(n$8D^Jmhbfs<)pc&=R; z&4uj~H+8k!n)uld7N}@%*r>C;WwuiGVli{<#$EO8t{11+?Lp&MpIKwGJQ z{<A287_^2T4+#yW=dR-{I@4V1t`zhg0s(C^w!#%<0^;zM0^Buys zvD<}7hO0u2usGCD>NIUG-u&?&){lz%zpsBMr@?P^7Hf{eg)Myj zJ)1ut@E_Li{U__kum1@DKmX6`6f4TlXf3kkxAvgj2!+kdwDz}+G1M+{9e;>>y z_$2+h^Q{u<@qd2Z#lK(9jY}uozP!-d@NKvF=EH z&|=#@!s;_OjW9B_gf*a3UQ1N|an`nYjkd}bXc4M=S>SjG%Kc^+no|4wB|2P|bW@Q!*-=Nl^Q3mVV zc?X5z={6f)7jZXKjyi2Ax1gZ)ZGo)9&6yi5KDVn15#d{`(fy_h=iaoo)Iat>_>^a- zHGFQkrP#Ez1}e3)mc72qa>hNMwc3sjR+l%Ggg(}^)sVA-82pW$hh0amwE zeXa9-4hrKpr!wr^m)5d(mbbNCo<`P{<(>&$a%4C3T|3S&ead7ZsIgVhVHK`3)wzwupYW*vPwG&S|e*U6KdTaZs7$= zCUTo$uWJTt4Wgk#)m@X^DC^4y3Mu(#R!J4HL40Pk68^Doa!D7m8TVo^2~Mm^)fHSnsS*TIwV$-?77bF=Ve$w#O=qsa$d4M#ON-_0e+;Qz!4R zjNY?In9?J{I)$vUbg`wg=9*F5TBpcnYx%g&)(%Sp3{P*DwE8bAFQj-^)6jqPCt-ii zGeUH=CPI_;dxR`qsPL?E7Gd_MMi$$l!$P0)8w>{qKNhmjsb?)S)!Evq;1+8RZ&%C7 z!UYVuooid_wpng?zHg{uUyTEn`|Z+LdN=ANtX}D4*!|T{csF68Vd_3#OM$>DmauIl ztfO2T8wxyID_lD~NO+QGvCyVfdCSa?FNNAwCRmGBu43rYy`lB(L?5dqbt&ul${vRH zb=F$a>@^ykhFA>qa@R2Y<@Hp!TYjHqY8#88r}e9)?>vk(A6f?N+Afre$s}a6j<@(^ zKW~YgyvRDYcw>u{d4q8K{v_+rM=7j**L1ZOaGhrTlx4obyIZ92RO%+w7#M2^>0+}C z2v}=ir|Vfdm#ZX{%Du*NsbFzoU(b=&4~Mr{Uu769IFEa4xl(Stq1AMYp~>;V)|@eCSc4K18n==Zd)Fz8uUi<^+wdg=I6!5rvjU7BgMwRGE0f-Q8h;q8u; z*4PI@)r&W(CX1IvFfY zo$^>7mAGapb0pF*G+MOwZ@0)YR0^|>t@1*s|FWN@{9sS(usWTEBBw&EfydWd_7?HC z)`>iBF{W!^S=^|eu=9KkVL*f^49i|hupOIbDLy0El4hjQn&H@B!RKiaD=RR;ICY{{Dxm>?{J9nX`8Ch8li`z}X!taHvc=eBt0CXpErx05M_U#d(i?IW zaJ62JIcMqHE~}7ZNQ@8=GDz6kX0>6=*N)agCwCgoziMvv=$PJmIH-+bg>zt59<&7ll9L){RsY| z;UCnG59%j3>SrwKX9nsgAL{3xT0aHU`dN?q>5KZwhWa^<`YDF`*^2s^i~2FE^>d}U zlmGu-e{#=%c>MT-=RfDO)lFAt%qDl&7cq@(Z?V_x9mVpe-EZ4r$Zmi3axKf&@eZ3k zv!LDOU6-IVm4@2$y>7@d9f@V{yx%dO#_o1$@)7o+SS$M!V-d1aa*?0^o);?M`oGto z-1DF3A3gv5|Lonl)_?EcJ^$$W@Bd%_?uqBNKkwh`{2Welji1VdFWboAbFei(X=$_nhx`9W&u<;aU$Sve7oQL9;%nh|&prW3eujABnak&2eTEcGHO%z6 zM;ZI{7w+~-&xV@(I&?REIy2OCGG9S^=K*EyrN6c}E$?5(?vx_ZRG@H_Y5HUjJF8UI zUiEQl`{?`@)9JEa_BVBgm<|{%rk+RL<=0PaquiVR!}Y^+-2eUO4_9k+HSz1WeZ-U$ z|Ka-KGx*siM2FlgGPpsXT66qTjjoFcITcf|L0m{ zSI^F*_`~)rML<7t_E;xU?PWJsWq5BAaQbgDx_x_=&#R04^S~NGo^Sud_4}jG-{nn$ z<(l_jf4T90xPJJ|{CC$c@Ny-~nRKy2rZ(O}(2w21!$#;sye0@SnZ^jM*7p_igijR= zgX;;=LOH>fs&u_f_{lVw=pMQR26?~JthIk9~{PSyg=Gii`A;p2X z636e_`jomvGSz8IydP9Et_+;aVgrqA^~ek?`uTCzzDh=B9@d5o>~xEG))m>bNejr< z;F09mx`k}zoo_6n<8gK=UD>c1^qg*9Zg%1cC)In47sPUp55`4L#+cWL-F0@at1qDQ2hy;yl=8GldH(~ ze|ttiM?Am zX}#em883vANzDb8CFeOZ-A^RlyI5Jq>mym3j;Za(+HHw5PVCCs8e-YPJA267gBjTR zhV9vj#>a?Ir;Kfva~#`<@5SclT};YMc*f>uJH{p+b!NVAZj-vsL1az8*UUN7k+@Ur zoN-XEWvtGSUBN>SOl0-z>^C0w7(wO@{=@>TEm?z8E6I(?8CkwKJDZxiLR{8^w@AgT zW7snPz3k5VTI8>Sd5!1d*0N@&^4h-C{cb!kYAcb7y(Z+~OpJ(!)im6jdu63rI=h+xLc-GWSg?iR^0%afh*C+WlmC zE*o*J;I|{{?tbFuUzzw{&(DUwPf7X(J|>$+u85mHNnkZo1lS*zyviyS%FQm~JNDUA zdNHx-0ArfQg2_~61!?eg2MIm=w=K#ehbeMb7E|+j!DL-l7gi$I0^821@7d~QPspd$ zUCHXzn^?c4&h{5IJZ#3@FO7#5jb`a1-jRBl=M&@mb++p_kJ&Qs_hTC`XC!Gdtz$(N z__32aFB(7Fw;SIS{A{}zyD4t#*IT6R{V4VzM?LcJYG>xWZw~SG@?p2z?jrRpamJgg zb_SL|;ZFMdE{NN;w^eWv8pp~Os$jf7YX`e?du`m#%5G$S?iIu@W*&3y(7-m+(1|=e z6KFeA;hnACsmyHFy>CXR#hz^L!BFG)rU%HtI+@ATy`yYi3q#4d7%Qnd$jUBW7|aUg z*hJ1&PHEaY5PLx)zp>997m^>@7Las3OR;gerjWw#Vo8^;2aKj|S?!;zr?a2#|BM|@ zy^*c@*oJkvb%o7z%}#>zY$H=TMYC?@qsXQ#b9U@*vn%e|?n7i`-gUMWrVeDp62Ua{ z=+U4Ib*I=CE!=HPO~;VS5yjcJZGBiK(*t5_wwp-#3fO;^X~RyYbYm;#iM9g|F0iq0 zJJ_DL^I>lqf3an3=WD-T?6UE!-HUZO?__uHu$)!Gk6U~0yAbpx#RZb3fs?JKJ&rWn zf0cc?R?uFpK|6A!&$-}-9uvurrWeS8t1HQgUe%aiqk$y<_0A-u!f6tC_qwf%=i{LF z9~Y43y}OW<**B0X^LmoHYj=`n&DO97*GsV6WFGt6!p?RT6HIf$`^3#DmWi3?h1dh! zLRg`T2ib(cnxx3OuWWZuH&e-w9ptNj5czBUOg4RYX49sDCCR;19a!m{&h`{3o!OhT z&ZfXeP1#ki%B)WHyrw+nHKfG*-XU8}>7YC#lil3CX{6 zf-S7F7a0__pJcu7%UqUkw)xLa!TNl#$-UFHmNN(b@1Or<6-bhwTWEI!|NL_+{^vif zX`XF+<_zC{`@+L5C)P&${Ikj5_i5K7+YWUje$LUOx8z?}*LMdY+wT{gv8C_FkZslO zfA@X8DAhLOLho(2R~FcIx^U5LVLL|p7TkWqw@tHYzPy*YKYMhRfB*T9R{9S<|0g-u z51C%KaL9q)xkEN*Djw3YmQzT*$;Co?Kg}F6@3u>bSf*4+gL8R9mM;2kN;ASEWU(!C zh`4OOX>@3Tkmtj*hZvux2sunMgk;%KE~Mt)HADFNeJ35+`0v+`_W2LiU(RI#`~113 zJ@B2cz3}Ym_KUAFm{#o@W%ub{ndS7J5>)GM9aiyUc6*Umf0H5R7*?=&JNw?TKJ4?( z^v0YX0ruI}N~UKe^T_`7Z_aLnB{#TX3dJ% zQ1jz#T7{X+^XWSI`Q_N0x6A+i`q6&>!TJyM&tf|GYAR_R9%NcII)h2NRL|6Jb0$+r zu?M8Qbu20NrKsuU^Nu7}xzwf%P9;n|z4sGS@h#-Xk+i01gD#Pw6W5dC-E*1Tx?Lev z9;TK1-(j~b8~*qD-30ffUe9RdOZ~yid2Ak!kim;;hn%cjF=X|TJR!G-)DG!utQS)7 zNu?0O5s#2L`~5>2u1_D*c6o`A4Wvs*^Pmr=GFjS$eBNvd`72+YkdN=vg{&ynIwU`H z52^Mry-|J7NfA{*c{TKh=`gbq<*y&&N?_;x!W8UA#kaJfT89!uqB|C48 zU}cY`XQlmPjb(bABL!;?4fcx3W^#FDAe~Bfvu(Q{&VIO9*b1_dgiqTFyj5;oy}|V31gQRZ*4E?{lYTmX9 z14kKaEIwqb{ccoTxspqa_C2Xd^Bl#=xamKQBl6w|TKo52W38_@*qqaGtU^;83+qtI z^pB6Tz46ME%rpNTV~4U=jrVfYW+C-fGLKIySiwb`jYU}@GIon-D;Ap3w#BxLZOoj@ z_@dVsv6WM$=Z0FzuYsrMJF|1CL za<)x5ma$C>o{&oyE3(4D`&rud3rJwja;#I-cxLHZnXQ|d%l_*6RaPp+9MU~>6^kBz zk*z*4Hn{oEacq6JSYzR0GuQ(6m24!tVSL(owQcsaC|1hymuZ#HT-&t+2BUZ)W$>&+ zC)m1`H*IT&?j$cV_hRAEr)`xpgxDTM-eVt|W?-N44P=9FrTyHBdc^jo zrt$mHMXdDns8GlkJMl&+hr|GUn`m$u?rke6nEnDB`lt&R(auZFCa{5?hQp*l;ccboY;&nq!COjs8+(6wWMt0=vqL||u?LnRYwGvD8+IWh%ksVvSy-_g z8I#?M><@m=<~E5Udqcxmik&lTw^EH|`J$?j9{VP;i6cj_n6$Zx-}{NgH|_;{^msD6 zJ?1jYb!rhi6TO&>+>q1QXFN6f4$oq``t35iI%q6uT=NR+dDl+PEIP;{wj5!t-}GbI zXYOP^!I?}q3d|yLu7_FqA;*l5E~PT{AHR-tjl9b$WdBTrwTI(!o)pMk|N8j3!d{ZA zYBcko|H2r1>H`(kRuaW8gIplt;W$sNU{&oNEg%AAy(7*TlP}Ik1d=I~F+duiPxwv*) zx)p zr%io39Io!?{iwWOt(%Ru^8PpWbK$=K{`3FOety?)X|a%J6GK8yOde_qxoQeI%KSo> zZx=!`Z1xLjbF*qlipxGB2M-uS7TqlqGO1#4$i6KhA>zxxkkdsAg_uwIgp^q~hF7|W(9A_nc&IZ5! zxXSkIr>lKja8BFgVXxV$yakPi-c7YNUHF3CsGowB{5gtkNZ-pARVyRQlkGC=YtG5q z&Ty9d-{YAZ?E3fX$MfJHtUp^wtDqw}C$NU8>jVWBp2|K2&SS;iT(AWd@L3yKDM2bzSw$YoyX|iC|2?CjkwShp)9j4r_srBo9!|Nu+Zg$<@Il?#H#%J z_5a!PU)`(eO#j@TN_xC?A_E#f!uKcRNTo8jjXBC5C3%b6NKBngeFZq~*4Agnn5>mc3j{Y7YA;KaV&2lI8#K{ateX@1H&Y{r~HK z@!a+=`rmx}Ui!@mzPQs&j|4VpJIWaL*_*vtv={wqN49+bRMvm5l{DO)!T8Sd#yItQ z5z?LJW_^y;z|T`cSe`+-Sl(sZZMWA&+RnTk8MmkWsNhZIo@_bqG(0HJs?gv$@6#LG zEiMyu=yMkKD&_jPqxW7Kb9LxUrmYiMr8%WZm0ppyR2#&&)nNy1$6uFb-bd21w--;_ zN~9QLdt0c4E&sDoaVd+G-j?xcKyYCA;h>6pcLp!%n$EboWRp0TGUaWf{uviMw5Sv7 z-gUO^{Uc|#)jBdbb)7lJi)&UGSG@mVyx)9z-0I!qjq|7UF+Mo4EN*eFNpXGqEZdgh zP|mnJZ8Hb^o-7z_8d=6z)-6Zey3gO@E}ax@VyW{xEe(4cBP;Lzf2_UvJ63NO_iqT1 z6e)#7kql|T#kKc3&r5?LB{CJFK~W-!lvG5N44Kkks7Qzg%Cz@Bw@OK+f#y;vN@-Lm zjo<6jaeRM!KF|Hseg6ZFW$(@TdcW8ETnjEYUVsQrg8OAv#(lXwh-|;x#=S3y<|dm+ zz`M{BK&Bd^C5OTUk4|(`TQUdGcJ&!l$FieTcJY3=RGPvS8BPYzhHfV8P$Sp-!kDaI zJc1o9s|@QR^;oCeRB$m(hMM9|+Qfee%$jHja4HRE+Sdv?(hX>#qauAG?GU@(Z!4Yp z&76CC+Zn6^Z*r0;DyX+ahb1!%P>x{)ExP}j+Y<4T>nR#Q+}95vkNdN^kR}tZso@zD zTkwGPIsD4JLzrdxY*PTOa=?IIyFvy9mL`$YMfGqJpQk_H%A#HXf-@8< zlFxlVxvC+SWa#-JMCNHF*LnOI>ry0QC&}4CPwzNE^^B)X?~P}cTCYa4=F5v=Rjv!$ zUnb9x337z3$OnAta;=n@jjRTphIP9wxmamQc$#g_?&}yvT(-1weY*y6ADn)$vlQ;L zwGEZ#k+$2Y_4Xm`?UH)ddQ2vBu4WT$@P&mNt)JPgsk~Y+0cUvE1J6)6MlY!%_hILJ1d@sJDZu`g5SLZU2tJlWT_TAiB z9ZRm`*M6bf$7J-YI+)Izb(D=~=WvS>m5G(T1liIyh|r1*XDz0qWhP+%(`3%K^ z5|@FsaS{{Qdwe(>z2Y|KT_?xZou!axPZ_XsO~mZz5Y+E94GDMGLT!*NiKw|k2L#v= z-#`tbt@nb{H9y5IkOb~v<5BiT(U2->Vn$8QPiBJWoTerXIY<9occxbTfGzZ^ykPGs zvRuRADO|6171toSm|LR%mcDbNjcv;J0SWyMaC{OBXw6jUn0$*~b!rmy-H$fYPnV-3 z#;l+YeD+};P0p{ix_F(PlqbjOKG&hox9LIohn?_2YY)s-X`<6s_pm>oPUkfI61ejn zmYlYs3Fa5}R^B_VLvQ=KvDQULhO+lz{CxGuATaMAs;;`qOA+xVHejqEcW7dlrm`ra+NNZRO|-ij3*% zL(Gg_7GUQ#62^(|6V*nC5=nn~R1=lYopSpK^EJC+WPl9NWHpy>qd_9#RY;43HkmN5 znrmCTiQ8Y6&kRgmLoc;_X};%s8?!b@rutHX2J>v|6zFxy;ij$sMs?)Ju`_l^g16#M z7+ARsCaZm;9cmJ}?wfktp49#z=ONB@BsVdLl(4@DkEX99yeLbf;~Q20F(0jx#LHGXt@9276vzS z_p4GkrJ8Q``i6PT0Qs=f#`@0mnN`b}jGUjWMZyO*dzThR2ET-pD;(Iq3|E+^afh+@ zjc3`Ki=5#DWwOt|hnrA(7P`Hwx%1b@aX$~cqt1zz$nVx9v?nX23NX$;*;CkCi<6^pMp0bwUa>_u#gu^4M=KR`kw$taUxuy2|Dhb;v!M%+Q=7YmS&F$8W zH=ph=Cs42`G~aW4h(*kv;pVi~z^ZXlAAbASK1D;M{xiS)i+?A^-#_v1R>ld_OC%&oHrVk>59{Lif|#=<8i^Ok;Ex4 z^i>vq^VcE8QSpCY|DWammP;w|yNvos$$)KK)Zv%R6yLpEh3_wtsyu*S?XL`~b;+E@ z<)Rgd!jVcog2O_g>!kk<98NoxBtm2e_y{? z5B>qapDj4ay`^_?U;XX59aEmr`<|a=b$3kRyC3Z2L;@+wK8Of-b^l7 z-U%WOQ=G}kDO}&=T&_<#p3_=}I1STXT)y4O-&vG7D{jEw*Z*hwZ_B+Rq0ZNR!iWjM zmZGgw1=(*MEz_FKg-W^c!q53LEk#u#%Y6wk!mxl0;dDQ1%hGx^3;!w|%NGYX3)5dX z3abrv3On{_T9%r+TN?W>wfyJ*5r5?BjlZw|&-CB_jNeVgy6rFcJ@RfFBcB;dy<6!F zT^A;CFUwM}vbP0`_8CSV2V!KiynL%}bdCKMqmcg1xIj=pIguigcnXO(?FvcO@jh-6_0! zq?pMPpBd_F<>0NyLQrnetM&M3z{s@BWR!E4!|iWAkT*>Ly1y9VjheNrFl;6}GagX6 zd>9=bYsaYR7BfTU25?%5x~#hXHfB>*Jo`!~h8|j+3(q$h!ia$p)PhUvSW!a}b6K?j z);djrF=vD5rWL8om{T3pk-BMc_}F~z46=swpU;I-%pvN!lqJ_l$3e)*8s^HnBBtfQ zN^t9nWg9hDf<&$db+hjnW$WwAXkmX)^%Oz<7%v#!HG;Z-=QVR6XBk{(-msPCCh)#; z4^{N$h|ra*->Lvlk2byrunS^ z|K&o6c(MrUj#xunNHg87kPQK=6~TYAAH)t>1J+}AvsvLE=$AU{p(`p7)CX9>$q99! zYhea0uVSIyXdBeGCqeS^t?+dCNI3IN5<+t=YA<_dP&uJvpmswC`$P%D(*7oD<9i8V z;tmnl6SEcy!{665xj(9nteFeDE(q8+H&#Q0r!zC)QlT(-evD=OgJ=jB^J7Pb5)`=^ z)tsiIis^~uE$XR@6>cHaw* z-%n!}9Y|q1JY?9$b{`lP=?$Vx8qy0k!kVXEkba31&N4d-QzmYKf}-_crMDWUr4_Qv zU#h{E-E)Dx?gI{Wd+GSt9C*_Z0b=Ccs420O!;yOUsJn=jl{bAT^q zlW1d|^H90;I(>S|N^l5U0oy+j)~R0wR@S*g?7^k*q{R!!mjm#wKrEe<5}@zVRxqBJ z0NWh=;9ZX=3_;SMK6F03Yh4fDNBPq=7os3OOcg$D2mmT!6$A~-VLXFO>2~XxAd?ab z#oayh&;|{7Q7;Q8z9hnnMTuauE)tG!SOT}c6w`_d33Ri*JDhD>021-a(Dy79Hq`*# zIwTI941*!V_W-L_G?(@=nhlkg9O2fW;q*ZKi4I7YgJb)*!|R99kUT#TKA290M+O#f z^W7sluQ!pI60oOcdFVV?_URH^cV{uY7qT^`JIaNJ)E96I?bgEB%bAq1_hq4tYY4~< zjG<~L`Gfl-N%nh0p8!qPVJhtBz+mY)kkV)jQ`!^hT>o^c<#RK9(h;zoI}U>S)VIvV zs1eM}A@S^M+f+EUgl2uhY-sYm1g`u{V;&z41P`xPYGYO;bL4zG6Yk!@hTgJ(^5tc) zMs8#6Xwawl3m8`D*|LK-FM!JawM=fe5;LmZn)`TkHdr+rX4)fqn7o`Na6D)UwQb}I zP&n7ZR9jh5!+$9=u6I|$q3A^rxyc@e&5dO`T9oMLZ^y#ZQ-itua{#e=rPTXn`P6{V z<2jN3PAGi3f=OLEmer892VKiNinOhSD;o=HJ=MlAQ*Z94=F5hIt!5;=n>`y;?~FKt+bZ#hlv| zDVPUZMRJgJFdW7S{2}S^boRB;LwXjS0{X{&Kx>mW=w@a@i6DheQwf9dmv?}2!*V!H z27$G=1WdV?MGw1_A)G(0rB>bnLu!H^MNd;{!+F^iKJi(O{i&Tp&1=~Na~7m-2@ z*y;<^&^&teqYZ%GVTx;;!MLhc)S6o?gshejP!ZDb&v|*4lw|xhznhBvy8nXTZ+Bj) z+-}=rW~1gK_|m3X`Ea2`Wxzadv%$N%E7LDtG8b<4u6j}wQYkxjMwQk4p;aj@H%@oS zc$zm?9~Gz!&Z&IYaip?)p-q*#Lb>QS-iPf1jgX$HF&Z9{i`A;Ku}wn~e&LjaRjsEW1?g}!f7K+k$ioadl^LKY zy&uT=cnYq~pY)r5m+2X_{eAs^mf!F`c8l#P8^w(+&Sc5Z_v~%`7Phr(0zZ?#&c13X zU`I9z_;EiB`S3lOd~31>KR50qyHwVN>yGPSSL(;GYu-9DN;!_?p+J+Da_;}lk1i`! zCjEW=f0o}6U#Dejn0BT%fu{Bylwuvf++;MK`g1Su{A5xrjxky}D>=oDeq4>(GHxEX zlrvPl$LxHf#hOMQWd`i)V+8uw7$lP6TvhzJh#eo9f4-k7b5a-n=lcIe|B3POPxPNr zg9@MjSlkyj*2QC<(Y%D(2!2NTEi`em3!Qv(pr~R<9eRC#8;Qw!Lq4i^lG!mcutoA^ zxPJXES-m-iY+I?%OC0h-;|Ja*IcXi#KlK~!-YEH>>;D)1XZC0M@2!W5<+y4|%LsMG zB05yca`Qq3%k2fV!t}Oh78{&LSxRqWg(IS>g(noR3so&HiEn-mvTPkAWw{_r(=yKF zr^VXZ4vV^)7~%Zww}qjxUo8Hq|4n5Z^Y``tng08q@w=H=xBUgbmps|Y=$)5iejRV1 zhqU}+minr&ovN$hoY5nu@x}qB@j|$mvlfHXT`>nZhl%~hVN5aqkXejw6E>bjs$1Zr3oE&1|V)^1O3h%HjoRf#fp3E_aJ%BRbG^%mt86+AUYf6l?_zDyEE^3X zwQ=mYEpypexnkCL&R%wF@mAL9?MnEj>k2YqQDXmiE4%H@1~xayg}x;h&U)?I&2HOc z0dXUDvFw2&cIR9l$baktL+v)g!J1I0d$FB;;OfY(f1J&ZNIc9gIJA!S*9iiRQYQ%N zieWRk6EN;!7PK|ivi1)O;0l!r1GUTP@S2_QwQvu(o*74%33oG7>j%;I&Q79jb912o z&MtWOWe>0i^FX$6D-3AAOJDNcz`C)SpyC?MDcEl2;;OB=+Tt`W`qo^o@5o8Z6K`X= zYbcmg7&(<`nXSZFxSyf?HN9$8^|x}>I}AC>VJBCxJd`uk^x?*hO{NYE^kqB*QC#@O zW=15q$Rv5)qa}hrF{QJ$SqTGwP{i}t(a)vX4_1CKW%pXBDT;>knK9s1Fo+E_I9#hY zB9-0KG=+5yzRc_wKZll;(a`gzl=;E(Oxe~;4B0gi+C-z+YhK07N4uS%{9J*(Fn298 zEoBq9x(7fu9Sv&?SHj`1b&SHvXO@lAW!duE*O-(sPniR6Y(V<(Hh74Q8RemiS>x0u zCg#fh>W$x*u=z{uS-t%eK{$5}J7!N98>Hg^ZVHQGiPL8AdhZX}QKoFm!Og5oO)h(F zTr3;=MT>2IJqM>mPlP z7Tz#sKgZJSm22yvS*#ZiIT^F(NBBXtvloOV`9l1wIB1F<#5OH5WmA`IW>1$bU{8*- zVa~su4^OuS0jHtGx_+I=X#`nuAxk!sbfYrXZTVL2T*+Q;;tdyWL}wazb94^3FguUi zB-qTQXl~;!ANJ=A@43*wd=t5I!>zg8{inFEk{Ci}#Bu%l`#2TDQS82iR_5)U9(w)A zpUkznHB4IhCJ3x@V<(E)e1}gU9CC?(y2KbbdB_(6#wIb3V@@**;&s?0ukXxgEhTp7 z{@Gx@VHKoyWHQZkDqE1dmaTT&PM3}qv3(`0*mgAmBpIAw&@WW3u-{PBx zR$p5aV@EdG z?vlYx5)}N-Z|uACTJZPv|5<*+{-!xsxT2r6GU#C2&2+i*ePg*}y~gZ}<~@{@ypUTc zK-}XkI$ZEuHMSydGWU{qhlJ4r4r0e}x!s0b={+s>et-^_Z#t44a7FjG|L*i;)}_C% z|IhSa@csdm6IUthm|rM#9o8q*`|wHlH9}-*`6$q`NqG>pH%Ojp%Iy*ws?M-9=#Zjr zeLpC)&>ldY?0YW!X7gQmzglD&=-VxnETSz3PWmML=lfsuDChm(*Z*hw?|;X?V%_!^ z{QLUAT39mL3-n#B;6$@898#SN#cCC_MSLLp#nTEdqIu9ic@AuKZKP-UZ-LmoQ-Pgl z3@%!$!0*?5xW3^5eJW=KeCSW5ojRRnyqL*8RKINz2G8KxgezQ< z_qE!JzyRiwnk)12-eoW&>a0O$6Ans`js8 z);T_6zK+Xdmk%9AdxV@}-V|MfyPxiH?p4av({4$&6fa{1%`Gr}YBtq4)`Fc`o=MS% zr?4$QLxmfCuE3gK*33lHM{qY;f!*Wk297&0oV}z84MlSxIno)1yn8@Tu)4_JNcV*G zjk6$0dM@lq$fMK!f=kB0oek^1+B+rEjdwtM{SDV)MDq=QLpF3B>tunW~@H@h-tCV}p@ zRm_PIH(+o0IOb*V`P#0jO6-JPiR=aW*jn+76rtDk7f`(D23O`5%G{W^g$kmFQklTo*kqu_L`EMs*(leG)ZVy%>7Ynx1im~iE1utB$;Yl(DX=3T|C>8LAA5xNNJP4P9~ zMoDs>gIyRUNkhhKc?#t_{v2elT}v5Hd;(Xutzds_ngZP;r$Xp3KWO%z0wNa?TwS$_ zed5+gKZ3?FXgq0(fa34Ffm0 z!1wO~a5wG@?RYX4(g!Ed3w)=6f@_NfJy^&}$1P{WBNnk%l@{!`!Zx_~jV{_RL@Yi03luk|GG{KAGS}WYve`yG3_u~17WWu-T3qJb+RiZL*RN4QmX1us zng?+DPN{GMsbL2#u%T8RW|?aHC(Oo*28jK5jESCk7e;_KJEeOLG^fl3`tdkOaEgFi ztQE+bU8Cp!v}E<7oZ;!bRd8~=5ahmXq>m2thNTy#LVckJSRZi(+1=5wLy$lRrkKLZ zEB9%i>P66>I}0A}5$iEA|C1FVFr!u6|4`SUTb8OYS2P!c-a-M43UGs%&NgaShBwT+ zX9KF&MPT>R1Fm3k|7gt_>ZXz->|Q>b_Ff@`W+@M9ddN_A!Px+@K2~8{=EpEN=N@Ek zzsotaOEK?v45s}AdxTo0*Ff7fhtZ6PXTKcEqLy}b3eDt>2^Vd;2G#{>we`>L!q{AK zj!t}DK7U#p|DX9+RpS3R|5l7!|IPec3Fl2h8H=038E#7{baNABGcruLGw>)C^Xiju zecx4DYusRJ@0L>GqYtx$ACzWL%}16CCwcCo-gwQSw#j8s2P^goZ%O4)i<3?YKm5x3 zeg1cye)|6~KlWezJM%yD?eC;iEX}n=X67~v zbOdkPJ5T@kY9~;tuB)udiZ$=*psK9>vdy7fN^nZ=Yh}?TfAa-Kfo3a4)&1u8qp7{a z{+eIR|IEMJ9sU@9Qyq^-ztP7IWskA@_hNkL&_mp{OC3LQ9FJ$7$--Jrdf49P2p$mj z3=iFDfRFm<;lg-b94vhuzmGD+R&QFc@33Zkc&+|#eh-?O8TR+}|5<+Hes2>OB0h82UD@mnvDF~0QCn*~oYOl0E&GG9zI{qcyD&) zRNsu_qDMG#t<&R~(XXd76M_eGl_O(nij~K6!F$}frv9|r8Ih}*9Ib4|B+`~U*EoV1 z9^$~=Jvobuc)0hs|Nd)ju#pYgl7ShxKJzmHv0Qj_hSkETcUqPfyCIOv)z_^9qC($Wvmq)Bs$ zq?-r%a&tQ$wfZ!@1YDw$#e*(-Pil$N0v8<)>SuPUN@43t5f*f?s9zbj209*YaqV7;TsAm8-(Ud zN|NTQYUtW{Euxz>m4wO`k`U{iHFMiEu*Wwg@jUkw;+LbsmxLWAZ_}n?$3P=iG;t~J z8PZ5H&ZqLy&&>I&+VSLdUnTlM$e`ObS<_DVgs_CzmI z8x)7`*7%`^vKIJSs0nPCT!5aajpPSR@+Jm8YxsL_ck}vRZTUbG1>uAYHFTKYg;IVA z(aiOSkSOCZiC=BZ@67v7{KPY5mbITj7gFT0&R9L1^u-U4Ibn>t&`{n;RTfFqQ~dVF zugHPta-?i*5*qwyq~+f9T9m3W6gSyelhu9Gc=N&vvh&p#WsA{^)LW?7xD%B%=#UTlPgKbcZXj(Dab)wJJaWq5 zHqqMZidvP1Va;tG_|a%5j2!PF4ey^MgR9}Sy(ireQEO^)j*po3PHgvfg1MVcBU?CV^2$|Ie)Q8|H<{j>#^+vH&u_^a$zOcB z3G02XKzooLp6&7&mBf4`dZmDFb~amXTBgX$Ke6stWrjdX?(eHL zM{|tNgv&^xIfi&7o8ahISIPNF^ZBseRCG}5E!jCk22~x{$z8vZhUW;yz4V-yu*mm5 zy3$)pbhm5Zj~%Vtidzf$T#Lt+DZYQ<{a@yvn2Y_o|Hi-G zomR8?Xp-<+v#3^A??bg)u0r*Z1vf3e@q2DLh1TJ4$ zb3fhKvdDUNW@sDiiYgA7IjTD7WJ;o6pfTf5_yDA7v<)=h%~MCi0&Rb zCd&L;EJ`y<7Kv=_M2kyYf8U?1(759G_x1l-ej{@w$6vcQzHYtYZhqX*adnZlW9xpX zsntb)8(3#}$+Yg$&tJTA^MJaCPbKR<-I-h$=_p^fGIc`T)3a^7({aPP%AQ$ur{#3( zEE2x+7hWCsoqsR*Vv+uz>;IShh8TnXB){=L<1c|&xBUfw4ZnC4LX~IoGsssGX?>oX zxk3t?zjQ%LDm{Ei+hOi#voxH@(&n$mN3vDRr{kgH{YhMlBFRnk#cyhZQMgMlIY^3e z5C0W~EZ^}ve{}i4gLnV=`TdK25d4{b^B!$R=i5!ER$Vis>}QRpReIOa(nD_wBg!Yi z4xJ`y?V)aAjEoGemUWv_wl$&Foq0yJ54uR;M{Km@VZr7$%$~j_P*Cu(*9r<{wjN& zw!h&+mbf}`3AQY8uH1s{N+#krnis*Lbqnr^&f@OCPW&ZRgu?<(a~iG(U`x$M;^;a9 zYhAdEP9&TsKkRA<==Y%+A{88D8-WHWgc7U36U6!JJ9hW;S|q)z0=~UG%4z5(l6-dw zNQ^VVUkf+zDX$vP)Mshrw&nw_?~^P$`f@%l`~HfI%~j?r56vg%2CYCdeOkGsrpctM zQJz2k@eXpYks`KTX2dUSBOaK-;PwVLkRP6aTFMb_V`E6dXfZ z&ykogiXSx@VFjOVG)%siocSq0nR8~KO}ER4X=@w`KHSH(e(!_Yer1w8qaIz!VbDDN zQRG19W_%1M#Mt6> z$+yU^{!OHQ%zmQ&Rfj*m|0H^q|C*eV)W=h_Y|#{I7j}*MLLaFYvP(^GqCF)?(X!$s zOkB?q_1Yn5O@#_}Ziq)tb+gfV|3q$H)F^!R@iDqq`5MX{8coLSTFcMB@e2((HiyLC zeT>jcQ*wP#9hzXb1iP#*BjJ&g`T3=0Bs?b{_aFbo-5=LWN18w7{K6F=a=9mYZ*w0! z5AJ98E}lsr{&)tCYaGUwA9o{<=NYKGw3XbiF-A(dFUh-xW5_vWDjCyw2|>mga>FAD z7iT)6CWSn1NM9^^*>nLVyh$X#etHn|5hm1zyp_n->;gA2=w*Cu9lBXP*)V`iHsnwwV>Oq*B`hk-W zeCLFd#B(Pv9m30NDYE%n4m~I10!THgV0GC$$Sz+73S)B7LS=JYxO6v?UojHPpOfTs zb|>KF>J8!#7=Cnu#c0teU?3CYs2T7O(d7CIkfaM%ctqM=X-Sj#HD}tmoo{3CWAPl8j~lYk0c6ETws;_&*v*qY68$`fgmYQ4aC0PD2&*6>#*kK13WVk#VyIZ(jBsHFxgDty2`SNW2&Cwp2d2`nr)f zcJA2RUY1;%xR#&!;wCpj{WP&Z)rn>*d;(HXj-GueM%j6D(MiAE=u*o)qI-`+CZ}2v ze`*^4)XxEh6#CO&KTITgO{2*$yHe1|FvLt}G(XSv9tx*AP)x)j)Y-ohcdd~o`(kT2 zC-YAzuKqg8-mw_()G|jZO_!1MOGW;qc*on~w<*|cd?uGCI~Sk(u#J=1<$|1sPQqKQ zHgM-=tRictJLBEVPBQb;d*n0_!Rk>OsB}d!idSr)Y}WbUr+I*WYJWh&g*NnbIgVsn zFU1YwpHnoZh=k2Ni+V~`aZB9{Jo@NRUTN|ew8(5ES|=5Y@~g%oxvGz3`;cU`@xptM z+FFJz7n&iR;jd7aK$#C(Ac+KH-=O)5!_fzyesVadj~G3!AWs%4V^X{oZOH0Il0Ro* zTkQ?Fan5C`U`r&?Zw^807B!=vnpaU{mkb{6rj3dUeek4NySOoz9-(h_`goIII~Uzn zMRh2plk84aqIh>7;tU$m1WoQYe@m(sP5x{C7Kr_}|AN0~?Q<09`}PPnE-?{IvwmX! zOr=Y3@Z}G4yTy_M|J1F5m+w8Qwx75ukdU}$7QW?LRkfW$^%>RptA%e(#T)y2EhA-=F_~=D!Iyt`c>^a?uNJi74;7k*E zi+)rr7KQe16YV`>BT7BEM)W=ni;NSNi#Sg|(GK5rB6$sGk#p8IQTE&*k(A0>5v=z8 z&EM81%>O>WEBLeg#Wdl0e$Vv1{N!$)_gOoV2qyILn#1bRobyNcfjKAm=!rZ(;C=;? zj5py2^l#-qU-ahvw{Pa<)zbI{KXq`>OYJ)27yEc-jY3_M#Gtx=_)F!5Zr9&G|3AxL zl$cbK0p5|kcJD#nFRq1r|1^XTzwnAP`kcr|4~ygFPulZe1}gE_mu@2s?g_l)vYEWU zFq3a(z??MZg#-NI~ zab#p;Em>+F#+xXmaa%_>f`UsPQTCN0o>yX#%5^1v&1fzD<5Ux3+AYmTkE}w@F$(cM&gPKz8^e6qQlP*&oa1vK$YS_RK`=q%nr{m)YS5KM%u%rs2HP z<7lMv`5fA@d>ntNUc7r$*nnEHABcB`J|lhEp?Gqi4{DKmLKgfeAeHU5?8fz8c&lzJ zn>OJNxqtsD3TvK#niLF|^;>hc$D>xMNM+u3Fo*hnq$f50yU(xGQ zCsLsQjC84AA&u=@LGP_Cx^wd~Yx#OV(VSMs=6_y^x*r~c#M8wn|J_a^rBKfODpJ6v z-vda?QyZ)z7)7q%<4JbcBk0M?L4`^Kudyg{8WDW^}Mz6(XDZC)#P@LicW?cs)GUqyo=L}ZeB3JiLy zi!~)W$f!5taYzz_WOE~sThAq0*))Qz8@?M2dhSW$7I$+JGg&11s)%#X`JgK$+t8f6 zT`2WaC!!pelGkak$=-SFOuyN2P?>+2srNJ9=DE=jpxH*@!hK}M*60e~{><#1@ z5(V_}7&6H%lN?SRiQLYoqspx9`00Yjq-%pLHt*oj)f=*CVN*Ub>+~c~2K1qOVMe4` zp&YfZ8_VAk@5QdWsQ`rs9#Ws_J+QS(mY-KygPyo{pzBl;dQdij=L|!TIVFMT&0Rz8 zrQYUzCJ6D;?H3T5YmF?oPb6CzUw*OM6Ld#f+=ICE5lu}}!PzVBbA#r}T;=X8|l(}mg$4wFj@+{N#10g^*Q@Wb?9UP|wLn6|xA;sU?mq@p!`Vq6pt8n;r5wf(h zLT|#ZqU_i~@SeN`DYqnIuP}=g-D@E^C9Zh#&hezaQ4?PdnGU{9J*2DPI3#5c#rjG; zXwu@sjKc^5fA|l&5%>Se-8?_a66>b^A7O_s5Rne?+%~;)IE4mi%@c(CyYNX{7k!Q?x&3Br05M zfMXnt8@06r8|dN%>+R6)D>+Ecu!(rTl;x#dy-0@C zEx5Ny3jL6g!TsaT5}TeqoaqJ=RP=5)8QD33=qUNXR#k0&3vKFb=};j#nZ^-ifgQPUaWwCEPzDcnkjDjqPg%p~Y5dxNrzDQ* zMFT_)NVlto&JFrOPG_j|^FB}EIwXuy#_Ti5FmNeeJ=hBOCyD3NoS~6b%UBY>s+e1K zcQ{?}Jd!96t>N4?a_}?pu8yZ87vSaEJo412Lu&Ohs9RA6hd$p!;zTddsr*4?QXLSB zur@SQbsE0&{t%k*a3g7ZRY&HOmve$oiu_%FTf9U;lFt`s6I}wfpc7%rypxh6{?KZ} z|C}h|N13jL_SUKVQ(+aojH9& zpMZl5N8;(hCsBUWS90p$96s^fOLVn=6#nw^J~R{k=Un+y)1H(TE=q}Wa&p_9!g;xH~@TReI=&iKFlcjE1ct- zYUaubRlH+HDtFZdkc?##uY1V?RqGq#o0Zml`{0qhlu916{EHy_xgw&_ zu?yK;c!CDid9xqqXz{iS%9w2hgLwF6z=tOkkeT5={CNc{to&XN`gbId+_vkWE^cBc#96jT;ybImEmCJ5mJ{Zi7M13v2y7kJpHH&KhU%f zU0P#>%MB~J#M2?fVU#?+yiO7Q_%ed89zj3Q?o+u<0s;8&ljPrVh8MKu1K=D zaTsdXA=P(ge0t;=GSPB6+4FijA7(0v=5F1K-uar~&k}`5qv|=@AL@=acHcri@2`;! zhF1JVFT~HJTbSlUCa6Z|8q(|JNjiN8ogXFMf-8oCXut$~HHCGeQ@?S_e#Q}exQ9&Zlhwulqd(rS_1DrPg3O7cv ziCmVDC0~vU&|>wmmdlU< zukMjOo$mY$uLtB-_YUOwED1acl<@5Ai>TqwS5!3vi}zh5KuLltSF_ZZ52`po?C;X} zgzh&qZj~Yqnm!q4yjzFw85!dnVo&%@`A4ob3G+cW1BhZ*FNY_Id%G*nA{nz1^yqj2 znQ-z6yc^~NC$A*(RDS~Zuq27Ed+`!gZxG?t67qcayzzYb^u1h}nF8LG&_K329U|hj zV8l;r54pYZAR01tBJ_st<_0BACbe2woXM&W&^|2~zxX;D1^Dn-0l-`_-6fL z(l|lDtGt`VQ;z0ji*Fj4|JoKq%p@$>ppRXQEXkJ)E!+`eO>$x<@>f6D@}X|qh(wD! z`6-@{s}Yt#oNC%oc;#erPWd&LGSQh{9Hokb=ADP=>cKej?iw=2#h>%!v~c;TGBkNz zCz^NqFxq!}tJrTXLeFnxB8i@($mdlhxA50Ce2DeLeGf{h$#Tn(?guHfsK5-3vdJUC zOe*Bnh%+jF`uw~IPtu*>j@#dTLesSqiS~n$Sar=KrcmLF`Oy>Gu>sO0E>))ia9-;xct3*Zz7mB#rXwld5B_g}&F(Ub=p`va5o}y)j zk)jc^Lqt)_oJI5F7m1SXw~I>d%@wJ<2@|c>a}=##vqdzf%R#hodYnitR`}b$DRjSD z@%Q!rS^h5MYz-x=!7h14dwB6wVUXis)Bz@IIbn%#)Xnfmt zT<|^zGlFQ5%Ta{JaPqPpkIDgT@O3!VPkWVzj+7o zs!pNC+_mCMIbB>~97Bk*cow(jHj=rjfD|^bBK3ivxzf%ruNT_a38WtKTbbL8;skJT|@OdgYeVbF}&Sr zdo02o=w-SF+S!r7%|qwtH#ghil;m!7qd|{Xml%iX`)2%(ajHbnbDOw0nUOjhL-Ke; z6PaOsjJ_(6<+qeK5@-1iGP3m=TD3=kuhcn-az|+L)7^HWk8Ym4dc-&^%vI(@ZcpLi z`7@|FX&{kF*hgP6md3q0G?My!lT}?e2qhLM^WhVRi#_{PcGu4+ObysUK0Q*#Q?H&u zlPp_E{#FBideT&UYx6TwKYlgZ`79CTl|}R0$D|UkRCoGHa}QEK8jN3pEtb&CCB5Nm zVVNCA@_tUk!F;+npOi%3|KiALXqb`}vutsXj}EyqyoqcO-!tE4evAwoZ9olo095lO zn>_JI;j$$jz;&^v9T0VuI5or(X8JSk{`pukYtIezuI3EspE!ztR=R+%F|p!R+$74ljRwD#|)Gnmnqw!BMMyko|}iDEZC@HdVC;St>^H zuRd)@W2Wrj^en|1M4Ufu%L!4UgzU6QlI?pX$<{)l1(jApQb{#~EQbnN3sJHs>TqmX`VY@@&THn}_xzqe z&%zSnhM3pZJ~zQ*Hi78R>{?&qH=ba(R6xWI`zW5owcYk^7LY0g`X zM;sUZNd8BuClvvQ(>P(D34I5Tf4r{r@i$4f>>P&8&2*XYwu?zD&{iSOFH@4b|l9>Ad_=OwUXawo;r8o z`74~Io?rOh&M}%D%e@=&<64&>r zmtd%-Dc5pmh!ThZ@UpQ;y?w4afVViebs35kO$LU{|%fF#;fg@dR z%r~BK(P_wyF8tCiwfxtChxxBTfxE7{oRc)T2mj;EPfja>b@}Nf?>HUL?r@B%dk6}L zo#K34V#LXr*-yCFPdz_Er#rX$O&;g$>sJ1wH_Q)e$9`J?926>VIVLa`iV2S zrjF0G)a4$TEX7HEY{qFRZm1ZsD!;-duNRkdNP}Z(8_!R^#NlYXI#uz~V+sG$96Q0@ zmc9bZaU6bl#7KeP{Hsp!i#G|JnmsvlTg zxat(nUAe4so?9Wm(<7F%sk94!uB9daZMz9qXP$65-4?cR#@%Qy8@NqfP`ZAT z)0@KHT)F;E6$%dB1*13(PGx~=`~_h}939(i{^pnt&LhukzSrkwPEBA(#q73vrx4Yt ze2eBtK~`x1r`f>F>B-4*ep1?1etIt*?tlgNDmHwKa5_C#LojnhL`7JTE5A#QE!QJ? zEPr9|LVkoofTO{iSpH|Z51gTw%{X%%TsUWR`U`>%FXTT@I?b6d{It`mtIPNYAMB`D zWR4tJYa4-nBjul4q$-&2c*kkwr)K`3xz6RqC9OZ+??)dVSo`bsuke1&e^LMH>7BIo zdM{U879CwOEJ@GWJo=0+Ub8G2>wMVyY0AOkbsHjWhu{{w_=T}09X-02wCN4Cld7+@ z^*y%LwshSgYo~=(wsSN;mrPu8#&+SmuO;8>mrL?CzrO#H-Ot^>$;b)gJ+(vLglqo1 z%#KC8C3{!!iu!HknRHphlT(??^LgRPGt67d+dnLTH}chV-fE|CUP9(X9z=NaJbXiW zO4ZYN-i@<(2dV;isUKGUs9#)1KTi42^^1h@cf!l@Gvn{-TT5+1!q?fneXzpDqgBrK zl>23y0&hLr)39f-r%pGUZb|)ZlXC_X8!UD`?%VTeQJcrAlI#5Y#r7k#ZJo>?6{}P% zDQVp?sOVt)y`o$>-jDXjK1au2ufObnZvNFdFX4LkJEQrV!mXTc3+Fs^d-a;HsC&`r z&iO3j!0a9a!L!#SEO9j=rPou)mj`Q!yH+%fTMu%mVhzzF(cRu+Sz9w)nXFGgcA8?NM<^<-7>W85>d?`&5$of8$Oxx)kcv{m zv4CQdu9~9o2Wnn^SYAH(eiWyUxSpT%F{qKS(4_ZML*Xo(3k@oaT&(| zBWBo;yxTo+d{QM!^=gC{(ih;pkqUYLyaQFvUBsE1^(a{13ciB|l1e`nT49t=V*~p$&WN46m%BnQw$R29;eg_Q+Ri&CPXHiAxG9>6)&^o#R%dRL870RLADz{PI ziurVD)kZWMa1uJ7C*a)-6U^mz1ruEp_}mnR+OEs6*IpCkR1ctXD|s~ReI}h*R!YJ? zcO&Zt9VPY4_u#v5d1~)Jfj%%Uq}4ZP;o6jTm>J5)hDJwJ&eO&ZPxWz(hC9YLOhu=w z@~EJ{pUO_(K*Kn?WGT-IxyqGTl5GXyJ697ut?_j9+kp@;sRyYy$|6duMxla*FXfYJ zEa?)0@1M`YFL|%AbxBv6n%aWDNt^g0q#}LpydT2w|cvi;VJn#kDSa%27%aAIB zTK^%FqDS zEAV;Fb-1$ApPE*GL+>rpv?#S3-F$2W&Cnb~b{Do{xNQYY8~P1iD<~81O>%U-ULR7{ z@e*~dd|;x-Zt`|oAe}RE9cC&7;f}n$IAPR#{5JFeXgD;Z^l#7Llp!BNhP{MtmfeW| z^kiaw=rxQ#AVYT^-A7d}1GUw7fT~w^<4jkASt}^jP?|^$zUrac+ZMc+@B};im(l~3 zia0c38g!000McJSf_KqdSX^vMx;DsA_1AhdLFEg4RJ5e7OOSdS8It>&m3TczhrTS? zPd4m|q4sH$$n||j#O~Q)a#1B4w^be|F*|d}Mr9kE6%_=;-2oul8#V^0ff^2h6IK9; z$1g*be_xPx9|q&cDUyWeH(=UK6>?r0kw}E#WWXbn%i#y_Hlaxx)*dw<%meK zr^S{u`N_iP_8q8_breUOsRa4T9+DFtvOe7)E7Cq^XyV=CH z2N&n=P@=QX_Q6BShoPignsjWLPP(glL)Ybfq2odjM(&@1^P`<`Y1;(!v#5t7#e}Ff zM8S$f%Se6L0f?cN#OIISp?P^Il`CA1Ub}*+@`p7rppP19F^?gpt?^`d+Chl1?uT1L zHsa~$C3tqvH3&Ts14F(`k`6l0=|fC4%8Ad(b<{mU2Yq+Az@Nd3X^8(; zB7MDx{CU8Qe37vyZj0o89MAL&BZpsq|NpuEJG%4|T#h;c5!r;8zF!Z` z?W*m+p8w~@@7ZtdLQ@}C!qLGeVY*Tf+25oE3Q1a!d3GFeI%5u+%im#%dNZ^_EUhg( zM!TtYA#IxRB)Ih<#N~Cx8!I>@z zZfy>2_L&H6vu5Mll_?SVgL+1U)k#N8UAl!@SL#T;teb>uP5h-2HGhLNy_2Bg+M8~X zlZqIBJWndW!$fLew3E~WxrQJ4&nxU7@$312uKg!19OwAIe0)Fu{wbX`|2e;e_Mb5R z&$Ry<4efYlon3flWf!@dBZu>9>g{=t_Zsnpor1jhA-#E%8Web*+k5hKYRB=S?wRoB z+_B{yQ`O@s%^t|BI_bb$ozaumP~3wT-1&j4aA53@`dz>M!LP3$=J0dvzyDeN?jUTp z{X+fzt|=9I#MME1{dRacqc5@U9Z8I1Rk4S99Vi^jL1(#vSkdA@Kdicn8#~9sRf`0; zSy+b6ff}T**I}~YlMnPbu1yu~x8oS?PLN(Pin?CXAlB!ksQpMT6!q>+&aPU7`!u^@ zowp5*>0?SG7ub@0TxZh)&msz& zZ(-x!8YtQG9vWg6&}X5$!FsV0yzYJmR(wdM3BB?OPtFUnt-BEEyzWrfJQwmK<><|M zm+@i89b9}a1;(3?BoB-7z+_GvJXN(JzjZwda|&G0H^m8q_~X$vU>=Z7JRJW=Pf*KO zAw?@D!Exm_{Cuz{$?T^@7ZhEAO`18BCv%VtY`=&5hwp%M*V5^sOGQ}Qqy|30bI8)Y zy~*a6C#j~f2Fl56!#*QG&Chem!N9%na_ub^s`BOOL$R`*((+<7khhV`;ebnj*MxTKFq(j*Xzm^)ILtYm6 zgsvusn@n++xgyaid4$}y68dzh01tE&5M9}F(vPQuWRn`1%_+lC+;&($-UEkSTm#?w z*i%pS4Upux3@Y6B5M^&YxU^E5KA0a%C#04`&lov8@Z~WKBO6Il>;y#pJ~ZUp3zW-8 zm`s(4bi@+$?5RO(&wfMG&3;ht`X^ipQH5#RF3>~G3f~oV!<)me;qULC;B4D^csAk| zzPqDF%ZAPX9akrSOW)v~pA01U4g!rm4wx}T7BU;U(Hox?@Py1xFfvr6g74!;)K$^zEpAbn@0*n!ZVc1`%nra0!4+*_j}x@DApW9*KS-y_J|`!S#zVf36BO@JmFCrIzGtl@on=^$Bd8oC}(pe95@BeV|4% z$@#g9XfJLOxgVxQ2Glv@xYHM5V1X8W)wq_d*Ko$nvpM8O?kFQEcBT_B$!>(#JC(`vSsCzcw?CNok)>V#)TU3>v}m1~8F=qp3rijs;I#R-VRqPY z99XvoOM?dxm*UM>bLlsn^z}CEvl$NaE*PWSxFbY+e;KMNYmxc8%&0<;0az#7qvG8h zvf{!lRJ_8+s+J&Hezz;!eN|2ZI#+{pmoGCD9+do6dcZ2|YWX!iXu| z$aBAPTt8ZxXx)DV`SW_wqjiB0GN>n}SUm=1&FM5GXb#!3WjG93Pzv@1QAE9IIyt=K z9>%P41LKn`@&3it_-(T_y?Q|x7Eg)A#n*?yc_jzjzxg6AUF|_saU_|P_!&3e9f&e+ zO7yUgJn2_+8sxJ|$%)oFw2GRBk@OLGb@>ElcE;4=bU5`gn*uXK{=^h}O}f+RDR_2Q zA-#e%Xou1Vz^o$lT&+ciw#w1o%UXWayIS`?!!OtG4#NJr|Dt{m(;7jJIxM6`*aBzi zHX^-37BFEJ4)a#T69%3%*X|YuuTi6=FNUI7$Y#>iB@AXN8~}NqFV!9v0ruIGsUA!w zr5)-}5U25@{#rL6tN1_HFB1As9e%F=F8AyiaG8A^V!oaO-WEQ146K8!(U-yH&=WAx zX#vB~`(XY0I7~Bo1SN8XaG>TYJdLgeu5m3mMi;`B_}egc@hv$1Xcr89oB5-DpRPE` z``7dT-1v=i8^=-8o4Y`9auz)@IhAG^szFH8RM@W-0qV|vbaYiTF{$?ly(VjVbISpG zbx@#tzrckjAnsd1^R3mWCdzhEm(uA3l8@ToL>0`G0QwMxfB2oPYasTIi3g z(4Tex_NPeb&jF!7fkJ=Eg#HW=`twlekB`tF5c;G3Z-4ZK{uB%SIr49R+TAWJ`1Slh z*Z%uo*6!a~|Nr&7B>#V|{rA7Ce}(O~U#NcvRyosWUw*@NbKMA8C_vI`PfL~!2Ht{? zxNVLmC@xR}zw{_vbaExJZ0kaFCRoGv>O`2{T#6@RN8{={x&&7*qoKk!;4HOuct0VX z!taymY3D;kGgyZX-)Tj))Ygz5ySmbIj;W;j!**)=Vl9oEp-voJ6L7roPO>iNJ-oba zg^BskAkEK`W`A0USB=6TDEb19@s9`j^ly+`brJ(#45G>*rYQ2u%@ z2}mAHy7MPM;=ZS-m}*Rv6%FYk+sm*J7QpS&U>cD<9~4^p!-=QacuHjgiLZPFFI-Q`ZS`DrZJ z%BPZ#Dzaplj~unVn1im1x6(8@BXYSuoUXH}!Cr>(G-9MVX6gh1H_nmHw?9B9j-G;M z+O@Flh#jqhS7@-bob=7|hjTAKz~FxGz%^hpCVp*#`Wr^zCpVF*U-TyHn`O{*b~K$_ z6hlMj7-1M}gvSN>^l(Z%4arl++72s%5x;}UPA%HcR2vV;QX)OJJ9&HvsNCA)v?HSf zDrKh8k%w@UrxdhTyvX77z)p-resP$SHH?}&X~UVzy_ZRlsCM?RcT2Bl*G=uf|b z+}Ys}c}5eguUf*TSVAmR^62PBLt-6jLu{oLNLza=9B&H%=NSUjEUARXpaA+VDVGe_ z$skXP7h>h=#U%N9K3sR_(HhUmw11Tr*4xXH)uv0yTGjQiI{5=ynM$F``BIv>Vh1Q6 z@t{%@Cem)j`VgU3k9P(QK>B(f5#;e;%%9uHIhSg*9hpTkM*WVa$MlF%Xa-R@AVYH9 zBQf@l70Jz?L-VW6iF@Z5IyFQSRtBq3>)CH1d;37*e^`&&UfM{kJvULV*-El>NF*`O z)ge2^lw!W-Ah2lPg|T0+qn&#l{@Eph+N`dDkE*v&CRh%YHuc3fiUz2cW+t4bQG)Tq zhe6-ji!tQ|A412jrOMyD(Kx#&UH$C~yuTlS(|y!v>r{KL;9PvyY2ak*o((qByq*l+1WQ@zB)AmM@2t!@0TpvmwY@I`mniKF+N)r_R zu1JAHng52;UtD2|dJE>1>3c=;d;z6+VF=8 zRSXdJg_MM2?WArkvR~~RbCq@%PQ0Zl4sADNj4Aj6euRh|Vpg5{MJCQW!ufwTZOR#yZFP;xPN@WW6VRlLv za#r6PODYlRy)WRIGK&VizY3A|io|$s1ZglILqk=)=|{Vbq(}Kz(6(Aa_dHsQFZ*eM z^Y1p~u}eGX>DiDuPiN8<1I9yHa3Rsx7!14m>ym4$qTy6y4wk=6AR5LiXyT6P@b*O} z$_4cxj-g8U+q1E-eEw`~Tqc0)Clw*?eiiy>M-#2+)7Z251SpP}L4I#O5224@==b-_ z&pESyz5m)l*x~hG)V~J9!Xa8Jjx^n?#BEhip^=Ui8GF{xnW%Al|f1q4{(_3{1THjaG%0P^zRp8?t*R}U{;2=M)(`L9ojJTG4nI4^kC zqR>UlX8Fw>$@IOz7XeY6F4&UVx7(@QaC^Q^Y_}!B44{?S;hy;f9KN)-XnfLok4ROXE zNetm8;o^#VT;o}?pAWay5^@L!8i_N;`0Y_*XBe!Jzz}YhCeG+c>e<20Xp+PbE?6th zxHCh_j-8RVRzeQpqSfMzRzm~jzZm~eeH}Z4p_pVn4Ax6PFjSYUN0TH5BS&yTSFOJe zMO+V0l#qj=x`OrlIe)dE%u0)wG?>C~PD>^Tx7F&L_sMD37f&*2*(AqPWs*P&K# z*mK;J#9*jyces8UdycdX5^^w9*NIzL!JeZ>qyz>-^~PN`)7f)0Nn$Wm4?X+xsHWlwgD7?c!;X92yVv~nOH3TGM*@N&$K#~UYU~H%ww*l%!ws9bOMj1> z6i;Cg!;WC6(UHIYGJA?92?&N6)yZ$N*b!;5>?s&xY-q$I>!&9>XhM zFBrtLB7Q=PCJ6+_FF@~}1((^i@Z7;3g7N#W^i$t3_EVvf#ExJb$ao*#Gj>Fi1O&tH z8r3PM*b!;T>?s(27d!J^*b%-d>%#l6Y=!7xhTn1I zHv^Lfr2rb_C;^rYq`1{Y4Nzn`v3BA%-yg zPFzs%_e4c;gl{%Gg5mf0g|$xXDGYMh5e&Zt*_UhC5ls>hjB_Dbi?m$!6b!#bH}Muf z7utF32!`LH`(}$HYLBub7=DXxT`i7CD_}=3{1)9kS{&hfj2*%7TXX|wafCr3JA&c2 z=pM@d@S7dMxFDjt7>kEUJI)%yn&G!(EqsgE5e&aY_l6WtQ7-|(@VixHJ4SItTseCR zhToz)B8nq?E7%c?(MvkBOb!{cXk59Z_$l*#1RG;*%1uCMfbW9 zM>I)5F#HzXZblrDc8NU&!*9_YU&Il;(lv4 zvmzMBA-Zae_;J)eXD2ZH7G0=C91+*Xj$rsLx>kxf!nd6r!SGvjxe{@N!5{1hhTk(p zRtynGG)X`({1#obLmZLzhCKztZ^>HtzGX)+{1%-GEuNxY0)pX%=)zjdn6zjev3|O5l6Vm61JWTdxqbl^Gd`K26F5OhTo!7JH!!9 z5)cf(MQ2xtBhuvAQ!vyLy*VL{@a@WuVE8RM$KW4+E3hIM1j$-7Ngy!%79F@LejJ|N z*h4V<79A@oj!;o!M=<;r9j+*jXp(?n_$@kOP#lq_&Yptdx9FffafELVb_Bz3$yyj_ zup=0L{~s4s0)pYU=x7A-1M$>kPr>k8bO3-jLZufwf?=oV9er^`69e(@aRYASWMu~m z@8o{}qu<5+UoV8u;s1X1*T+x2|MiK<`sBw8;rGdXe=&~xuYZ4kb{i)n`~7p_!oT0Y J{$F4F{{T9wPig=F literal 0 HcmV?d00001 diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv new file mode 100644 index 0000000..31930f5 --- /dev/null +++ b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv @@ -0,0 +1,3 @@ +1.748432577600628418e-03,1.731401999422814723e-03,1.122151221660537443e-04,6.491534659083154912e-02,5.814734214734215006e-02 +2.947809370891782410e-01,2.944307686462998563e-01,1.583079387122451887e-02,5.371672374936287825e-02,3.197685197685198183e-02 +5.489845542957124541e-02,5.247558942437172136e-02,8.490768522510403968e-03,1.608367698800930723e-01,4.793399993399993786e-02 diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json new file mode 100644 index 0000000..96acca2 --- /dev/null +++ b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json @@ -0,0 +1,20 @@ +{ + "bvalues": "[0.0e+00 1.0e+00 2.0e+00 5.0e+00 1.0e+01 2.0e+01 3.0e+01 5.0e+01 7.5e+01\n 1.0e+02 1.5e+02 2.5e+02 3.5e+02 4.0e+02 5.5e+02 7.0e+02 8.5e+02 1.0e+03\n 1.1e+03 1.2e+03]", + "cons_max": "[0.0031 0.7 0.1276 1.3 ]", + "cons_min": "[-1.00e-04 -1.00e-01 -1.96e-02 7.00e-01]", + "depth": 4, + "loss_coef": 0.09, + "range": [ + [ + 0.0005, + 0.05, + 0.01 + ], + [ + 0.003, + 0.55, + 0.1 + ] + ], + "snr": 100 +} \ No newline at end of file diff --git a/tests/IVIMmodels/unit_tests/test_ivim_fit.py b/tests/IVIMmodels/unit_tests/test_ivim_fit.py index 9ed85b7..a18b746 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_fit.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_fit.py @@ -147,8 +147,60 @@ def test_bounds(bound_input, eng): assert passDp, f"Fit still passes when initial guess Ds is out of fit bounds; potentially initial guesses not respected for: {name}" ''' +def test_deep_learning_algorithms(deep_learning_algorithms, record_property): + algorithm, data, bvals, kwargs, requires_matlab, tolerances = deep_learning_algorithms + if requires_matlab: + if eng is None: + pytest.skip("Running without matlab; if Matlab is available please run pytest --withmatlab") + else: + kwargs = {**kwargs, 'eng': eng} + + tolerances = tolerances_helper(tolerances, data) + fit = OsipiBase(bvalues=bvals, algorithm=algorithm, **kwargs) + + array_2d = np.array([dat["data"] for _, dat in data.items()]) + start_time = time.time() + fit_result = fit.osipi_fit(array_2d, bvals) + elapsed_time = time.time() - start_time + + errors = [] # Collect all assertion errors + + def to_list_if_needed(value): + return value.tolist() if isinstance(value, np.ndarray) else value + + for i, (name, dat) in enumerate(data.items()): + try: + record_property('test_data', { + "name": name, + "algorithm": algorithm, + "f_fit": to_list_if_needed(fit_result['f'][i]), + "Dp_fit": to_list_if_needed(fit_result['Dp'][i]), + "D_fit": to_list_if_needed(fit_result['D'][i]), + "f": to_list_if_needed(dat['f']), + "Dp": to_list_if_needed(dat['Dp']), + "D": to_list_if_needed(dat['D']), + "rtol": tolerances["rtol"], + "atol": tolerances["atol"] + }) + + npt.assert_allclose(fit_result['f'][i], dat['f'], + rtol=tolerances["rtol"]["f"], atol=tolerances["atol"]["f"]) + + if dat['f'] < 0.80: + npt.assert_allclose(fit_result['D'][i], dat['D'], + rtol=tolerances["rtol"]["D"], atol=tolerances["atol"]["D"]) + + if dat['f'] > 0.03: + npt.assert_allclose(fit_result['Dp'][i], dat['Dp'], + rtol=tolerances["rtol"]["Dp"], atol=tolerances["atol"]["Dp"]) + + except AssertionError as e: + errors.append(f"{name + ' ' + algorithm+ ' D=' + str(dat['D']) + ' Dp=' + str(dat['Dp']) + ' f=' + str(dat['f'])}: {e}") + + if errors: + all_errors = "\n".join(errors) + raise AssertionError(f"Some tests failed:\n{all_errors}") - \ No newline at end of file diff --git a/utilities/data_simulation/GenerateData.py b/utilities/data_simulation/GenerateData.py index f1309e2..99dbe14 100644 --- a/utilities/data_simulation/GenerateData.py +++ b/utilities/data_simulation/GenerateData.py @@ -145,3 +145,30 @@ def multilinear_signal(self, D, F, S0, bvalues, offset=0): signal *= S0 signal += offset return signal + + def simulate_training_data(self, bvalues, SNR = (5,100), n = 1000000, Drange = (0.0005,0.0034), frange = (0,1), Dprange = (0.005,0.1), rician_noise = False): + test = self._rng.uniform(0, 1, (n, 1)) + D = Drange[0] + (test * (Drange[1] - Drange[0])) + test = self._rng.uniform(0, 1, (n, 1)) + f = frange[0] + (test * (frange[1] - frange[0])) + test = self._rng.uniform(0, 1, (n, 1)) + Dp = Dprange[0] + (test * (Dprange[1] - Dprange[0])) + data_sim = np.zeros([len(D), len(bvalues)]) + bvalues = np.array(bvalues) + if type(SNR) == tuple: + test = self._rng.uniform(0, 1, (n, 1)) + SNR = np.exp(np.log(SNR[1]) + (test * (np.log(SNR[0]) - np.log(SNR[1])))) + addnoise = True + elif SNR == 0: + addnoise = False + else: + SNR = SNR * np.ones_like(Dp) + addnoise = True + # loop over array to fill with simulated IVIM data + for aa in range(len(D)): + data_sim[aa, :] = self.ivim_signal(D[aa][0], Dp[aa][0], f[aa][0], 1, bvalues, snr=SNR[aa], rician_noise=rician_noise) + # if SNR is set to zero, don't add noise + S0_noisy = np.mean(data_sim[:, bvalues == 0], axis=1) + data_sim = data_sim / S0_noisy[:, None] + return data_sim, D, f, Dp + From 8aa6dadb6e249a6c2fc681a768e03d5a2f0c8df1 Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion Date: Mon, 23 Jun 2025 12:10:01 +0200 Subject: [PATCH 3/9] updated train data SNR and added exceptions for DL testing to prevent errors after merge :) --- conftest.py | 5 +- requirements.txt | 3 +- tests/IVIMmodels/unit_tests/generic_DL.json | 1134 ++++++++--------- tests/IVIMmodels/unit_tests/test_ivim_fit.py | 4 +- .../unit_tests/test_ivim_synthetic.py | 4 +- 5 files changed, 577 insertions(+), 573 deletions(-) diff --git a/conftest.py b/conftest.py index 1cbbe35..a4b8d13 100644 --- a/conftest.py +++ b/conftest.py @@ -243,9 +243,8 @@ def algorithmlist(algorithmFile): algorithms = algorithm_information["algorithms"] for algorithm in algorithms: algorithm_dict = algorithm_information.get(algorithm, {}) - if not algorithm_dict.get('deep_learning', False): - requires_matlab = algorithm_dict.get("requires_matlab", False) - yield algorithm, requires_matlab + requires_matlab = algorithm_dict.get("requires_matlab", False) + yield algorithm, requires_matlab, algorithm_dict.get('deep_learning', False) def bound_input(datafile,algorithmFile): # Find the algorithms from algorithms.json diff --git a/requirements.txt b/requirements.txt index cc94a5e..8853a91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ pandas sphinx sphinx_rtd_theme pytest-json-report -ivimnet \ No newline at end of file +ivimnet +super_ivim_dc \ No newline at end of file diff --git a/tests/IVIMmodels/unit_tests/generic_DL.json b/tests/IVIMmodels/unit_tests/generic_DL.json index 289392e..46fd494 100644 --- a/tests/IVIMmodels/unit_tests/generic_DL.json +++ b/tests/IVIMmodels/unit_tests/generic_DL.json @@ -1,758 +1,758 @@ { "Myocardium LV": { - "noise": 0.05, + "noise": 0.04, "D": 0.0024, "f": 0.15, "Dp": 0.08, "data": [ - 1.0414761561278763, - 0.9543261077240296, - 0.8973202278041915, - 0.875673251659577, - 0.8280418658198557, - 0.8958022300221469, - 0.9173950534039884, - 0.8317113405445333, - 0.6706905989242123, - 0.6921684393805962, - 0.6088101133967462, - 0.4832140883185282, - 0.4323792867026822, - 0.38563566932033716, - 0.2987971934002683, - 0.12065701822058425, - 0.06375659797908062, - 0.08780731396391583, - 0.03806733579088041, - -0.007727071899961364 + 1.033180924902301, + 0.9607468657793776, + 0.912606451182656, + 0.888620393895259, + 0.8418819322382458, + 0.884731423360733, + 0.8948278336097781, + 0.8166950158442055, + 0.6786227776445861, + 0.6874715517646282, + 0.6056532504758427, + 0.47986924885264165, + 0.41929421834511005, + 0.37360032607203914, + 0.28445075605440917, + 0.1282091905031671, + 0.07311015923259691, + 0.0856679032303328, + 0.042585284457289964, + 0.0033612521618336458 ] }, "myocardium RV": { - "noise": 0.05, + "noise": 0.04, "D": 0.0024, "f": 0.15, "Dp": 0.08, "data": [ - 1.018130474834411, - 0.9609171021077032, - 0.8649652714635861, - 0.9112196676382909, - 0.8501302652858714, - 0.7921253678406212, - 0.7426742053880208, - 0.8216937258798647, - 0.6826547057806759, - 0.6202693700942281, - 0.5354165262437048, - 0.4593879049073265, - 0.4256689200493212, - 0.386303095977355, - 0.15180872294197184, - 0.22166125931683023, - 0.09303039386870172, - 0.04854864403792119, - 0.06178473487961477, - 0.02793297584570685 + 1.0145043798675288, + 0.9660196612863166, + 0.8867224861101718, + 0.91705752667823, + 0.8595526518110584, + 0.8017899336155123, + 0.7550511551970039, + 0.8086809241124706, + 0.688194063129757, + 0.6299522963355337, + 0.5469383807534097, + 0.4608083021236803, + 0.41392592502242126, + 0.37413426739765343, + 0.16685997968777203, + 0.20901258338016387, + 0.09652919594429378, + 0.05426096728953708, + 0.06155920372827745, + 0.03188929035836821 ] }, "myocardium ra": { - "noise": 0.05, + "noise": 0.04, "D": 0.0015, "f": 0.07, "Dp": 0.07, "data": [ - 1.0115104179794072, - 0.9592284470237417, - 0.9907407481807518, - 0.9701958502235285, - 0.9832619611247679, - 0.9226084706447879, - 0.8964681257799296, - 0.8058241769813856, - 0.7855605950094182, - 0.8640343886934098, - 0.71113579042937, - 0.6738151374761032, - 0.4970519703012802, - 0.39624232626712863, - 0.47767832760684903, - 0.33172877824247354, - 0.22624097472696142, - 0.24171216114003355, - 0.1445681631577838, - 0.20377619373869593 + 1.0092083343835259, + 0.9661574802430908, + 0.990206450003812, + 0.9706325316312445, + 0.9767925839190637, + 0.9220420032510354, + 0.896704422240442, + 0.8176423934101318, + 0.7947310478273071, + 0.8513319609173063, + 0.717433034543031, + 0.6668879161875433, + 0.5076708740135724, + 0.41907282532720147, + 0.46365437068395976, + 0.3304714439286537, + 0.23296693987075087, + 0.23487193869963477, + 0.15137581352968732, + 0.19376654820017183 ] }, "Blood RV": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 1.0, "Dp": 0.1, "data": [ - 1.004117274360202, - 0.9001750154762196, - 0.8262144668987947, - 0.6256094182787147, - 0.36251995439688267, - 0.10262795002111597, - 0.08148909014667471, - -0.011005970525050387, - -0.01930670067328502, - 0.04446958565879572, - -0.07617803967310156, - 0.03357843546126774, - 0.022008550569263697, - -0.08500549539941143, - -0.0026807799758221383, - -0.05816063921732141, - 0.009761582528698201, - 0.005422145900150086, - -0.02790361453161008, - -0.01117137079931383 + 1.0032938194881618, + 0.9011074959881675, + 0.8247177241346322, + 0.6217936665654985, + 0.36359185175179465, + 0.10916941666421531, + 0.07514868579091256, + -0.007457187020223218, + -0.015334743664598449, + 0.035584748512989074, + -0.06094237055801714, + 0.02686274837179178, + 0.017606840455411082, + -0.06800439631952913, + -0.0021446239806577104, + -0.04652851137385713, + 0.007809266022958561, + 0.004337716720120069, + -0.022322891625288062, + -0.008937096639451064 ] }, "Blood RA": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 1.0, "Dp": 0.1, "data": [ - 1.0489290319928273, - 0.8909263540876897, - 0.8655462087617176, - 0.5691970073196508, - 0.31800967828120386, - 0.11538210717002553, - -0.004187481545519553, - 0.05057059190130978, - 0.08920896827058303, - 0.03940492488165636, - -0.026116311696022677, - 0.05885164807268148, - 0.03428622678823648, - 0.027706227111570707, - -0.023770390365566028, - -0.029424468316703662, - 0.03086054658282188, - 0.016178689173731928, - 0.05617024240504107, - 0.06103537430395393 + 1.0391432255942619, + 0.8937085668773437, + 0.8561831176249705, + 0.5766637377982473, + 0.32798363085925153, + 0.11937274238334296, + 0.006607428437157149, + 0.041804062920864915, + 0.07147779149049599, + 0.03153301989127758, + -0.02089298817635404, + 0.04708131846092277, + 0.02742898143058931, + 0.022164981689256565, + -0.019016312292452823, + -0.02353957465336293, + 0.024688437266257503, + 0.012942951338985542, + 0.044936193924032856, + 0.048828299443163144 ] }, "muscle": { - "noise": 0.05, + "noise": 0.04, "D": 0.00137, "f": 0.1, "Dp": 0.0263, "data": [ - 0.9714276818954892, - 0.9802637566544565, - 0.9334162015399322, - 1.0101339767413773, - 0.9038053357932732, - 0.9441339822995856, - 0.9811635020543724, - 0.8917657398829961, - 0.7957125159315066, - 0.729721241740571, - 0.7160232331367825, - 0.6294350916311617, - 0.5570356038224215, - 0.5073972425411085, - 0.4002331624994297, - 0.2701546943856365, - 0.22489459396945471, - 0.22238175420842007, - 0.1661025732510989, - 0.15528542071965007 + 0.9771421455163913, + 0.9834454308259023, + 0.9452156251065997, + 1.0044139802947745, + 0.915969921803736, + 0.9422614234128238, + 0.966768746315094, + 0.8868649011898158, + 0.801775641750857, + 0.7421732033410118, + 0.7197688278589072, + 0.6313747309126961, + 0.5570672087225664, + 0.5099772094974028, + 0.4049154491880213, + 0.2851134303775019, + 0.23608983440436468, + 0.2236446560863158, + 0.17276532513782425, + 0.1590053575168229 ] }, "Liver": { - "noise": 0.05, + "noise": 0.04, "D": 0.0015, "f": 0.11, "Dp": 0.1, "data": [ - 1.005697694414328, - 1.0296553455912854, - 0.9673997721515275, - 0.9923036065618027, - 0.958322841487569, - 0.9001047809063214, - 0.8816045574359181, - 0.8344130032658189, - 0.7167065156804507, - 0.7460590218679478, - 0.7265372617550753, - 0.5369587818053495, - 0.49133492872023193, - 0.4612159410743326, - 0.4046612458419037, - 0.29009641079868914, - 0.2642319987283172, - 0.21112638380969095, - 0.12385698121445748, - 0.11935684135378682 + 1.0045581555314624, + 1.0213638998197319, + 0.969398694488538, + 0.9838565535209267, + 0.9501015461451721, + 0.8958005059278971, + 0.8765465132191194, + 0.8328169780131174, + 0.7324377081858197, + 0.7500542360964734, + 0.7233657030730803, + 0.5519045170693782, + 0.49836479783347865, + 0.46666122408420285, + 0.40173482533228394, + 0.29436604798073696, + 0.2611243113260643, + 0.20861827555417325, + 0.1332704687060602, + 0.12490867518647186 ] }, "gall bladder": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 0.0, "Dp": 0.1, "data": [ - 0.9523815647424912, - 1.032757861693169, - 1.0208923239324414, - 0.9960713724470585, - 1.0954743459226557, - 0.9269415183478232, - 0.9821839240559127, - 0.7854672286362608, - 0.8145990550130255, - 0.7171481283888134, - 0.5586465363670376, - 0.42636915882044135, - 0.31931668796316237, - 0.3142373711268758, - 0.04276652367211642, - 0.15297036815364162, - 0.13165739182159109, - 0.024464542568317357, - -0.03978115454201203, - 0.03958370667582257 + 0.961905251793993, + 1.0256071884552098, + 1.01551745195674, + 0.9938794858782594, + 1.0704685834478262, + 0.9299061213951083, + 0.9685333762989757, + 0.8005153781940202, + 0.8113824877622958, + 0.7218821468473943, + 0.5744428594179848, + 0.435568637604556, + 0.32544090019276095, + 0.311628739283941, + 0.07262320066184397, + 0.14686758017350968, + 0.12094224665750349, + 0.029529047728226676, + -0.024448290153361626, + 0.037131709830116565 ] }, "esophagus": { - "noise": 0.05, + "noise": 0.04, "D": 0.00167, "f": 0.32, "Dp": 0.03, "data": [ - 0.9700230451815722, - 0.9640836332018894, - 0.9268778941200213, - 0.9431218226292617, - 0.895234170014858, - 0.9026509322829899, - 0.8262225731133575, - 0.6525695561966889, - 0.6226416509744647, - 0.5911441734045834, - 0.4482699307573094, - 0.41847261713492356, - 0.35140906022062113, - 0.2685256769581542, - 0.3325457936770929, - 0.23403766328303385, - 0.1192979773913282, - 0.1454474011064396, - 0.05253676969682378, - 0.08764435184661902 + 0.9760184361452577, + 0.9691484902482909, + 0.9373217631823604, + 0.944451896555969, + 0.8973473615256383, + 0.8887773110682924, + 0.8163527823728226, + 0.6614411629941387, + 0.6248484487586474, + 0.5911848582410676, + 0.4651908816661642, + 0.4243955339989611, + 0.35693330038669246, + 0.28455262538581105, + 0.3203165877858471, + 0.2294822657030623, + 0.12832792887457084, + 0.14195952181202723, + 0.06369348051138615, + 0.08844760465205179 ] }, "esophagus cont": { - "noise": 0.05, + "noise": 0.04, "D": 0.00167, "f": 0.32, "Dp": 0.03, "data": [ - 1.0064916942930082, - 1.0898158301770804, - 0.9700968827320784, - 0.92753661283127, - 0.8348341943602735, - 0.8807606299497848, - 0.7459175246364695, - 0.752690755777689, - 0.5975529317710861, - 0.5100753987968977, - 0.5016446634790717, - 0.44244890513467694, - 0.3999848072343228, - 0.41426923770210267, - 0.33641183391601337, - 0.2106493086629893, - 0.14637861880972128, - 0.1183822606096154, - 0.060343581730889766, - 0.07437812310993061 + 1.0051933554344066, + 1.0697342478284437, + 0.971896954072006, + 0.9319837287175755, + 0.8490273810019706, + 0.8712650692017283, + 0.7521087435913123, + 0.7415381226589388, + 0.6047774733959446, + 0.526329838554919, + 0.5078906678435741, + 0.44357656439876386, + 0.3957938979976538, + 0.40114747398096984, + 0.32340941997698347, + 0.21077158200702664, + 0.1499924420092853, + 0.12030740941456787, + 0.06993893013863893, + 0.07783462166270105 ] }, "st wall": { - "noise": 0.05, + "noise": 0.04, "D": 0.0015, "f": 0.3, "Dp": 0.012, "data": [ - 0.9614650464095889, - 1.0146466845776958, - 1.0251119839509732, - 0.9301147156388705, - 0.8407853159831342, - 0.9746737421761303, - 0.8288441114092242, - 0.8072852067771582, - 0.7134443480016787, - 0.7037478594648261, - 0.5630620532604259, - 0.5282708628352321, - 0.4123946518270492, - 0.4160974562797989, - 0.3453001870835757, - 0.27648779815187785, - 0.2188296362651662, - 0.10763272028333518, - 0.16260410856774776, - 0.11105320684887388 + 0.9691720371276711, + 1.010791807855152, + 1.0182473591167254, + 0.9395515722008307, + 0.8637591505339656, + 0.9627990401016886, + 0.8387755161482753, + 0.8086409516733656, + 0.7202532865809711, + 0.7015690569861011, + 0.5721598465279487, + 0.5218244134009936, + 0.4136332070822221, + 0.410205378899944, + 0.33767467069420626, + 0.27019501543651475, + 0.214186274782251, + 0.1173447673001895, + 0.15697038509717565, + 0.11198444327354337 ] }, "Stomach Contents": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 0.0, "Dp": 0.0, "data": [ - 0.9310843015691552, - 0.9619695749273509, - 0.9023065970929444, - 0.9851165418446419, - 0.9521433939573646, - 0.9599509082487774, - 0.8686295937425018, - 0.8635615283375762, - 0.7532782265539696, - 0.7567248617550365, - 0.6078580802195307, - 0.48916900427956644, - 0.3285596647668797, - 0.2982203304014941, - 0.13991589714962996, - 0.10432550191849926, - 0.04837910531117902, - 0.06807960933657477, - 0.030689883066384722, - 0.03144188538636681 + 0.9448674412553242, + 0.9689765590425553, + 0.9206488704851425, + 0.9851156213963261, + 0.9558038218755933, + 0.9563136333158716, + 0.8776899120482471, + 0.8629908179550725, + 0.7623258249950511, + 0.7535435335403728, + 0.6138120944999792, + 0.4858085139718561, + 0.33283528163573484, + 0.2988151067036357, + 0.15034269944385478, + 0.1079516871853958, + 0.05431961744917384, + 0.0644211011428326, + 0.03192853993335577, + 0.030618252798551963 ] }, "pancreas": { - "noise": 0.05, + "noise": 0.04, "D": 0.0013, "f": 0.15, "Dp": 0.01, "data": [ - 0.9928999447173146, - 0.9525884401862714, - 0.9544025739942831, - 1.019780145611553, - 0.961400430143548, - 0.9569039948062483, - 0.9661430640694069, - 0.8583162595452268, - 0.8680011464621598, - 0.7917372192063143, - 0.6403280950200478, - 0.6088869053903841, - 0.46771540523097155, - 0.6232680672291963, - 0.4029692054952672, - 0.2779379656422561, - 0.2655335879894387, - 0.24431552892980105, - 0.18473475287844374, - 0.14312294681217042 + 0.9943199557738517, + 0.9615513907492641, + 0.962486593496966, + 1.01325958270585, + 0.9640697696093661, + 0.9557220836708255, + 0.9586366184308593, + 0.8641503962017191, + 0.8627793118164445, + 0.7937023818566902, + 0.6588382726900143, + 0.6124017243901765, + 0.4829344002386456, + 0.6002324161049521, + 0.40566062654474405, + 0.29080684705848964, + 0.268738824423758, + 0.24178418995751585, + 0.18847082013521696, + 0.15022167388023694 ] }, "Right kydney cortex": { - "noise": 0.05, + "noise": 0.04, "D": 0.00212, "f": 0.097, "Dp": 0.02, "data": [ - 0.9447164616388711, - 1.0253524557069253, - 0.9468030021740546, - 1.1027993540032097, - 1.0112607157637477, - 0.9170695922780615, - 0.9318788149355747, - 0.8925299262472647, - 0.7701581384241947, - 0.6793398491842773, - 0.6572998748193475, - 0.5157976361332927, - 0.47535603661203346, - 0.3877673061842632, - 0.2628517365408753, - 0.25671357183005467, - 0.08192037711847165, - 0.1315631464014776, - -0.009927392935916046, - 0.10736124706369708 + 0.9557731693110969, + 1.0195153523853666, + 0.9559175939441356, + 1.078489079465684, + 1.0017035283696583, + 0.9197625101858864, + 0.925621475639203, + 0.8835968935990496, + 0.7745063782760604, + 0.692196408222722, + 0.6582108325694905, + 0.5190708826023274, + 0.4662971316152858, + 0.3875660263909982, + 0.2665586409332584, + 0.24631812310457812, + 0.09532963541849249, + 0.12692822927033753, + 0.009594589477006352, + 0.10007541016107514 ] }, "right kidney medulla": { - "noise": 0.05, + "noise": 0.04, "D": 0.00209, "f": 0.158, "Dp": 0.019, "data": [ - 0.9939325550509083, - 0.9769465793399255, - 0.965260696936534, - 0.8861841240325065, - 1.0021437148818337, - 0.8581427450798357, - 0.8580008326775601, - 0.7898237498945948, - 0.7294053682674831, - 0.6479355858753167, - 0.5760151477149101, - 0.5706331155943267, - 0.3294110737489627, - 0.3545622889211826, - 0.278112120050124, - 0.13294296073806244, - 0.2190707765277487, - 0.03969232934431753, - -0.036670871081434755, - 0.07399982807913605 + 0.9951460440407266, + 0.980610942856737, + 0.9703278436097708, + 0.9043328668604725, + 0.9927638450434675, + 0.8696301856072838, + 0.8624367935145194, + 0.7957704921401572, + 0.7350925130953995, + 0.6597137947500391, + 0.5857209281357799, + 0.5566471657772445, + 0.3446015850700227, + 0.3566570801329421, + 0.27583891025126467, + 0.14534586021572554, + 0.20375485775193092, + 0.052582777324161785, + -0.012436215521854588, + 0.07291283248781157 ] }, "Left kidney cortex": { - "noise": 0.05, + "noise": 0.04, "D": 0.00212, "f": 0.097, "Dp": 0.02, "data": [ - 1.058610075459223, - 0.9603284679793281, - 0.9518735294413312, - 0.9760242027907666, - 0.9143294993148827, - 0.9345568670493143, - 0.9069139228958475, - 0.8214728744866402, - 0.8207206998561044, - 0.7821725781684973, - 0.7121928931052638, - 0.5352966671483371, - 0.42550424064337955, - 0.40421086397522105, - 0.3087318820412746, - 0.22552935189848855, - 0.17643830523984294, - 0.14364947466713723, - 0.14839305825625088, - 0.06962283503370063 + 1.0468880603673782, + 0.9674961622032888, + 0.9599740157579568, + 0.9770689584957297, + 0.9241585552105663, + 0.9337523300028886, + 0.9056495620074213, + 0.82675125219055, + 0.8149564274215881, + 0.774462591410098, + 0.7021252471982236, + 0.5346701074143629, + 0.4264156948403627, + 0.4007208726237645, + 0.30326275733357777, + 0.22137074715932523, + 0.17094397791558952, + 0.13659729188286526, + 0.1362509504307399, + 0.06988468053707798 ] }, "left kidney medulla": { - "noise": 0.05, + "noise": 0.04, "D": 0.00209, "f": 0.158, "Dp": 0.019, "data": [ - 1.0696846876257082, - 0.9944476109980003, - 0.9345852013753286, - 0.917965814581272, - 0.9113951980580977, - 0.8562427844533702, - 0.7907698414887543, - 0.7468883137498656, - 0.8500183261263754, - 0.7240914828941581, - 0.6502933564134067, - 0.5873884036622673, - 0.44002324389302944, - 0.422312250329574, - 0.2020018131709333, - 0.196833508107462, - 0.11121199829130335, - 0.12743794653359145, - 0.05793124120030294, - 0.07503516982609999 + 1.0557477501005665, + 0.994611768183197, + 0.9457874471608064, + 0.929758219299485, + 0.9201650315844786, + 0.8681102171061115, + 0.8086520005634749, + 0.7614221432243738, + 0.8315828793825132, + 0.7206385123651122, + 0.6451434950945771, + 0.570051396231597, + 0.43309132118527605, + 0.4108570492596552, + 0.21495066474791216, + 0.19645829811124518, + 0.11746783516277465, + 0.12277927107558091, + 0.06324547430353558, + 0.07374110588538273 ] }, "spleen": { - "noise": 0.05, + "noise": 0.04, "D": 0.0013, "f": 0.2, "Dp": 0.03, "data": [ - 1.0406891927127284, - 1.1421805725534993, - 0.9712122549708575, - 0.9328597405180468, - 0.9219674445445825, - 0.8575471432718786, - 0.795853472538119, - 0.8453455611515053, - 0.7236899749139, - 0.6371721300759845, - 0.6466445962936025, - 0.6139781063913334, - 0.5160364863184796, - 0.5226170618192205, - 0.4366671404988213, - 0.40407026774054444, - 0.3471132427761567, - 0.16279312392662906, - 0.25280036274001105, - 0.1507097449302377 + 1.0325513541701827, + 1.1123544145261721, + 0.9742249256516672, + 0.9396794841599914, + 0.9251401460661807, + 0.8638837943986638, + 0.8068256778835379, + 0.835132449467526, + 0.7283043235625228, + 0.652224455742792, + 0.6494135821853746, + 0.6068089850706042, + 0.5143419653844743, + 0.5132171828991017, + 0.42760445301671146, + 0.38766009006814756, + 0.33068433537997927, + 0.173839586026749, + 0.2405297177510098, + 0.15418956733631253 ] }, "spinal cord": { - "noise": 0.05, + "noise": 0.04, "D": 0.00041, "f": 0.178, "Dp": 0.0289, "data": [ - 1.0350348261603348, - 0.9881173929532401, - 1.0159094446411976, - 0.9040873399202006, - 0.9804932581335475, - 0.9916144240980125, - 0.851552084185367, - 0.8732141509851978, - 0.8012466746589524, - 0.7262491077666176, - 0.7045544540842098, - 0.7293489316518207, - 0.7218468593284062, - 0.6201918664289697, - 0.6042211809316861, - 0.5775834039668379, - 0.5854901370792571, - 0.6445765726048157, - 0.4825856101525844, - 0.5900286566520527 + 1.0280278609282678, + 0.9894124087290663, + 1.0105934605574332, + 0.9181433920272777, + 0.9747867809146716, + 0.9763212578346278, + 0.8585914189168204, + 0.8680279908139578, + 0.8044938720507925, + 0.740773708345681, + 0.7187040142639285, + 0.7318889153712291, + 0.7199020676728957, + 0.63568702117008, + 0.6145873939327691, + 0.5854508514525849, + 0.5844167380941034, + 0.6247653592062735, + 0.49078978257612843, + 0.5725374742644902 ] }, "Bone Marrow": { - "noise": 0.05, + "noise": 0.04, "D": 0.00043, "f": 0.145, "Dp": 0.05, "data": [ - 0.9970318173523992, - 1.0006434152520487, - 1.018209085021019, - 0.9563480643325503, - 0.9143856650490984, - 0.8558112415285944, - 0.7935998988418784, - 0.7611400408646187, - 0.9345174322860307, - 0.8048602103030668, - 0.7556633585268748, - 0.7322228326193456, - 0.7222214605640584, - 0.6505000275910844, - 0.6831273225528944, - 0.676502193789096, - 0.617660081757738, - 0.5958910006147835, - 0.5432855457656088, - 0.5349508966811534 + 0.9976254538819194, + 0.9990268713188439, + 1.0116605563575343, + 0.9582964191157695, + 0.9193641998024293, + 0.8648532025081588, + 0.8101589607889702, + 0.7786552382990123, + 0.9138691875128335, + 0.8078864164068891, + 0.7648654018070447, + 0.7393494595075057, + 0.7248846610107041, + 0.6643784607444544, + 0.6814868439159852, + 0.6677550541709638, + 0.6127763537286554, + 0.587949855689514, + 0.5411836787838665, + 0.5300311974922378 ] }, "Artery": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 1.0, "Dp": 0.1, "data": [ - 1.1366652743929608, - 0.9031078968867813, - 0.8276404785227217, - 0.6703488575617523, - 0.35463043473306477, - 0.17999841083004475, - -0.005301951566434267, - -0.026002339288390332, - -0.025976254242574114, - -0.0314649963825061, - -0.06597097622587501, - -0.03190712544992341, - 0.061423460792576726, - -0.01410326352575495, - 0.06885107149344581, - -0.008697506332233622, - 0.08605639313609106, - 0.00041575373852931327, - 0.01208369365799957, - -0.049815610235614566 + 1.1093322195143687, + 0.9034538011166169, + 0.8258585334337737, + 0.6575852179919285, + 0.35728023602074027, + 0.17106578531135835, + 0.0057158524204253755, + -0.01945428203089517, + -0.020670386520029723, + -0.02516291712005238, + -0.052776719800235905, + -0.025525700357161146, + 0.049138768634061504, + -0.011282610820603958, + 0.05508085719475665, + -0.006958005065786897, + 0.06884511450887285, + 0.0003326029908234506, + 0.009666954926399655, + -0.03985248818849165 ] }, "Vein": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 1.0, "Dp": 0.1, "data": [ - 1.0246512699008448, - 0.9525140798242245, - 0.6645580509918904, - 0.638261134232097, - 0.36686671463468157, - 0.14487481261837412, - 0.019134419276372692, - -0.12375298340567041, - -0.011689865160312663, - 0.002827581793995449, - -0.12913694494128608, - 0.04388747661411013, - -0.11902684240931445, - -0.041892796222326355, - 0.05622029125932542, - 0.05021727325560413, - -0.07412321200463441, - 0.05842113202675782, - 0.03548103045918604, - -0.00952204490054799 + 1.0197210159206758, + 0.9429787474665715, + 0.6953925914091087, + 0.6319150393282043, + 0.36706925994203377, + 0.14296690674202184, + 0.025264949094670943, + -0.09765479732471924, + -0.009241275254220563, + 0.002271145421148856, + -0.10330949477256475, + 0.035109981294065694, + -0.09522147392745144, + -0.03351423697786109, + 0.044976233007460335, + 0.0401738186044833, + -0.05929856960370753, + 0.046736905621406255, + 0.028384824367348835, + -0.007617635920438392 ] }, "asc lower intestine": { - "noise": 0.05, + "noise": 0.04, "D": 0.00131, "f": 0.69, "Dp": 0.029, "data": [ - 1.0310050726425262, - 1.0786855981547856, - 0.905032835711743, - 0.9791870157610291, - 0.9291607613596673, - 0.7301516576604141, - 0.579963207738715, - 0.4685531576016079, - 0.37122838352980947, - 0.32263920108649796, - 0.13979193917260468, - 0.2330337216536861, - 0.17574159838764214, - 0.2577630654754723, - 0.18085525908143332, - 0.17355227443805832, - 0.1230183037281744, - 0.18333043869275675, - 0.14772233622396666, - 0.02088579003900755 + 1.024804058114021, + 1.0589227837960973, + 0.916087733926352, + 0.96431791613657, + 0.9077820781691823, + 0.721783995723083, + 0.581396538074047, + 0.4652823667373273, + 0.36885881276163374, + 0.3200920672492273, + 0.1645539740144957, + 0.2312098254476927, + 0.17979701066390505, + 0.2429248446681493, + 0.17484777791251824, + 0.1636242361481287, + 0.1187759092815024, + 0.1633931944500911, + 0.1328527078387414, + 0.029581661515141952 ] }, "trans lower intestine": { - "noise": 0.05, + "noise": 0.04, "D": 0.00131, "f": 0.69, "Dp": 0.029, "data": [ - 1.1010910165710657, - 0.9589485258640357, - 0.9460125348317381, - 0.9021826524332424, - 0.8477384509092252, - 0.6261562626372219, - 0.4961681603306774, - 0.46384706442332085, - 0.3800300793387826, - 0.23097018624158372, - 0.30767900657047303, - 0.3433823377021268, - 0.2781958930360044, - 0.1971206257280814, - 0.07308806504798766, - 0.07482710194380694, - 0.11520105129248473, - 0.08399119922734945, - 0.04586663820196364, - 0.07259384018643282 + 1.0808728132568524, + 0.9631331259634974, + 0.9488714932223482, + 0.9027144254743407, + 0.8426442298088287, + 0.6385876797045293, + 0.5143605001476169, + 0.46151749219469773, + 0.37590016940881227, + 0.2467568553732959, + 0.29886362793279037, + 0.3194887182864452, + 0.2617604463825949, + 0.19441089287023655, + 0.08863402268576173, + 0.08464409815272762, + 0.11252210733295068, + 0.08392180287776525, + 0.05136814942113898, + 0.07094810163308216 ] }, "desc lower intestine": { - "noise": 0.05, + "noise": 0.04, "D": 0.00131, "f": 0.69, "Dp": 0.029, "data": [ - 1.0325140842231593, - 0.9257803136790758, - 1.0117244258862137, - 0.8643929081857167, - 0.9004162931520309, - 0.606100414631902, - 0.5903776292670289, - 0.5108809559255845, - 0.36796610314533457, - 0.28936305573833354, - 0.22090639026774, - 0.274903578964495, - 0.2353185102438208, - 0.12165334016083454, - 0.08854845711319659, - 0.12526607999630837, - 0.14000920576701195, - -0.0014170609609992502, - 0.016068844967175137, - 0.06689049367488156 + 1.0260112673785273, + 0.9365985562155295, + 1.0014410060659287, + 0.8724826300763201, + 0.8847865036030733, + 0.6225430013002734, + 0.5897280752966981, + 0.49914460539650857, + 0.3662489884540538, + 0.2934711509706957, + 0.22944553489060396, + 0.2647057112963398, + 0.227458540148848, + 0.1340370644164391, + 0.10100233633792886, + 0.12499528059472875, + 0.13236863091257245, + 0.015595194727086287, + 0.027529914833308175, + 0.06638542442384117 ] }, "small intestine": { - "noise": 0.05, + "noise": 0.04, "D": 0.00131, "f": 0.69, "Dp": 0.029, "data": [ - 1.0113923280346593, - 0.9516429649530004, - 1.028263051337911, - 0.8702056497106295, - 0.7882616940484249, - 0.6559200169813942, - 0.5171409903767998, - 0.4689268545793833, - 0.34658802842547975, - 0.3041407511081699, - 0.20505903889702215, - 0.2749110731916181, - 0.18737578729206558, - 0.24810421746997058, - 0.1631729167488409, - 0.00028511829852838344, - 0.13368329347787375, - 0.059266858804970776, - -0.00953865541370462, - 0.023426001677845962 + 1.0091138624277274, + 0.957288677234669, + 1.0146719064272864, + 0.8771328232962504, + 0.7950628243201886, + 0.6623986831798672, + 0.5311387641845149, + 0.4655813243195477, + 0.34914652867817, + 0.30529330726656484, + 0.21676765379402968, + 0.26471170667803834, + 0.18910436178744383, + 0.23519776626374791, + 0.1607019040464443, + 0.025010511236504773, + 0.1273079010812619, + 0.0641423305398623, + 0.007043914528604367, + 0.031613830826212684 ] }, "pericardium": { - "noise": 0.05, + "noise": 0.04, "D": 0.003, "f": 0.07, "Dp": 0.01, "data": [ - 1.0317736953179435, - 1.021755679310279, - 1.0124932359422896, - 0.919227516752285, - 0.974858852394421, - 0.9059177859197673, - 0.8962201140038372, - 0.8157261967964851, - 0.7299953974218134, - 0.7318032532445874, - 0.6228612099840378, - 0.4905121653825162, - 0.3035470208513579, - 0.27965049598505864, - 0.23695132503732727, - 0.15839380613261925, - 0.03703305780999669, - -0.02262660123747843, - 0.08509977607208054, - 0.04530243888669494 + 1.0254189562543548, + 1.016708077284339, + 1.0086047114941583, + 0.9319300461110076, + 0.9730576750080627, + 0.9113646625255759, + 0.8973387467530622, + 0.8211640702882257, + 0.739133466365069, + 0.7283851038188696, + 0.6200116264309581, + 0.48141910109657626, + 0.30834880138367365, + 0.27999893914815877, + 0.22533955783346057, + 0.1495047069086678, + 0.04415248468137799, + -0.008840250674543366, + 0.07494032381810614, + 0.04132424950352531 ] }, "config": { diff --git a/tests/IVIMmodels/unit_tests/test_ivim_fit.py b/tests/IVIMmodels/unit_tests/test_ivim_fit.py index a18b746..0ac54e9 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_fit.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_fit.py @@ -71,7 +71,7 @@ def to_list_if_needed(value): assert elapsed_time < max_time, f"Algorithm {name} took {elapsed_time} seconds, which is longer than 2 second to fit per voxel" #less than 0.5 seconds per voxel def test_default_bounds_and_initial_guesses(algorithmlist,eng): - algorithm, requires_matlab = algorithmlist + algorithm, requires_matlab, deep_learning = algorithmlist if requires_matlab: if eng is None: pytest.skip(reason="Running without matlab; if Matlab is available please run pytest --withmatlab") @@ -79,6 +79,8 @@ def test_default_bounds_and_initial_guesses(algorithmlist,eng): kwargs = {'eng': eng} else: kwargs={} + if deep_learning: + pytest.skip(reason="deep learning algorithms do not take initial guesses. Bound testing for deep learning algorithms not yet implemented") fit = OsipiBase(algorithm=algorithm,**kwargs) #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" diff --git a/tests/IVIMmodels/unit_tests/test_ivim_synthetic.py b/tests/IVIMmodels/unit_tests/test_ivim_synthetic.py index 66a0b40..16dcb3e 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_synthetic.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_synthetic.py @@ -12,9 +12,11 @@ @pytest.mark.slow def test_generated(algorithmlist, ivim_data, SNR, rtol, atol, fit_count, rician_noise, save_file, save_duration_file, use_prior,eng): # assert save_file == "test" - ivim_algorithm, requires_matlab = algorithmlist + ivim_algorithm, requires_matlab, deep_learning = algorithmlist if requires_matlab and eng is None: pytest.skip(reason="Running without matlab; if Matlab is available please run pytest --withmatlab") + if deep_learning: + pytest.skip(reason="Slow drifting in performance not yet implmented for deep learning algorithms") #requieres training a network per b-value set and inferencing all data in 1 go. So not 1 data point per time, but all data in 1 go :). Otherwise network will be trained many many times... rng = np.random.RandomState(42) # random.seed(42) S0 = 1 From 4c10aeb8eedd5c4b74e643e4ec5f1d658e69f567 Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion <7846269+oliverchampion@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:10:20 +0200 Subject: [PATCH 4/9] Delete tests/IVIMmodels/unit_tests/models directory --- .../unit_tests/models/super_ivim_dc.pt | Bin 81581 -> 0 bytes .../unit_tests/models/super_ivim_dc_NRMSE.csv | 3 --- .../unit_tests/models/super_ivim_dc_init.json | 20 ------------------ 3 files changed, 23 deletions(-) delete mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt delete mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv delete mode 100644 tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt b/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt deleted file mode 100644 index 4ffd3658ea0c5fedf66376a2e52200a6663b66bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81581 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 J{$F4F{{T9wPig=F diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv deleted file mode 100644 index 31930f5..0000000 --- a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_NRMSE.csv +++ /dev/null @@ -1,3 +0,0 @@ -1.748432577600628418e-03,1.731401999422814723e-03,1.122151221660537443e-04,6.491534659083154912e-02,5.814734214734215006e-02 -2.947809370891782410e-01,2.944307686462998563e-01,1.583079387122451887e-02,5.371672374936287825e-02,3.197685197685198183e-02 -5.489845542957124541e-02,5.247558942437172136e-02,8.490768522510403968e-03,1.608367698800930723e-01,4.793399993399993786e-02 diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json b/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json deleted file mode 100644 index 96acca2..0000000 --- a/tests/IVIMmodels/unit_tests/models/super_ivim_dc_init.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "bvalues": "[0.0e+00 1.0e+00 2.0e+00 5.0e+00 1.0e+01 2.0e+01 3.0e+01 5.0e+01 7.5e+01\n 1.0e+02 1.5e+02 2.5e+02 3.5e+02 4.0e+02 5.5e+02 7.0e+02 8.5e+02 1.0e+03\n 1.1e+03 1.2e+03]", - "cons_max": "[0.0031 0.7 0.1276 1.3 ]", - "cons_min": "[-1.00e-04 -1.00e-01 -1.96e-02 7.00e-01]", - "depth": 4, - "loss_coef": 0.09, - "range": [ - [ - 0.0005, - 0.05, - 0.01 - ], - [ - 0.003, - 0.55, - 0.1 - ] - ], - "snr": 100 -} \ No newline at end of file From 633e181de348905adb3166fbc382350be1df5da5 Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion Date: Mon, 23 Jun 2025 13:20:35 +0200 Subject: [PATCH 5/9] broaden bounds + add trained networks to ignore file --- .gitignore | 4 +++- conftest.py | 2 +- .../unit_tests/models/super_ivim_dc.pt | Bin 81581 -> 81581 bytes .../unit_tests/models/super_ivim_dc_NRMSE.csv | 6 +++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 977ba28..88b6009 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ tests/IVIMmodels/unit_tests/*.log junit/* ivim_simulation.bval ivim_simulation.bvec +*.pt # Unit test / coverage reports .tox/ @@ -32,4 +33,5 @@ nosetests.xml coverage.xml *.pyc phantoms/MR_XCAT_qMRI/*.json -phantoms/MR_XCAT_qMRI/*.txt \ No newline at end of file +phantoms/MR_XCAT_qMRI/*.txt +tests/IVIMmodels/unit_tests/models diff --git a/conftest.py b/conftest.py index a4b8d13..e9c19ce 100644 --- a/conftest.py +++ b/conftest.py @@ -288,5 +288,5 @@ def deep_learning_algorithms(datafile,algorithmFile): if algorithm_dict.get('deep_learning',False): kwargs = algorithm_dict.get("options", {}) requires_matlab = algorithm_dict.get("requires_matlab", False) - tolerances = algorithm_dict.get("tolerances", {"atol":{"f": 2e-1, "D": 8e-4, "Dp": 6e-2},"rtol":{"f": 0.2, "D": 0.3, "Dp": 0.3}}) + tolerances = algorithm_dict.get("tolerances", {"atol":{"f": 2e-1, "D": 8e-4, "Dp": 8e-2},"rtol":{"f": 0.2, "D": 0.3, "Dp": 0.4}}) yield algorithm, all_data, bvals, kwargs, requires_matlab, tolerances \ No newline at end of file diff --git a/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt b/tests/IVIMmodels/unit_tests/models/super_ivim_dc.pt index 4ffd3658ea0c5fedf66376a2e52200a6663b66bc..8938205c5796a5f8dbfcc05bf3d3498f65422abc 100644 GIT binary patch delta 36259 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 delta 36245 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 Date: Mon, 23 Jun 2025 21:06:44 +0200 Subject: [PATCH 6/9] update after review Ivan - Now use ivim_fit_full_volume instead - Skipped --- src/standardized/IVIM_NEToptim.py | 27 +++++++++- .../OGC_AmsterdamUMC_Bayesian_biexp.py | 46 +++++++++++++--- src/standardized/OGC_AmsterdamUMC_biexp.py | 24 ++++++++- .../OGC_AmsterdamUMC_biexp_segmented.py | 27 +++++++++- src/standardized/Super_IVIM_DC.py | 27 ++++++++++ src/wrappers/OsipiBase.py | 52 +++++++++++-------- tests/IVIMmodels/unit_tests/algorithms.json | 6 +-- tests/IVIMmodels/unit_tests/test_ivim_fit.py | 2 +- 8 files changed, 173 insertions(+), 38 deletions(-) diff --git a/src/standardized/IVIM_NEToptim.py b/src/standardized/IVIM_NEToptim.py index ab6f674..e0fe3db 100644 --- a/src/standardized/IVIM_NEToptim.py +++ b/src/standardized/IVIM_NEToptim.py @@ -60,7 +60,7 @@ def initialize(self, bounds, initial_guess, fitS0, traindata, SNR): if SNR is None: warnings.warn('No SNR indicated. Data simulated with SNR = (5-1000)') SNR = (5, 1000) - self.training_data(self.bvalues,n=1000000,SNR=SNR) + self.osipi_training_data(self.bvalues,n=1000000,SNR=SNR) self.arg=Arg() if bounds is not None: self.arg.net_pars.cons_min = bounds[0] # Dt, Fp, Ds, S0 @@ -77,7 +77,7 @@ def ivim_fit(self, signals, bvalues, **kwargs): Args: signals (array-like) - bvalues (array-like, optional): b-values for the signals. If None, self.bvalues will be used. Default is None. + bvalues (array-like): b-values for the signals. If None, self.bvalues will be used. Default is None. Returns: _type_: _description_ @@ -94,6 +94,29 @@ def ivim_fit(self, signals, bvalues, **kwargs): return results + + def ivim_fit_full_volume(self, signals, bvalues, **kwargs): + """Perform the IVIM fit + + Args: + signals (array-like) + bvalues (array-like): b-values for the signals. If None, self.bvalues will be used. Default is None. + + Returns: + _type_: _description_ + """ + if not np.array_equal(bvalues, self.bvalues): + raise ValueError("bvalue list at fitting must be identical as the one at initiation, otherwise it will not run") + signals = self.osipi_reshape_to_voxelwise(signals) + paramsNN = deep.predict_IVIM(signals, self.bvalues, self.net, self.arg) + + results = {} + results["D"] = paramsNN[0] + results["f"] = paramsNN[1] + results["Dp"] = paramsNN[2] + + return results + class NetArgs: def __init__(self): self.optim = 'adam' # these are the optimisers implementd. Choices are: 'sgd'; 'sgdr'; 'adagrad' adam diff --git a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py index f6d5cb4..fa9a08c 100644 --- a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py @@ -1,5 +1,5 @@ from src.wrappers.OsipiBase import OsipiBase -from src.original.OGC_AmsterdamUMC.LSQ_fitting import flat_neg_log_prior, fit_bayesian, empirical_neg_log_prior, fit_segmented +from src.original.OGC_AmsterdamUMC.LSQ_fitting import flat_neg_log_prior, fit_bayesian, empirical_neg_log_prior, fit_segmented, fit_bayesian_array, fit_segmented_array import numpy as np class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase): @@ -34,7 +34,7 @@ class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase): supported_dimensions = 1 supported_priors = True - def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, fitS0=True, prior_in=None): + def __init__(self, bvalues=None, thresholds=150, bounds=None, initial_guess=None, fitS0=True, prior_in=None): """ Everything this algorithm requires should be implemented here. @@ -44,14 +44,21 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non the requirements. Args: - datain is a 2D array with values of D, f, D* (and S0) that will form the prior. + datain (Array): is a 2D array with values of D, f, D* (and S + ) that will form the prior. + thresholds (Bolean, optional): a bolean indicating what threshold is used + prior_in (array, optional): 2D array of D, f, D* and (optionally) S0 values which form the prior + """ super(OGC_AmsterdamUMC_Bayesian_biexp, self).__init__(bvalues=bvalues, bounds=bounds, thresholds=thresholds, initial_guess=initial_guess) #, fitS0, prior_in) self.OGC_algorithm = fit_bayesian - self.initialize(bounds, initial_guess, fitS0, prior_in) + self.OGC_algorithm_array = fit_bayesian_array + self.initialize(bounds, initial_guess, fitS0, prior_in, thresholds) self.fit_segmented=fit_segmented - def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None): + def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None, thresholds=None): + + if 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=([0, 0, 0.005, 0.7],[0.005, 1.0, 0.2, 1.3]) @@ -64,7 +71,7 @@ def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None) self.initial_guess = initial_guess self.use_initial_guess = True self.use_bounds = True - self.thresholds = 150 + self.thresholds = thresholds if prior_in is None: print('using a flat prior between bounds') self.neg_log_prior=flat_neg_log_prior([self.bounds[0][0],self.bounds[1][0]],[self.bounds[0][1],self.bounds[1][1]],[self.bounds[0][2],self.bounds[1][2]],[self.bounds[0][3],self.bounds[1][3]]) @@ -101,4 +108,31 @@ def ivim_fit(self, signals, bvalues, initial_guess=None, **kwargs): results["f"] = fit_results[1] results["Dp"] = fit_results[2] + return results + + def ivim_fit_full_volume(self, signals, bvalues, initial_guess=None, **kwargs): + """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_ + """ + bvalues=np.array(bvalues) + + epsilon = 0.000001 + fit_results = fit_segmented_array(bvalues, signals, bounds=self.bounds, cutoff=self.thresholds, p0=self.initial_guess) + fit_results=np.array(fit_results+(1,)) + for i in range(4): + if fit_results[i] < self.bounds[0][i] : fit_results[0] = self.bounds[0][i]+epsilon + if fit_results[i] > self.bounds[1][i] : fit_results[0] = self.bounds[1][i]-epsilon + fit_results = self.OGC_algorithm_array(bvalues, signals, self.neg_log_prior, x0=fit_results, fitS0=self.fitS0, bounds=self.bounds) + + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["Dp"] = fit_results[2] + return results \ No newline at end of file diff --git a/src/standardized/OGC_AmsterdamUMC_biexp.py b/src/standardized/OGC_AmsterdamUMC_biexp.py index 8c74f43..7af0b60 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp.py @@ -1,5 +1,5 @@ from src.wrappers.OsipiBase import OsipiBase -from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_least_squares +from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_least_squares, fit_least_squares_array import numpy as np class OGC_AmsterdamUMC_biexp(OsipiBase): @@ -45,6 +45,7 @@ def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=Non #super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues, bounds, initial_guess, fitS0) super(OGC_AmsterdamUMC_biexp, self).__init__(bvalues=bvalues, bounds=bounds, initial_guess=initial_guess) self.OGC_algorithm = fit_least_squares + self.OGC_algorithm_array = fit_least_squares_array self.fitS0=fitS0 self.initialize(bounds, initial_guess, fitS0) @@ -83,4 +84,25 @@ def ivim_fit(self, signals, bvalues, **kwargs): results["f"] = fit_results[1] results["Dp"] = fit_results[2] + return results + + def ivim_fit_full_volume(self, signals, bvalues, **kwargs): + """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_ + """ + + bvalues=np.array(bvalues) + fit_results = self.OGC_algorithm_array(bvalues, signals, p0=self.initial_guess, bounds=self.bounds, fitS0=self.fitS0) + + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["Dp"] = fit_results[2] + return results \ No newline at end of file diff --git a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py index 5219581..3e9f809 100644 --- a/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py +++ b/src/standardized/OGC_AmsterdamUMC_biexp_segmented.py @@ -1,5 +1,5 @@ from src.wrappers.OsipiBase import OsipiBase -from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_segmented +from src.original.OGC_AmsterdamUMC.LSQ_fitting import fit_segmented, fit_segmented_array import numpy as np class OGC_AmsterdamUMC_biexp_segmented(OsipiBase): @@ -44,6 +44,7 @@ def __init__(self, bvalues=None, thresholds=150, bounds=None, initial_guess=None """ super(OGC_AmsterdamUMC_biexp_segmented, self).__init__(bvalues, thresholds, bounds, initial_guess) self.OGC_algorithm = fit_segmented + self.OGC_algorithm_array = fit_segmented_array self.initialize(bounds, initial_guess, thresholds) @@ -65,6 +66,7 @@ def initialize(self, bounds, initial_guess, thresholds): print('warning, no thresholds were defined, so default bounds are used of 150') else: self.thresholds = thresholds + def ivim_fit(self, signals, bvalues, **kwargs): """Perform the IVIM fit @@ -84,4 +86,27 @@ def ivim_fit(self, signals, bvalues, **kwargs): results["f"] = fit_results[1] results["Dp"] = fit_results[2] + return results + + + def ivim_fit_full_volume(self, signals, bvalues, **kwargs): + """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_ + """ + signals = self.osipi_reshape_to_voxelwise(signals) + + bvalues=np.array(bvalues) + fit_results = self.OGC_algorithm_array(bvalues, signals, bounds=self.bounds, cutoff=self.thresholds, p0=self.initial_guess) + + results = {} + results["D"] = fit_results[0] + results["f"] = fit_results[1] + results["Dp"] = fit_results[2] + return results \ No newline at end of file diff --git a/src/standardized/Super_IVIM_DC.py b/src/standardized/Super_IVIM_DC.py index 278923f..a9da09a 100644 --- a/src/standardized/Super_IVIM_DC.py +++ b/src/standardized/Super_IVIM_DC.py @@ -108,3 +108,30 @@ def ivim_fit(self, signals, bvalues, **kwargs): results["Dp"] = Dp return results + + + def test_deep_learning_algorithms(self, signals, bvalues, **kwargs): + """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: + results: a dictionary containing "d", "f", and "Dp". + """ + if not np.array_equal(bvalues, self.bvalues): + raise ValueError("bvalue list at fitting must be identical as the one at initiation, otherwise it will not run") + + Dp, Dt, f, S0_superivimdc = infer_from_signal( + signal=signals, + bvalues=self.bvalues, + model_path=f"{self.working_dir}/{self.super_ivim_dc_filename}.pt", + ) + + results = {} + results["D"] = Dt + results["f"] = f + results["Dp"] = Dp + + return results diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index 576419d..f5ce46e 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -110,31 +110,26 @@ def osipi_fit(self, data, bvalues=None, **kwargs): minimum_bvalue = np.min(use_bvalues) # We normalize the signal to the minimum bvalue. Should be 0 or very close to 0. b0_indices = np.where(use_bvalues == minimum_bvalue)[0] - if not self.deep_learning: - for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): - # Normalize array - single_voxel_data = data[ijk] - single_voxel_data_normalization_factor = np.mean(single_voxel_data[b0_indices]) - single_voxel_data_normalized = single_voxel_data/single_voxel_data_normalization_factor - - args = [single_voxel_data_normalized, use_bvalues] - fit = self.ivim_fit(*args, **kwargs) # For single voxel fits, we assume this is a dict with a float value per key. - for key in list(fit.keys()): - results[key][ijk] = fit[key] - else: - # Note that I am probably high-jacking the wrong part of the code as DL works best for 2D arrays (b-values vs signal decays). - # normalizing of signal still needs implementing for DL. - args = [data, use_bvalues] - results = self.ivim_fit(*args,**kwargs) # For single voxel fits, we assume this is a dict with a float value per key. + for ijk in tqdm(np.ndindex(data.shape[:-1]), total=np.prod(data.shape[:-1])): + # Normalize array + single_voxel_data = data[ijk] + single_voxel_data_normalization_factor = np.mean(single_voxel_data[b0_indices]) + single_voxel_data_normalized = single_voxel_data/single_voxel_data_normalization_factor + + args = [single_voxel_data_normalized, use_bvalues] + fit = self.ivim_fit(*args, **kwargs) # For single voxel fits, we assume this is a dict with a float value per key. + for key in list(fit.keys()): + results[key][ijk] = fit[key] #self.parameter_estimates = self.ivim_fit(data, bvalues) return results - + + def osipi_fit_full_volume(self, data, bvalues=None, **kwargs): """Sends a full volume in one go to the fitting algorithm. The osipi_fit method only sends one voxel at a time. Args: - data (array): 3D (single slice) or 4D (multi slice) DWI data. + data (array): 2D (data x b-values), 3D (single slice) or 4D (multi slice) DWI data, with last dimension the b-value dimension. bvalues (array, optional): The b-values of the DWI data. Defaults to None. Returns: @@ -179,7 +174,20 @@ def osipi_fit_full_volume(self, data, bvalues=None, **kwargs): print("Full volume fitting not supported for this algorithm") return False - + + + def osipi_reshape_to_voxelwise(self, data): + """ + reshapes multi-D input (spatial dims, bvvalue) data to 2D voxel-wise array + Args: + data (array): mulit-D array (data x b-values) + Returns: + out (array): 2D array (voxel x b-value) + """ + B = data.shape[-1] + voxels = int(np.prod(data.shape[:-1])) # e.g., X*Y*Z + return data.reshape(voxels, B) + def osipi_print_requirements(self): """ Prints the requirements of the algorithm. @@ -285,11 +293,11 @@ def osipi_check_required_initial_guess(self): return True - def osipi_check_required_bvalues(): + def osipi_check_required_bvalues(self): """Minimum number of b-values required""" pass - def osipi_author(): + def osipi_author(self): """Author identification""" return '' @@ -323,7 +331,7 @@ def osipi_simple_bias_and_RMSE_test(self, SNR, bvalues, f, Dstar, D, noise_reali print(f"Dstar bias:\t{Dstar_bias}\nDstar RMSE:\t{Dstar_RMSE}") print(f"D bias:\t{D_bias}\nD RMSE:\t{D_RMSE}") - def training_data(self, bvalues, data=None, SNR=(5,1000), n=1000000,Drange=(0.0005,0.0034),frange=(0,1),Dprange=(0.005,0.1),rician_noise=False): + def osipi_training_data(self, bvalues, data=None, SNR=(5,1000), n=1000000,Drange=(0.0005,0.0034),frange=(0,1),Dprange=(0.005,0.1),rician_noise=False): rng = np.random.RandomState(42) if data is None: gen = GenerateData(rng=rng) diff --git a/tests/IVIMmodels/unit_tests/algorithms.json b/tests/IVIMmodels/unit_tests/algorithms.json index 1a8cd31..051a259 100644 --- a/tests/IVIMmodels/unit_tests/algorithms.json +++ b/tests/IVIMmodels/unit_tests/algorithms.json @@ -14,15 +14,11 @@ "PV_MUMC_biexp", "PvH_KB_NKI_IVIMfit", "OJ_GU_seg", - "IVIM_NEToptim", - "Super_IVIM_DC" + "IVIM_NEToptim" ], "IVIM_NEToptim": { "deep_learning": true }, - "Super_IVIM_DC": { - "deep_learning": true - }, "ASD_MemorialSloanKettering_QAMPER_IVIM": { "requires_matlab": true }, diff --git a/tests/IVIMmodels/unit_tests/test_ivim_fit.py b/tests/IVIMmodels/unit_tests/test_ivim_fit.py index 0ac54e9..3dd0964 100644 --- a/tests/IVIMmodels/unit_tests/test_ivim_fit.py +++ b/tests/IVIMmodels/unit_tests/test_ivim_fit.py @@ -163,7 +163,7 @@ def test_deep_learning_algorithms(deep_learning_algorithms, record_property): array_2d = np.array([dat["data"] for _, dat in data.items()]) start_time = time.time() - fit_result = fit.osipi_fit(array_2d, bvals) + fit_result = fit.osipi_fit_full_volume(array_2d, bvals) elapsed_time = time.time() - start_time errors = [] # Collect all assertion errors From ed3d63baa007fb955600c47a00504f252b5a7373 Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion <7846269+oliverchampion@users.noreply.github.com> Date: Mon, 23 Jun 2025 21:25:46 +0200 Subject: [PATCH 7/9] Update requirements.txt --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8853a91..cc94a5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,4 @@ pandas sphinx sphinx_rtd_theme pytest-json-report -ivimnet -super_ivim_dc \ No newline at end of file +ivimnet \ No newline at end of file From c41e2e2302f076a684f6436169127c620f1a536e Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion Date: Tue, 24 Jun 2025 10:05:16 +0200 Subject: [PATCH 8/9] Update OGC_AmsterdamUMC_Bayesian_biexp.py --- src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py index fa9a08c..5253503 100644 --- a/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py +++ b/src/standardized/OGC_AmsterdamUMC_Bayesian_biexp.py @@ -20,7 +20,7 @@ class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase): # 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 + 1] # 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 @@ -34,7 +34,7 @@ class OGC_AmsterdamUMC_Bayesian_biexp(OsipiBase): supported_dimensions = 1 supported_priors = True - def __init__(self, bvalues=None, thresholds=150, bounds=None, initial_guess=None, fitS0=True, prior_in=None): + def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, fitS0=True, prior_in=None): """ Everything this algorithm requires should be implemented here. @@ -71,6 +71,9 @@ def initialize(self, bounds=None, initial_guess=None, fitS0=True, prior_in=None, self.initial_guess = initial_guess self.use_initial_guess = True self.use_bounds = True + if thresholds 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]') + thresholds = 150 self.thresholds = thresholds if prior_in is None: print('using a flat prior between bounds') From d03af5d636790f87b15bdd63d1d1b25ea51d4e41 Mon Sep 17 00:00:00 2001 From: Oliver Gurney-Champion Date: Wed, 25 Jun 2025 09:25:45 +0200 Subject: [PATCH 9/9] Move reshape to specific code --- src/standardized/IVIM_NEToptim.py | 14 +++++++++++++- src/wrappers/OsipiBase.py | 12 ------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/standardized/IVIM_NEToptim.py b/src/standardized/IVIM_NEToptim.py index e0fe3db..1beaef9 100644 --- a/src/standardized/IVIM_NEToptim.py +++ b/src/standardized/IVIM_NEToptim.py @@ -107,7 +107,7 @@ def ivim_fit_full_volume(self, signals, bvalues, **kwargs): """ if not np.array_equal(bvalues, self.bvalues): raise ValueError("bvalue list at fitting must be identical as the one at initiation, otherwise it will not run") - signals = self.osipi_reshape_to_voxelwise(signals) + signals = self.reshape_to_voxelwise(signals) paramsNN = deep.predict_IVIM(signals, self.bvalues, self.net, self.arg) results = {} @@ -117,6 +117,18 @@ def ivim_fit_full_volume(self, signals, bvalues, **kwargs): return results + def reshape_to_voxelwise(self, data): + """ + reshapes multi-D input (spatial dims, bvvalue) data to 2D voxel-wise array + Args: + data (array): mulit-D array (data x b-values) + Returns: + out (array): 2D array (voxel x b-value) + """ + B = data.shape[-1] + voxels = int(np.prod(data.shape[:-1])) # e.g., X*Y*Z + return data.reshape(voxels, B) + class NetArgs: def __init__(self): self.optim = 'adam' # these are the optimisers implementd. Choices are: 'sgd'; 'sgdr'; 'adagrad' adam diff --git a/src/wrappers/OsipiBase.py b/src/wrappers/OsipiBase.py index f5ce46e..471c246 100644 --- a/src/wrappers/OsipiBase.py +++ b/src/wrappers/OsipiBase.py @@ -176,18 +176,6 @@ def osipi_fit_full_volume(self, data, bvalues=None, **kwargs): return False - def osipi_reshape_to_voxelwise(self, data): - """ - reshapes multi-D input (spatial dims, bvvalue) data to 2D voxel-wise array - Args: - data (array): mulit-D array (data x b-values) - Returns: - out (array): 2D array (voxel x b-value) - """ - B = data.shape[-1] - voxels = int(np.prod(data.shape[:-1])) # e.g., X*Y*Z - return data.reshape(voxels, B) - def osipi_print_requirements(self): """ Prints the requirements of the algorithm.