Ejemplo n.º 1
0
    def _maximize_over_fixed_effects(self):
        """
        Update the model fixed effects for which no closed-form update is available (i.e. based on sufficient
        statistics).
        """

        # Default optimizer, if not initialized in the launcher.
        # Should better be done in a dedicated initializing method. TODO.
        if self.statistical_model.has_maximization_procedure is not None \
                and self.statistical_model.has_maximization_procedure:
            self.statistical_model.maximize(self.individual_RER, self.dataset)

        else:
            if self.gradient_based_estimator is None:
                self.gradient_based_estimator = ScipyOptimize()
                self.gradient_based_estimator.statistical_model = self.statistical_model
                self.gradient_based_estimator.dataset = self.dataset
                self.gradient_based_estimator.optimized_log_likelihood = 'class2'
                self.gradient_based_estimator.max_iterations = 5
                self.gradient_based_estimator.max_line_search_iterations = 10
                self.gradient_based_estimator.memory_length = 5
                self.gradient_based_estimator.convergence_tolerance = 1e-6
                self.gradient_based_estimator.print_every_n_iters = 1
                self.gradient_based_estimator.save_every_n_iters = 100000

            # Print information only when wanted.
            self.gradient_based_estimator.verbose = not self.current_iteration % self.print_every_n_iters

            if self.gradient_based_estimator.verbose > 0:
                print('')
                print('[ maximizing over the fixed effects with the ' +
                      self.gradient_based_estimator.name + ' optimizer ]')

            self.gradient_based_estimator.individual_RER = self.individual_RER

            success = False
            while not success:
                try:
                    self.gradient_based_estimator.update()
                    success = True
                except RuntimeError as error:
                    print('>> ' + str(error.args[0]) + ' [ in mcmc_saem ]')
                    self.statistical_model.adapt_to_error(error.args[1])

                if self.gradient_based_estimator.verbose > 0:
                    print('')
                    print('[ end of the gradient-based maximization ]')
Ejemplo n.º 2
0
def estimate_geodesic_regression(xml_parameters):
    print('')
    print('[ estimate_geodesic_regression function ]')
    print('')
    """
    Create the dataset object.
    """

    dataset = create_dataset(xml_parameters.dataset_filenames,
                             xml_parameters.visit_ages,
                             xml_parameters.subject_ids,
                             xml_parameters.template_specifications)

    assert dataset.is_time_series(
    ), "Cannot run a geodesic regression on a non-time_series dataset."
    """
    Create the model object.
    """

    model = instantiate_geodesic_regression_model(xml_parameters, dataset)
    """
    Create the estimator object.
    """

    if xml_parameters.optimization_method_type == 'GradientAscent'.lower():
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.scale_initial_step_size = xml_parameters.scale_initial_step_size
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

    elif xml_parameters.optimization_method_type == 'ScipyLBFGS'.lower():
        estimator = ScipyOptimize()
        estimator.memory_length = xml_parameters.memory_length
        if not model.freeze_template and model.use_sobolev_gradient and estimator.memory_length > 1:
            print(
                '>> Using a Sobolev gradient for the template data with the ScipyLBFGS estimator memory length '
                'being larger than 1. Beware: that can be tricky.')
            # estimator.memory_length = 1
            # msg = 'Impossible to use a Sobolev gradient for the template data with the ScipyLBFGS estimator memory ' \
            #       'length being larger than 1. Overriding the "memory_length" option, now set to "1".'
            # warnings.warn(msg)

    else:
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.scale_initial_step_size = xml_parameters.scale_initial_step_size
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

        msg = 'Unknown optimization-method-type: \"' + xml_parameters.optimization_method_type \
              + '\". Defaulting to GradientAscent.'
        warnings.warn(msg)

    estimator.max_iterations = xml_parameters.max_iterations
    estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
    estimator.convergence_tolerance = xml_parameters.convergence_tolerance

    estimator.print_every_n_iters = xml_parameters.print_every_n_iters
    estimator.save_every_n_iters = xml_parameters.save_every_n_iters

    estimator.dataset = dataset
    estimator.statistical_model = model
    """
    Launch.
    """

    if not os.path.exists(Settings().output_dir):
        os.makedirs(Settings().output_dir)

    model.name = 'GeodesicRegression'
    print('')
    print('[ update method of the ' + estimator.name + ' optimizer ]')

    start_time = time.time()
    estimator.update()
    estimator.write()
    end_time = time.time()
    print('>> Estimation took: ' +
          str(time.strftime("%H:%M:%S", time.gmtime(end_time - start_time))))

    return model
