def combine(self): super().combine() statistic_maps = {} for name in self._sample_storage: samples_path = os.path.join(self._output_dir, name + '.samples.npy') samples = open_memmap(samples_path, mode='r') statistic_maps[name] = np.mean(samples, axis=1) statistic_maps[name + '.std'] = np.std(samples, axis=1) write_all_as_nifti(restore_volumes(statistic_maps, self._mask), os.path.join(self._output_dir, 'univariate_normal'), nifti_header=self._nifti_header, gzip=self._write_volumes_gzipped) write_all_as_nifti({'UsedMask': self._mask}, self._output_dir, nifti_header=self._nifti_header, gzip=self._write_volumes_gzipped) if not self._keep_samples: for ind, name in enumerate(self._model.get_free_param_names()): os.remove(os.path.join(self._output_dir, name + '.samples.npy')) else: return load_samples(self._output_dir)
def samples_npy_to_nifti(samples_npy_fname, used_mask, nifti_header, nifti_fname=None): """Convert a npy file containing sample results to a nifti file. Since the sample npy files are stored as a two dimensional matrix (with on the first axis the ROI index number and on the second the samples), we need to have the lookup table for the spatial information about the samples. Args: samples_npy_fname (str): the filename of the samples file to convert used_mask (ndarray or str): either an three dimensional matrix with the mask or a path to a nifti file. nifti_header (nibabel header): the header to use for writing the nifti file nifti_fname (str): the filename of the nifti file. If not given it defaults to the same directory as the samples file. """ samples = np.load(samples_npy_fname, mmap_mode='r') if isinstance(used_mask, str): used_mask = load_nifti(used_mask).get_data() if np.count_nonzero(used_mask) != samples.shape[0]: raise ValueError( 'The number of voxels in the mask ({}) does not correspond ' 'with the number of voxels in the samples file ({})'.format( np.count_nonzero(used_mask), samples.shape[0])) if nifti_fname is None: nifti_fname = os.path.join( os.path.dirname(samples_npy_fname), os.path.splitext(os.path.basename(samples_npy_fname))[0] + '.nii.gz') volume = restore_volumes(samples, used_mask) write_nifti(volume, nifti_fname, nifti_header)
def create_signal_estimates(model, input_data, parameters): """Create the signals estimates for your estimated model parameters. This function is typically used to obtain signal estimates from optimization results. This function evaluates the model as it is in the model fitting and sample. That is, this method includes the gradient deviations (if set in the input data) and loads all static and fixed parameters maps. Args: model (str or model): the model or the name of the model to use for estimating the signals input_data (mdt.utils.MRIInputData): the input data object, we will set this to the model parameters (str or dict): either a directory file name or a dictionary containing optimization results Each element is assumed to be a 4d volume with the voxels we are using for the simulations. Returns: ndarray: the 4d array with the signal estimates per voxel """ if isinstance(model, str): model = get_model(model)() model.set_input_data(input_data) build_model = model.build() if isinstance(parameters, str): parameters = get_all_nifti_data(parameters) parameters = create_roi(parameters, input_data.mask) parameters = model.param_dict_to_array(parameters) kernel_data = { 'data': build_model.get_kernel_data(), 'parameters': Array(parameters, ctype='mot_float_type'), 'estimates': Zeros((parameters.shape[0], build_model.get_nmr_observations()), 'mot_float_type') } _get_simulate_function(build_model).evaluate(kernel_data, parameters.shape[0]) results = kernel_data['estimates'].get_data() return restore_volumes(results, input_data.mask)
def create_signal_estimates(model, input_data, parameters): """Create the signals estimates for your estimated model parameters. This function is typically used to obtain signal estimates from optimization results. This function evaluates the model as it is in the model fitting and sampling. That is, this method includes the gradient deviations (if set in the input data) and loads all static and fixed parameters maps. Args: model (str or model): the model or the name of the model to use for estimating the signals input_data (mdt.utils.MRIInputData): the input data object, we will set this to the model parameters (str or dict): either a directory file name or a dictionary containing optimization results Each element is assumed to be a 4d volume with the voxels we are using for the simulations. Returns: ndarray: the 4d array with the signal estimates per voxel """ if isinstance(model, string_types): model = get_model(model)() model.set_input_data(input_data) if isinstance(parameters, string_types): parameters = get_all_nifti_data(parameters) parameters = create_roi(parameters, input_data.mask) parameters = model.param_dict_to_array(parameters) build_model = model.build() if parameters.shape[0] != build_model.get_nmr_problems(): raise ValueError( 'The number of voxels in the parameters does not match those in the model.' ) calculator = CalculateModelEstimates() results = calculator.calculate(model.build(), parameters) return restore_volumes(results, input_data.mask)
def get_init_data(model_name): inits = {} free_parameters = get_model(model_name)().get_free_param_names() if 'S0.s0' in free_parameters and input_data.has_input_data('b'): if input_data.get_input_data( 'b').shape[0] == input_data.nmr_voxels: masked_observations = ma.masked_array( input_data.observations, input_data.get_input_data('b') < 250e6) inits['S0.s0'] = restore_volumes( np.mean(masked_observations, axis=1), input_data.mask) else: unweighted_locations = np.where( input_data.get_input_data('b') < 250e6)[0] inits['S0.s0'] = np.mean( input_data.signal4d[..., unweighted_locations], axis=-1) if model_name.startswith('BallStick_r2'): inits.update( get_subset(free_parameters, get_model_fit('BallStick_r1'))) inits['w_stick1.w'] = np.minimum(inits['w_stick0.w'], 0.05) elif model_name.startswith('BallStick_r3'): inits.update( get_subset(free_parameters, get_model_fit('BallStick_r2'))) inits['w_stick2.w'] = np.minimum(inits['w_stick1.w'], 0.05) elif model_name.startswith('Tensor'): fit_results = get_model_fit('BallStick_r1') inits.update(get_subset(free_parameters, fit_results)) inits['Tensor.theta'] = fit_results['Stick0.theta'] inits['Tensor.phi'] = fit_results['Stick0.phi'] elif model_name.startswith('NODDI'): fit_results = get_model_fit('BallStick_r1') inits.update(get_subset(free_parameters, fit_results)) inits['w_ic.w'] = fit_results['w_stick0.w'] / 2.0 inits['w_ec.w'] = fit_results['w_stick0.w'] / 2.0 inits['w_csf.w'] = fit_results['w_ball.w'] inits['NODDI_IC.theta'] = fit_results['Stick0.theta'] inits['NODDI_IC.phi'] = fit_results['Stick0.phi'] elif model_name.startswith('BinghamNODDI_r1'): noddi_results = get_model_fit('NODDI') inits.update(get_subset(free_parameters, noddi_results)) inits['w_in0.w'] = noddi_results['w_ic.w'] inits['w_en0.w'] = noddi_results['w_ec.w'] inits['w_csf.w'] = noddi_results['w_csf.w'] inits['BinghamNODDI_IN0.theta'] = noddi_results['NODDI_IC.theta'] inits['BinghamNODDI_IN0.phi'] = noddi_results['NODDI_IC.phi'] inits['BinghamNODDI_IN0.k1'] = noddi_results['NODDI_IC.kappa'] elif model_name.startswith('BinghamNODDI_r2'): bs2_results = get_model_fit('BallStick_r2') inits.update(get_subset(free_parameters, bs2_results)) inits.update( get_subset(free_parameters, get_model_fit('BinghamNODDI_r1'))) inits['BinghamNODDI_IN1.theta'] = bs2_results['Stick1.theta'] inits['BinghamNODDI_IN1.phi'] = bs2_results['Stick1.phi'] elif model_name.startswith('Kurtosis'): fit_results = get_model_fit('Tensor') inits.update(get_subset(free_parameters, fit_results)) inits.update({ 'KurtosisTensor.' + key: fit_results['Tensor.' + key] for key in ['theta', 'phi', 'psi', 'd', 'dperp0', 'dperp1'] }) elif model_name.startswith('CHARMED_r'): nmr_dir = model_name[len('CHARMED_r'):len('CHARMED_r') + 1] fit_results = get_model_fit('BallStick_r' + nmr_dir) inits.update(get_subset(free_parameters, fit_results)) inits['Tensor.theta'] = fit_results['Stick0.theta'] inits['Tensor.phi'] = fit_results['Stick0.phi'] for dir_ind in range(int(nmr_dir)): inits['w_res{}.w'.format(dir_ind)] = fit_results[ 'w_stick{}.w'.format(dir_ind)] inits['CHARMEDRestricted{}.theta'.format( dir_ind)] = fit_results['Stick{}.theta'.format(dir_ind)] inits['CHARMEDRestricted{}.phi'.format(dir_ind)] = fit_results[ 'Stick{}.phi'.format(dir_ind)] elif model_name.startswith('BallRacket_r'): nmr_dir = model_name[len('BallRacket_r'):len('BallRacket_r') + 1] fit_results = get_model_fit('BallStick_r' + nmr_dir) inits.update(get_subset(free_parameters, fit_results)) for dir_ind in range(int(nmr_dir)): inits['w_res{}.w'.format(dir_ind)] = fit_results[ 'w_stick{}.w'.format(dir_ind)] inits['Racket{}.theta'.format(dir_ind)] = fit_results[ 'Stick{}.theta'.format(dir_ind)] inits['Racket{}.phi'.format(dir_ind)] = fit_results[ 'Stick{}.phi'.format(dir_ind)] elif model_name.startswith('AxCaliber'): fit_results = get_model_fit('BallStick_r1') inits.update(get_subset(free_parameters, fit_results)) inits['GDRCylinders.theta'] = fit_results['Stick0.theta'] inits['GDRCylinders.phi'] = fit_results['Stick0.phi'] elif model_name.startswith('ActiveAx'): fit_results = get_model_fit('BallStick_r1') inits.update(get_subset(free_parameters, fit_results)) inits['w_ic.w'] = fit_results['w_stick0.w'] / 2.0 inits['w_ec.w'] = fit_results['w_stick0.w'] / 2.0 inits['w_csf.w'] = fit_results['w_ball.w'] inits['CylinderGPD.theta'] = fit_results['Stick0.theta'] inits['CylinderGPD.phi'] = fit_results['Stick0.phi'] elif model_name.startswith('QMT_ReducedRamani'): inits['S0.s0'] = np.mean(input_data.signal4d, axis=-1) return inits
def signal4d(self): # return self._signal4d return restore_volumes(self._observation_list, self.mask)
def compute_fim(model, input_data, optimization_results, output_folder=None, cl_device_ind=None, cl_load_balancer=None, initialization_data=None): """Compute the Fisher Information Matrix (FIM). This is typically done as post-processing step during the model fitting process, but can also be performed separately after optimization. Since the FIM depends on which parameters were optimized, results will change if different parameters are fixed. That is, this function will compute the FIM for every estimable parameter (free-non-fixed parameters). If you want to have the exact same FIM results as when you computed the FIM as optimization post-processing it is important to have exactly the same maps fixed. Contrary to the post-processing of the optimization maps, all FIM results are written to a single sub-folder in the provided output folder. Args: model (str or :class:`~mdt.models.base.EstimableModel`): The name of a composite model or an implementation of a composite model. input_data (:class:`~mdt.lib.input_data.MRIInputData`): the input data object containing all the info needed for the model fitting. optimization_results (dict or str): the optimization results, either a dictionary with results or the path to a folder. output_folder (string): Optionally, the path to the folder where to place the output cl_device_ind (List[Union[mot.lib.cl_environments.CLEnvironment, int]] or mot.lib.cl_environments.CLEnvironment or int): the CL devices to use. Either provide MOT CLEnvironment's or indices from into the list from the function mdt.get_cl_devices(). cl_load_balancer (mot.lib.load_balancers.LoadBalancer or Tuple[float]): the load balancer to use. Can also be an array of fractions (summing to 1) with one fraction per device. For example, for two devices one can specify ``cl_load_balancer = [0.3, 0.7]`` to let one device to more work than another. initialization_data (dict): provides (extra) initialization data to use during model fitting. This dictionary can contain the following elements: * ``inits``: dictionary with per parameter an initialization point * ``fixes``: dictionary with per parameter a fixed point, this will remove that parameter from the fitting * ``lower_bounds``: dictionary with per parameter a lower bound * ``upper_bounds``: dictionary with per parameter a upper bound * ``unfix``: a list of parameters to unfix For example:: initialization_data = { 'fixes': {'Stick0.theta: np.array(...), ...}, 'inits': {...} } Returns: dict: all the computed FIM maps in a flattened dictionary. """ initialization_data = initialization_data or {} if isinstance(optimization_results, str): optimization_results = get_all_nifti_data(optimization_results) if not check_user_components(): init_user_settings(pass_if_exists=True) cl_runtime_info = CLRuntimeInfo(cl_environments=cl_device_ind, double_precision=True, load_balancer=cl_load_balancer) if isinstance(model, str): model_name = model model_instance = get_model(model)() else: model_name = model.name model_instance = model model_instance.set_input_data(input_data) initialization_data = SimpleInitializationData(**initialization_data) initialization_data.apply_to_model(model_instance, input_data) with mot.configuration.config_context(CLRuntimeAction(cl_runtime_info)): opt_points = create_roi(optimization_results, input_data.mask) opt_array = combine_dict_to_array(opt_points, model_instance.get_free_param_names()) covars = model_instance.compute_covariance_matrix(opt_array) covariance_names = model_instance.get_covariance_output_names() return_results = {} for ind, name in enumerate(covariance_names): if name.endswith('.std'): return_results[name] = np.nan_to_num(np.sqrt(covars[..., ind])) else: return_results[name] = covars[..., ind] return_results = restore_volumes(return_results, input_data.mask) write_volume_maps(return_results, os.path.join(output_folder, model_name, 'FIM')) return return_results