Source code for SALib.sample.morris.brute

""" """

from SALib.sample.morris.strategy import Strategy
from scipy.special import comb as nchoosek  # type: ignore
from itertools import combinations, islice
import sys
import numpy as np  # type: ignore
from typing import List


[docs] class BruteForce(Strategy): """Implements the brute force optimisation strategy""" def _sample( self, input_sample, num_samples, num_params, k_choices, num_groups=None ): return self.brute_force_most_distant( input_sample, num_samples, num_params, k_choices, num_groups )
[docs] def brute_force_most_distant( self, input_sample: np.ndarray, num_samples: int, num_params: int, k_choices: int, num_groups: int = None, ) -> List: """Use brute force method to find most distant trajectories Parameters ---------- input_sample : numpy.ndarray num_samples : int The number of samples to generate num_params : int The number of parameters k_choices : int The number of optimal trajectories num_groups : int, default=None The number of groups Returns ------- list """ scores = self.find_most_distant( input_sample, num_samples, num_params, k_choices, num_groups ) maximum_combo = self.find_maximum(scores, num_samples, k_choices) return maximum_combo
[docs] def find_most_distant( self, input_sample: np.ndarray, num_samples: int, num_params: int, k_choices: int, num_groups: int = None, ) -> np.ndarray: """ Finds the 'k_choices' most distant choices from the 'num_samples' trajectories contained in 'input_sample' Parameters ---------- input_sample : numpy.ndarray num_samples : int The number of samples to generate num_params : int The number of parameters k_choices : int The number of optimal trajectories num_groups : int, default=None The number of groups Returns ------- numpy.ndarray """ # Now evaluate the (N choose k_choices) possible combinations if nchoosek(num_samples, k_choices) >= sys.maxsize: raise ValueError("Number of combinations is too large") number_of_combinations = int(nchoosek(num_samples, k_choices)) # First compute the distance matrix for each possible pairing # of trajectories and store in a shared-memory array distance_matrix = self.compute_distance_matrix( input_sample, num_samples, num_params, num_groups ) # Initialise the output array chunk = int(1e6) if chunk > number_of_combinations: chunk = number_of_combinations counter = 0 # Generate a list of all the possible combinations combo_gen = combinations(range(num_samples), k_choices) scores = np.zeros(number_of_combinations, dtype=np.float32) # Generate the pairwise indices once pairwise = np.array([y for y in combinations(range(k_choices), 2)]) mappable = self.mappable for combos in self.grouper(chunk, combo_gen): scores[(counter * chunk) : ((counter + 1) * chunk)] = mappable( combos, pairwise, distance_matrix ) counter += 1 return scores
[docs] @staticmethod def grouper(n, iterable): it = iter(iterable) while True: chunk = tuple(islice(it, n)) if not chunk: return yield chunk
[docs] @staticmethod def mappable(combos, pairwise, distance_matrix): """ Obtains scores from the distance_matrix for each pairwise combination held in the combos array Parameters ---------- combos : numpy.ndarray pairwise : numpy.ndarray distance_matrix : numpy.ndarray """ combos = np.array(combos) # Create a list of all pairwise combination for each combo in combos combo_list = combos[ :, pairwise[:,], ] addresses = (combo_list[:, :, 1], combo_list[:, :, 0]) all_distances = distance_matrix[addresses] new_scores = np.sqrt(np.einsum("ij,ij->i", all_distances, all_distances)) return new_scores
[docs] def find_maximum(self, scores, N, k_choices): """Finds the `k_choices` maximum scores from `scores` Parameters ---------- scores : numpy.ndarray N : int k_choices : int Returns ------- list """ if not isinstance(scores, np.ndarray): raise TypeError("Scores input is not a numpy array") index_of_maximum = int(scores.argmax()) maximum_combo = self.nth( combinations(range(N), k_choices), index_of_maximum, None ) return sorted(maximum_combo)
[docs] @staticmethod def nth(iterable, n, default=None): """Returns the nth item or a default value Parameters ---------- iterable : iterable n : int default : default=None The default value to return """ if not isinstance(n, int): raise TypeError("n is not an integer") return next(islice(iterable, n, None), default)