Ejemplo n.º 3
0
def estimate_longitudinal_atlas(xml_parameters):
    print('')
    print('[ estimate_longitudinal_atlas function ]')
    print('')
    """
    Create the dataset object.
    """

    dataset = create_dataset(xml_parameters.dataset_filenames,
                             xml_parameters.visit_ages,
                             xml_parameters.subject_ids,
                             xml_parameters.template_specifications)
    """
    Create the model object.
    """

    model, individual_RER = instantiate_longitudinal_atlas_model(
        xml_parameters, dataset)
    """
    Create the estimator object.
    """

    if xml_parameters.optimization_method_type == 'GradientAscent'.lower():
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.scale_initial_step_size = xml_parameters.scale_initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

    elif xml_parameters.optimization_method_type == 'ScipyLBFGS'.lower():
        estimator = ScipyOptimize()
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.memory_length = xml_parameters.memory_length
        if not model.is_frozen[
                'template_data'] and model.use_sobolev_gradient and estimator.memory_length > 1:
            print(
                '>> Using a Sobolev gradient for the template data with the ScipyLBFGS estimator memory length '
                'being larger than 1. Beware: that can be tricky.')
            # estimator.memory_length = 1
            # msg = 'Impossible to use a Sobolev gradient for the template data with the ScipyLBFGS estimator memory ' \
            #       'length being larger than 1. Overriding the "memory_length" option, now set to "1".'
            # warnings.warn(msg)

    elif xml_parameters.optimization_method_type == 'McmcSaem'.lower():
        sampler = SrwMhwgSampler()

        # Onset age proposal distribution.
        onset_age_proposal_distribution = MultiScalarNormalDistribution()
        onset_age_proposal_distribution.set_variance_sqrt(
            xml_parameters.onset_age_proposal_std)
        sampler.individual_proposal_distributions[
            'onset_age'] = onset_age_proposal_distribution

        # Log-acceleration proposal distribution.
        log_acceleration_proposal_distribution = MultiScalarNormalDistribution(
        )
        log_acceleration_proposal_distribution.set_variance_sqrt(
            xml_parameters.log_acceleration_proposal_std)
        sampler.individual_proposal_distributions[
            'log_acceleration'] = log_acceleration_proposal_distribution

        # Sources proposal distribution.
        sources_proposal_distribution = MultiScalarNormalDistribution()
        sources_proposal_distribution.set_variance_sqrt(
            xml_parameters.sources_proposal_std)
        sampler.individual_proposal_distributions[
            'sources'] = sources_proposal_distribution

        estimator = McmcSaem()
        estimator.sampler = sampler
        estimator.sample_every_n_mcmc_iters = xml_parameters.sample_every_n_mcmc_iters
        estimator.print_every_n_iters = xml_parameters.print_every_n_iters

        # Gradient-based estimator.
        # estimator.gradient_based_estimator = ScipyOptimize()
        # estimator.gradient_based_estimator.memory_length = 5

        estimator.gradient_based_estimator = GradientAscent()
        estimator.gradient_based_estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.gradient_based_estimator.scale_initial_step_size = False
        estimator.gradient_based_estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.gradient_based_estimator.line_search_expand = xml_parameters.line_search_expand

        estimator.gradient_based_estimator.statistical_model = model
        estimator.gradient_based_estimator.dataset = dataset
        estimator.gradient_based_estimator.optimized_log_likelihood = 'class2'
        estimator.gradient_based_estimator.max_iterations = 5
        estimator.gradient_based_estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.gradient_based_estimator.convergence_tolerance = xml_parameters.convergence_tolerance
        estimator.gradient_based_estimator.print_every_n_iters = 1
        estimator.gradient_based_estimator.save_every_n_iters = 100000

    else:
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.scale_initial_step_size = xml_parameters.scale_initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

        msg = 'Unknown optimization-method-type: \"' + xml_parameters.optimization_method_type \
              + '\". Defaulting to GradientAscent.'
        warnings.warn(msg)

    estimator.optimized_log_likelihood = xml_parameters.optimized_log_likelihood

    estimator.max_iterations = xml_parameters.max_iterations
    estimator.convergence_tolerance = xml_parameters.convergence_tolerance

    estimator.print_every_n_iters = xml_parameters.print_every_n_iters
    estimator.save_every_n_iters = xml_parameters.save_every_n_iters

    estimator.dataset = dataset
    estimator.statistical_model = model

    # Initial random effects realizations
    estimator.individual_RER = individual_RER
    """
    Launch.
    """

    if not os.path.exists(Settings().output_dir):
        os.makedirs(Settings().output_dir)

    model.name = 'LongitudinalAtlas'
    print('')
    print('[ update method of the ' + estimator.name + ' optimizer ]')
    print('')

    start_time = time.time()
    estimator.update()
    estimator.write()
    end_time = time.time()
    print('>> Estimation took: ' +
          str(time.strftime("%H:%M:%S", time.gmtime(end_time - start_time))))

    return model
Ejemplo n.º 4
0
class McmcSaem(AbstractEstimator):
    """
    GradientAscent object class.
    An estimator is an algorithm which updates the fixed effects of a statistical model.

    """

    ####################################################################################################################
    ### Constructor:
    ####################################################################################################################

    def __init__(self):
        AbstractEstimator.__init__(self)
        self.name = 'McmcSaem'

        self.current_mcmc_iteration = 0

        self.gradient_based_estimator = None
        self.sample_every_n_mcmc_iters = None

        self.sampler = None
        self.sufficient_statistics = None  # Dictionary of numpy arrays.
        self.number_of_burn_in_iterations = None  # Number of iterations without memory.

        self.current_acceptance_rates = {
        }  # Acceptance rates of the current iteration.
        self.average_acceptance_rates = {
        }  # Mean acceptance rates, computed over all past iterations.

        self.memory_window_size = 1  # Size of the averaging window for the acceptance rates.
        self.current_acceptance_rates_in_window = None  # Memory of the last memory_window_size acceptance rates.
        self.average_acceptance_rates_in_window = None  # Moving average of current_acceptance_rates_in_window.

        self.model_parameters_trajectory = None  # Memory of the model parameters along the estimation.
        self.save_model_parameters_every_n_iters = None  # Resolution of the model parameters trajectory.

        self.individual_random_effects_samples_stack = None  # Stack of the last individual random effect samples.

    ####################################################################################################################
    ### Public methods:
    ####################################################################################################################

    def update(self):
        """
        Runs the MCMC-SAEM algorithm and updates the statistical model.
        """

        # Initialization -----------------------------------------------------------------------------------------------
        self._initialize_number_of_burn_in_iterations()
        self._initialize_acceptance_rate_information()
        sufficient_statistics = self._initialize_sufficient_statistics()
        self._initialize_model_parameters_trajectory()
        self._initialize_individual_random_effects_samples_stack()

        # Ensures that all the model fixed effects are initialized.
        self.statistical_model.update_fixed_effects(self.dataset,
                                                    sufficient_statistics)

        # Print initial console information.
        print('------------------------------------- Iteration: ' +
              str(self.current_iteration) +
              ' -------------------------------------')
        print('>> MCMC-SAEM algorithm launched for ' +
              str(self.max_iterations) + ' iterations (' +
              str(self.number_of_burn_in_iterations) +
              ' iterations of burn-in).')
        self.statistical_model.print(self.individual_RER)

        # Initialization of the average random effects realizations.
        averaged_population_RER = {
            key: np.zeros(value.shape)
            for key, value in self.population_RER.items()
        }
        averaged_individual_RER = {
            key: np.zeros(value.shape)
            for key, value in self.individual_RER.items()
        }

        # Main loop ----------------------------------------------------------------------------------------------------
        while self.current_iteration < self.max_iterations:
            self.current_iteration += 1
            step = self._compute_step_size()

            # Simulation.
            current_model_terms = None
            for n in range(self.sample_every_n_mcmc_iters):
                self.current_mcmc_iteration += 1

                # Single iteration of the MCMC.
                self.current_acceptance_rates, current_model_terms = self.sampler.sample(
                    self.statistical_model, self.dataset, self.population_RER,
                    self.individual_RER, current_model_terms)

                # Adapt proposal variances.
                self._update_acceptance_rate_information()
                if not (self.current_mcmc_iteration % self.memory_window_size):
                    self.average_acceptance_rates_in_window \
                        = {key: np.mean(self.current_acceptance_rates_in_window[key])
                           for key in self.sampler.individual_proposal_distributions.keys()}
                    self.sampler.adapt_proposal_distributions(
                        self.average_acceptance_rates_in_window,
                        self.current_mcmc_iteration,
                        not self.current_iteration % self.print_every_n_iters
                        and n == self.sample_every_n_mcmc_iters - 1)

            # Maximization for the class 1 fixed effects.
            sufficient_statistics = self.statistical_model.compute_sufficient_statistics(
                self.dataset,
                self.population_RER,
                self.individual_RER,
                model_terms=current_model_terms)
            self.sufficient_statistics = {
                key: value + step * (sufficient_statistics[key] - value)
                for key, value in self.sufficient_statistics.items()
            }
            self.statistical_model.update_fixed_effects(
                self.dataset, self.sufficient_statistics)

            # Maximization for the class 2 fixed effects.
            fixed_effects_before_maximization = self.statistical_model.get_fixed_effects(
            )
            self._maximize_over_fixed_effects()
            fixed_effects_after_maximization = self.statistical_model.get_fixed_effects(
            )
            fixed_effects = {
                key:
                value + step * (fixed_effects_after_maximization[key] - value)
                for key, value in fixed_effects_before_maximization.items()
            }
            self.statistical_model.set_fixed_effects(fixed_effects)

            # Averages the random effect realizations in the concentration phase.
            if step < 1.0:
                coefficient_1 = float(self.current_iteration + 1 -
                                      self.number_of_burn_in_iterations)
                coefficient_2 = (coefficient_1 - 1.0) / coefficient_1
                averaged_population_RER = {
                    key: value * coefficient_2 +
                    self.population_RER[key] / coefficient_1
                    for key, value in averaged_population_RER.items()
                }
                averaged_individual_RER = {
                    key: value * coefficient_2 +
                    self.individual_RER[key] / coefficient_1
                    for key, value in averaged_individual_RER.items()
                }
                self._update_individual_random_effects_samples_stack()

            # Saving, printing, writing.
            if not (self.current_iteration %
                    self.save_model_parameters_every_n_iters):
                self._update_model_parameters_trajectory()
            if not (self.current_iteration % self.print_every_n_iters):
                self.print()
            if not (self.current_iteration % self.save_every_n_iters):
                self.write()

        # Finalization -------------------------------------------------------------------------------------------------
        self.population_RER = averaged_population_RER
        self.individual_RER = averaged_individual_RER

    def print(self):
        """
        Prints information.
        """
        # Iteration number.
        print('')
        print('------------------------------------- Iteration: ' +
              str(self.current_iteration) +
              ' -------------------------------------')

        # Averaged acceptance rates over all the past iterations.
        print('>> Average acceptance rates (all past iterations):')
        for random_effect_name, average_acceptance_rate in self.average_acceptance_rates.items(
        ):
            print('\t\t %.2f \t[ %s ]' %
                  (average_acceptance_rate, random_effect_name))

        # Let the model under optimization print information about itself.
        self.statistical_model.print(self.individual_RER)

    def write(self, population_RER=None, individual_RER=None):
        """
        Save the current results.
        """
        # Call the write method of the statistical model.
        if population_RER is None: population_RER = self.population_RER
        if individual_RER is None: individual_RER = self.individual_RER
        self.statistical_model.write(self.dataset,
                                     self.population_RER,
                                     self.individual_RER,
                                     update_fixed_effects=False)

        # Save the recorded model parameters trajectory.
        # self.model_parameters_trajectory is a list of dictionaries
        np.save(
            os.path.join(
                Settings().output_dir, self.statistical_model.name +
                '__EstimatedParameters__Trajectory.npy'),
            np.array(self.model_parameters_trajectory))

        # Save the memorized individual random effects samples.
        if self.current_iteration > self.number_of_burn_in_iterations:
            np.save(
                os.path.join(
                    Settings().output_dir, self.statistical_model.name +
                    '__EstimatedParameters__IndividualRandomEffectsSamples.npy'
                ), self.individual_random_effects_samples_stack)

    ####################################################################################################################
    ### Private_maximize_over_remaining_fixed_effects() method and associated utilities:
    ####################################################################################################################

    def _maximize_over_fixed_effects(self):
        """
        Update the model fixed effects for which no closed-form update is available (i.e. based on sufficient
        statistics).
        """

        # Default optimizer, if not initialized in the launcher.
        # Should better be done in a dedicated initializing method. TODO.
        if self.statistical_model.has_maximization_procedure is not None \
                and self.statistical_model.has_maximization_procedure:
            self.statistical_model.maximize(self.individual_RER, self.dataset)

        else:
            if self.gradient_based_estimator is None:
                self.gradient_based_estimator = ScipyOptimize()
                self.gradient_based_estimator.statistical_model = self.statistical_model
                self.gradient_based_estimator.dataset = self.dataset
                self.gradient_based_estimator.optimized_log_likelihood = 'class2'
                self.gradient_based_estimator.max_iterations = 5
                self.gradient_based_estimator.max_line_search_iterations = 10
                self.gradient_based_estimator.memory_length = 5
                self.gradient_based_estimator.convergence_tolerance = 1e-6
                self.gradient_based_estimator.print_every_n_iters = 1
                self.gradient_based_estimator.save_every_n_iters = 100000

            # Print information only when wanted.
            self.gradient_based_estimator.verbose = not self.current_iteration % self.print_every_n_iters

            if self.gradient_based_estimator.verbose > 0:
                print('')
                print('[ maximizing over the fixed effects with the ' +
                      self.gradient_based_estimator.name + ' optimizer ]')

            self.gradient_based_estimator.individual_RER = self.individual_RER

            success = False
            while not success:
                try:
                    self.gradient_based_estimator.update()
                    success = True
                except RuntimeError as error:
                    print('>> ' + str(error.args[0]) + ' [ in mcmc_saem ]')
                    self.statistical_model.adapt_to_error(error.args[1])

                if self.gradient_based_estimator.verbose > 0:
                    print('')
                    print('[ end of the gradient-based maximization ]')

    ####################################################################################################################
    ### Other private methods:
    ####################################################################################################################

    def _compute_step_size(self):
        aux = self.current_iteration - self.number_of_burn_in_iterations + 1
        if aux <= 0:
            return 1.0
        else:
            return aux**-0.9

    def _initialize_number_of_burn_in_iterations(self):
        if self.number_of_burn_in_iterations is None:
            # Because some models will set it manually (e.g. deep Riemannian models)
            if self.max_iterations > 4000:
                self.number_of_burn_in_iterations = self.max_iterations - 2000
            else:
                self.number_of_burn_in_iterations = int(self.max_iterations /
                                                        2)

    def _initialize_acceptance_rate_information(self):
        # Initialize average_acceptance_rates.
        self.average_acceptance_rates = {
            key: 0.0
            for key in self.sampler.individual_proposal_distributions.keys()
        }

        # Initialize current_acceptance_rates_in_window.
        self.current_acceptance_rates_in_window = {
            key: np.zeros((self.memory_window_size, ))
            for key in self.sampler.individual_proposal_distributions.keys()
        }
        self.average_acceptance_rates_in_window = {
            key: 0.0
            for key in self.sampler.individual_proposal_distributions.keys()
        }

    def _update_acceptance_rate_information(self):
        # Update average_acceptance_rates.
        coefficient_1 = float(self.current_mcmc_iteration)
        coefficient_2 = (coefficient_1 - 1.0) / coefficient_1
        self.average_acceptance_rates = {
            key: value * coefficient_2 +
            self.current_acceptance_rates[key] / coefficient_1
            for key, value in self.average_acceptance_rates.items()
        }

        # Update current_acceptance_rates_in_window.
        for key in self.current_acceptance_rates_in_window.keys():
            self.current_acceptance_rates_in_window[key][(self.current_mcmc_iteration - 1) % self.memory_window_size] \
                = self.current_acceptance_rates[key]

    def _initialize_sufficient_statistics(self):
        sufficient_statistics = self.statistical_model.compute_sufficient_statistics(
            self.dataset, self.population_RER, self.individual_RER)
        self.sufficient_statistics = {
            key: np.zeros(value.shape)
            for key, value in sufficient_statistics.items()
        }
        return sufficient_statistics

    ####################################################################################################################
    ### Model parameters trajectory saving methods:
    ####################################################################################################################

    def _initialize_model_parameters_trajectory(self):
        number_of_trajectory_points = min(self.max_iterations, 500)
        self.save_model_parameters_every_n_iters = max(
            1, int(self.max_iterations / float(number_of_trajectory_points)))
        self.model_parameters_trajectory = {}
        for (key, value) in self.statistical_model.get_fixed_effects(
                mode='all').items():
            self.model_parameters_trajectory[key] = np.zeros(
                (number_of_trajectory_points + 1, value.size))
            self.model_parameters_trajectory[key][0, :] = value.flatten()

    def _update_model_parameters_trajectory(self):
        for (key, value) in self.statistical_model.get_fixed_effects(
                mode='all').items():
            self.model_parameters_trajectory[key][
                int(self.current_iteration /
                    float(self.save_model_parameters_every_n_iters)
                    ), :] = value.flatten()

    def _get_vectorized_individual_RER(self):
        return np.concatenate(
            [value.flatten() for value in self.individual_RER.values()])

    def _initialize_individual_random_effects_samples_stack(self):
        number_of_concentration_iterations = self.max_iterations - self.number_of_burn_in_iterations
        self.individual_random_effects_samples_stack = {}
        for (key, value) in self.individual_RER.items():
            self.individual_random_effects_samples_stack[key] = np.zeros(
                (number_of_concentration_iterations, value.size))
            self.individual_random_effects_samples_stack[key][
                0, :] = value.flatten()

    def _update_individual_random_effects_samples_stack(self):
        for (key, value) in self.individual_RER.items():
            self.individual_random_effects_samples_stack[key][
                self.current_iteration - self.number_of_burn_in_iterations -
                1, :] = value.flatten()
Ejemplo n.º 5
0
def estimate_longitudinal_metric_model(xml_parameters):
    print('')
    print('[ estimate_longitudinal_metric_model function ]')
    print('')

    dataset = None

    # Two alternatives: scalar dataset or image dataset for now.
    observation_type = 'None'

    template_specifications = xml_parameters.template_specifications
    for val in template_specifications.values():
        if val['deformable_object_type'].lower() == 'scalar':
            dataset = read_and_create_scalar_dataset(xml_parameters)
            observation_type = 'scalar'
            #dataset.order_observations()
            break

    if dataset is None:
        dataset = read_and_create_image_dataset(
            xml_parameters.dataset_filenames, xml_parameters.visit_ages,
            xml_parameters.subject_ids, xml_parameters.template_specifications)
        observation_type = 'image'

    model, individual_RER = instantiate_longitudinal_metric_model(
        xml_parameters, dataset, observation_type=observation_type)

    if xml_parameters.optimization_method_type == 'GradientAscent'.lower():
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.scale_initial_step_size = xml_parameters.scale_initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand
        estimator.optimized_log_likelihood = xml_parameters.optimized_log_likelihood

    elif xml_parameters.optimization_method_type == 'ScipyLBFGS'.lower():
        estimator = ScipyOptimize()
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.memory_length = xml_parameters.memory_length
        # estimator.memory_length = 1
        # msg = 'Impossible to use a Sobolev gradient for the template data with the ScipyLBFGS estimator memory ' \
        #       'length being larger than 1. Overriding the "memory_length" option, now set to "1".'
        # warnings.warn(msg)

    elif xml_parameters.optimization_method_type == 'McmcSaem'.lower():
        sampler = SrwMhwgSampler()
        estimator = McmcSaem()
        estimator.sampler = sampler

        # Onset age proposal distribution.
        onset_age_proposal_distribution = MultiScalarNormalDistribution()
        onset_age_proposal_distribution.set_variance_sqrt(
            xml_parameters.onset_age_proposal_std)
        sampler.individual_proposal_distributions[
            'onset_age'] = onset_age_proposal_distribution

        # Log-acceleration proposal distribution.
        log_acceleration_proposal_distribution = MultiScalarNormalDistribution(
        )
        log_acceleration_proposal_distribution.set_variance_sqrt(
            xml_parameters.log_acceleration_proposal_std)
        sampler.individual_proposal_distributions[
            'log_acceleration'] = log_acceleration_proposal_distribution

        # Sources proposal distribution

        if model.number_of_sources > 0:
            sources_proposal_distribution = MultiScalarNormalDistribution()
            sources_proposal_distribution.set_variance_sqrt(
                xml_parameters.sources_proposal_std)
            sampler.individual_proposal_distributions[
                'sources'] = sources_proposal_distribution

        estimator.sample_every_n_mcmc_iters = xml_parameters.sample_every_n_mcmc_iters

        # Gradient-based estimator.
        estimator.gradient_based_estimator = GradientAscent()
        estimator.gradient_based_estimator.statistical_model = model
        estimator.gradient_based_estimator.dataset = dataset
        estimator.gradient_based_estimator.optimized_log_likelihood = 'class2'
        estimator.gradient_based_estimator.max_iterations = 5
        estimator.gradient_based_estimator.max_line_search_iterations = 5
        estimator.gradient_based_estimator.convergence_tolerance = 1e-2
        estimator.gradient_based_estimator.print_every_n_iters = 1
        estimator.gradient_based_estimator.save_every_n_iters = 100000
        estimator.gradient_based_estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.gradient_based_estimator.line_search_shrink = 0.5
        estimator.gradient_based_estimator.line_search_expand = 1.2
        estimator.gradient_based_estimator.scale_initial_step_size = True
        estimator.number_of_burn_in_iterations = xml_parameters.max_iterations

    else:
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

        msg = 'Unknown optimization-method-type: \"' + xml_parameters.optimization_method_type \
              + '\". Defaulting to GradientAscent.'
        warnings.warn(msg)

    estimator.max_iterations = xml_parameters.max_iterations
    estimator.convergence_tolerance = xml_parameters.convergence_tolerance

    estimator.print_every_n_iters = xml_parameters.print_every_n_iters
    estimator.save_every_n_iters = xml_parameters.save_every_n_iters

    estimator.dataset = dataset
    estimator.statistical_model = model

    # Initial random effects realizations
    estimator.individual_RER = individual_RER
    """
    Launch.
    """

    if not os.path.exists(Settings().output_dir):
        os.makedirs(Settings().output_dir)

    model.name = 'LongitudinalMetricModel'
    print('')
    print('[ update method of the ' + estimator.name + ' optimizer ]')

    start_time = time.time()
    estimator.update()
    estimator.write()
    end_time = time.time()
    print('>> Estimation took: ' + str(
        time.strftime("%d days, %H hours, %M minutes and %S seconds.",
                      time.gmtime(end_time - start_time))))
def estimate_longitudinal_registration_for_subject(args, overwrite=True):
    i, general_settings, xml_parameters, registration_output_path, \
    full_subject_ids, full_dataset_filenames, full_visit_ages = args

    Settings().initialize(general_settings)

    """
    Create the dataset object.
    """

    xml_parameters.dataset_filenames = [full_dataset_filenames[i]]
    xml_parameters.visit_ages = [full_visit_ages[i]]
    xml_parameters.subject_ids = [full_subject_ids[i]]

    dataset = create_dataset([full_dataset_filenames[i]], [full_visit_ages[i]], [full_subject_ids[i]],
                             xml_parameters.template_specifications)

    """
    Create a dedicated output folder for the current subject, adapt the global settings.
    """

    subject_registration_output_path = os.path.join(
        registration_output_path, 'LongitudinalRegistration__subject_' + full_subject_ids[i])

    if not overwrite and os.path.isdir(subject_registration_output_path):
        return None

    print('')
    print('[ longitudinal registration of subject ' + full_subject_ids[i] + ' ]')
    print('')

    if os.path.isdir(subject_registration_output_path):
        shutil.rmtree(subject_registration_output_path)
        os.mkdir(subject_registration_output_path)

    Settings().output_dir = subject_registration_output_path
    Settings().state_file = os.path.join(Settings().output_dir, 'pydef_state.p')

    """
    Create the model object.
    """

    model, individual_RER = instantiate_longitudinal_atlas_model(xml_parameters, dataset)

    # In case of given initial random effect realizations, select only the relevant ones.
    for (xml_parameter, random_effect_name) \
            in zip([xml_parameters.initial_onset_ages,
                    xml_parameters.initial_log_accelerations,
                    xml_parameters.initial_sources],
                   ['onset_age', 'log_acceleration', 'sources']):
        if xml_parameter is not None and individual_RER[random_effect_name].shape[0] > 1:
            individual_RER[random_effect_name] = np.array([individual_RER[random_effect_name][i]])

    """
    Create the estimator object.
    """

    if xml_parameters.optimization_method_type == 'GradientAscent'.lower():
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

    elif xml_parameters.optimization_method_type == 'ScipyLBFGS'.lower():
        estimator = ScipyOptimize()
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.memory_length = xml_parameters.memory_length
        if not model.is_frozen['template_data'] and model.use_sobolev_gradient and estimator.memory_length > 1:
            print('>> Using a Sobolev gradient for the template data with the ScipyLBFGS estimator memory length '
                  'being larger than 1. Beware: that can be tricky.')

    elif xml_parameters.optimization_method_type == 'ScipyPowell'.lower():
        estimator = ScipyOptimize()
        estimator.method = 'Powell'

    elif xml_parameters.optimization_method_type == 'McmcSaem'.lower():
        sampler = SrwMhwgSampler()

        momenta_proposal_distribution = MultiScalarNormalDistribution()
        # initial_control_points = model.get_control_points()
        # momenta_proposal_distribution.set_mean(np.zeros(initial_control_points.size,))
        momenta_proposal_distribution.set_variance_sqrt(xml_parameters.momenta_proposal_std)
        sampler.individual_proposal_distributions['momenta'] = momenta_proposal_distribution

        estimator = McmcSaem()
        estimator.sampler = sampler
        estimator.sample_every_n_mcmc_iters = xml_parameters.sample_every_n_mcmc_iters

    else:
        estimator = GradientAscent()
        estimator.initial_step_size = xml_parameters.initial_step_size
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.line_search_shrink = xml_parameters.line_search_shrink
        estimator.line_search_expand = xml_parameters.line_search_expand

        msg = 'Unknown optimization-method-type: \"' + xml_parameters.optimization_method_type \
              + '\". Defaulting to GradientAscent.'
        warnings.warn(msg)

    estimator.max_iterations = xml_parameters.max_iterations
    estimator.convergence_tolerance = xml_parameters.convergence_tolerance

    estimator.print_every_n_iters = xml_parameters.print_every_n_iters
    estimator.save_every_n_iters = xml_parameters.save_every_n_iters

    estimator.dataset = dataset
    estimator.statistical_model = model

    # Initial random effects realizations
    estimator.individual_RER = individual_RER

    """
    Launch.
    """

    if not os.path.exists(Settings().output_dir): os.makedirs(Settings().output_dir)

    model.name = 'LongitudinalRegistration'
    print('')
    print('[ update method of the ' + estimator.name + ' optimizer ]')

    try:
        start_time = time.time()
        estimator.update()
        model._write_model_parameters(estimator.individual_RER)
        end_time = time.time()

    except RuntimeError as error:
        print('>> Failure of the longitudinal registration procedure for subject %s: %s' % (full_subject_ids[i], error))

        if not (estimator.name.lower() == 'scipyoptimize' and estimator.method.lower() == 'scipypowell'):
            print('>> Second try with the ScipyPowell optimiser.')

            estimator = ScipyOptimize()
            estimator.method = 'Powell'
            estimator.max_iterations = xml_parameters.max_iterations
            estimator.convergence_tolerance = xml_parameters.convergence_tolerance
            estimator.print_every_n_iters = xml_parameters.print_every_n_iters
            estimator.save_every_n_iters = xml_parameters.save_every_n_iters
            estimator.dataset = dataset
            estimator.statistical_model = model
            estimator.individual_RER = individual_RER

            start_time = time.time()
            estimator.update()
            model._write_model_parameters(estimator.individual_RER)
            end_time = time.time()

    print('')
    print('>> Estimation took: ' + str(time.strftime("%H:%M:%S", time.gmtime(end_time - start_time))))

    return model
def estimate_longitudinal_registration_for_subject(args):
    i, general_settings, xml_parameters, registration_output_path, \
    full_dataset = args

    Settings().initialize(general_settings)

    print('')
    print('[ longitudinal registration of subject ' +
          full_dataset.subject_ids[i] + ' ]')
    print('')
    """
    Create the dataset object.
    """

    dataset = create_image_dataset([
        full_dataset.subject_ids[i] for _ in range(len(full_dataset.times[i]))
    ], full_dataset.deformable_objects[i], full_dataset.times[i])
    """
    Create a dedicated output folder for the current subject, adapt the global settings.
    """

    subject_registration_output_path = os.path.join(
        registration_output_path, 'LongitudinalMetricRegistration__subject_' +
        full_dataset.subject_ids[i])
    if os.path.isdir(subject_registration_output_path):
        shutil.rmtree(subject_registration_output_path)
        os.mkdir(subject_registration_output_path)

    Settings().output_dir = subject_registration_output_path
    Settings().state_file = os.path.join(subject_registration_output_path,
                                         'pydef_state.p')
    """
    Create the model object.
    """
    Settings().number_of_threads = 1

    model, individual_RER = instantiate_longitudinal_metric_model(
        xml_parameters, dataset, observation_type='image')

    model.is_frozen['v0'] = True
    model.is_frozen['p0'] = True
    model.is_frozen['reference_time'] = True
    model.is_frozen['onset_age_variance'] = True
    model.is_frozen['log_acceleration_variance'] = True
    model.is_frozen['noise_variance'] = True
    model.is_frozen['metric_parameters'] = True
    model.is_frozen['noise_variance'] = True

    # In case of given initial random effect realizations, select only the relevant ones.
    for (xml_parameter, random_effect_name) \
            in zip([xml_parameters.initial_onset_ages,
                    xml_parameters.initial_log_accelerations],
                   ['onset_age', 'log_acceleration']):
        if xml_parameter is not None and len(
                individual_RER[random_effect_name].shape) > 1:
            individual_RER[random_effect_name] = np.array(
                [individual_RER[random_effect_name][i, :]])
    """
    Create the estimator object.
    """

    if xml_parameters.optimization_method_type == 'ScipyPowell'.lower():
        estimator = ScipyOptimize()
        estimator.method = 'Powell'

    elif xml_parameters.optimization_method_type == 'ScipyLBFGS'.lower():
        estimator = ScipyOptimize()
        estimator.max_line_search_iterations = xml_parameters.max_line_search_iterations
        estimator.memory_length = xml_parameters.memory_length

    estimator.max_iterations = xml_parameters.max_iterations
    estimator.convergence_tolerance = xml_parameters.convergence_tolerance

    estimator.print_every_n_iters = xml_parameters.print_every_n_iters
    estimator.save_every_n_iters = xml_parameters.save_every_n_iters

    estimator.dataset = dataset
    estimator.statistical_model = model

    # Initial random effects realizations
    estimator.individual_RER = individual_RER
    """
    Launch.
    """

    if not os.path.exists(Settings().output_dir):
        os.makedirs(Settings().output_dir)

    model.name = 'LongitudinalMetricRegistration'
    print('')
    print('[ update method of the ' + estimator.name + ' optimizer ]')

    start_time = time.time()
    estimator.update()
    model._write_model_parameters()
    model._write_model_predictions(dataset,
                                   estimator.individual_RER,
                                   sample=False)
    model._write_individual_RER(dataset, estimator.individual_RER)
    end_time = time.time()
    print('')
    print('>> Estimation took: ' +
          str(time.strftime("%H:%M:%S", time.gmtime(end_time - start_time))))

    return model