Module bayesvalidrox.surrogate_models.exploration

Expand source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from scipy.spatial import distance


class Exploration:
    """
    Created based on the Surrogate Modeling Toolbox (SUMO) [1].

    [1] Gorissen, D., Couckuyt, I., Demeester, P., Dhaene, T. and Crombecq, K.,
        2010. A surrogate modeling and adaptive sampling toolbox for computer
        based design. Journal of machine learning research.-Cambridge, Mass.,
        11, pp.2051-2055. sumo@sumo.intec.ugent.be - http://sumo.intec.ugent.be

    Attributes
    ----------
    MetaModel : obj
        MetaModel object.
    n_candidate : int
        Number of candidate samples.
    mc_criterion : str
        Selection crieterion. The default is `'mc-intersite-proj-th'`. Another
        option is `'mc-intersite-proj'`.
    w : int
        Number of random points in the domain for each sample of the
        training set.
    """

    def __init__(self, MetaModel, n_candidate,
                 mc_criterion='mc-intersite-proj-th'):
        self.MetaModel = MetaModel
        self.Marginals = []
        self.n_candidate = n_candidate
        self.mc_criterion = mc_criterion
        self.w = 100

    def get_exploration_samples(self):
        """
        This function generates candidates to be selected as new design and
        their associated exploration scores.

        Returns
        -------
        all_candidates : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """
        MetaModel = self.MetaModel
        explore_method = MetaModel.ExpDesign.explore_method

        print("\n")
        print(f' The {explore_method}-Method is selected as the exploration '
              'method.')
        print("\n")

        if explore_method == 'Voronoi':
            # Generate samples using the Voronoi method
            all_candidates, exploration_scores = self.get_vornoi_samples()
        else:
            # Generate samples using the MC method
            all_candidates, exploration_scores = self.get_mc_samples()

        return all_candidates, exploration_scores

    # -------------------------------------------------------------------------
    def get_vornoi_samples(self):
        """
        This function generates samples based on voronoi cells and their
        corresponding scores

        Returns
        -------
        new_samples : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """

        mc_criterion = self.mc_criterion
        n_candidate = self.n_candidate
        # Get the Old ExpDesign #samples
        old_ED_X = self.MetaModel.ExpDesign.X
        ndim = old_ED_X.shape[1]

        # calculate error #averageErrors
        error_voronoi, all_candidates = self.approximate_voronoi(
            self.w, old_ED_X
            )

        # Pick the best candidate point in the voronoi cell
        # for each best sample
        selected_samples = np.empty((0, ndim))
        bad_samples = []

        for index in range(len(error_voronoi)):

            # get candidate new samples from voronoi tesselation
            candidates = self.closest_points[index]

            # get total number of candidates
            n_new_samples = candidates.shape[0]

            # still no candidate samples around this one, skip it!
            if n_new_samples == 0:
                print('The following sample has been skipped because there '
                      'were no candidate samples around it...')
                print(old_ED_X[index])
                bad_samples.append(index)
                continue

            # find candidate that is farthest away from any existing sample
            max_min_distance = 0
            best_candidate = 0
            min_intersite_dist = np.zeros((n_new_samples))
            min_projected_dist = np.zeros((n_new_samples))

            for j in range(n_new_samples):

                new_samples = np.vstack((old_ED_X, selected_samples))

                # find min distorted distance from all other samples
                euclidean_dist = self._build_dist_matrix_point(
                    new_samples, candidates[j], do_sqrt=True)
                min_euclidean_dist = np.min(euclidean_dist)
                min_intersite_dist[j] = min_euclidean_dist

                # Check if this is the maximum minimum distance from all other
                # samples
                if min_euclidean_dist >= max_min_distance:
                    max_min_distance = min_euclidean_dist
                    best_candidate = j

                # Projected distance
                projected_dist = distance.cdist(
                    new_samples, [candidates[j]], 'chebyshev')
                min_projected_dist[j] = np.min(projected_dist)

            if mc_criterion == 'mc-intersite-proj':
                weight_euclidean_dist = 0.5 * ((n_new_samples+1)**(1/ndim) - 1)
                weight_projected_dist = 0.5 * (n_new_samples+1)
                total_dist_scores = weight_euclidean_dist * min_intersite_dist
                total_dist_scores += weight_projected_dist * min_projected_dist

            elif mc_criterion == 'mc-intersite-proj-th':
                alpha = 0.5  # chosen (tradeoff)
                d_min = 2 * alpha / n_new_samples
                if any(min_projected_dist < d_min):
                    candidates = np.delete(
                        candidates, [min_projected_dist < d_min], axis=0
                        )
                    total_dist_scores = np.delete(
                        min_intersite_dist, [min_projected_dist < d_min],
                        axis=0
                        )
                else:
                    total_dist_scores = min_intersite_dist
            else:
                raise NameError(
                    'The MC-Criterion you requested is not available.'
                    )

            # Add the best candidate to the list of new samples
            best_candidate = np.argsort(total_dist_scores)[::-1][:n_candidate]
            selected_samples = np.vstack(
                (selected_samples, candidates[best_candidate])
                )

        self.new_samples = selected_samples
        self.exploration_scores = np.delete(error_voronoi, bad_samples, axis=0)

        return self.new_samples, self.exploration_scores

    # -------------------------------------------------------------------------
    def get_mc_samples(self, all_candidates=None):
        """
        This function generates random samples based on Global Monte Carlo
        methods and their corresponding scores, based on [1].

        [1] Crombecq, K., Laermans, E. and Dhaene, T., 2011. Efficient
            space-filling and non-collapsing sequential design strategies for
            simulation-based modeling. European Journal of Operational Research
            , 214(3), pp.683-696.
            DOI: https://doi.org/10.1016/j.ejor.2011.05.032

        Implemented methods to compute scores:
            1) mc-intersite-proj
            2) mc-intersite-proj-th

        Arguments
        ---------
        all_candidates : array, optional
            Samples to compute the scores for. The default is `None`. In this
            case, samples will be generated by defined model input marginals.

        Returns
        -------
        new_samples : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """
        MetaModel = self.MetaModel
        explore_method = MetaModel.ExpDesign.explore_method
        mc_criterion = self.mc_criterion
        if all_candidates is None:
            n_candidate = self.n_candidate
        else:
            n_candidate = all_candidates.shape[0]

        # Get the Old ExpDesign #samples
        old_ED_X = MetaModel.ExpDesign.X
        ndim = old_ED_X.shape[1]

        # ----- Compute the number of random points -----
        if all_candidates is None:
            # Generate MC Samples
            all_candidates = MetaModel.ExpDesign.generate_samples(
                self.n_candidate, explore_method
                )
        self.all_candidates = all_candidates

        # initialization
        new_samples = np.empty((0, ndim))
        min_intersite_dist = np.zeros((n_candidate))
        min_projected_dist = np.zeros((n_candidate))

        for i, candidate in enumerate(all_candidates):

            # find candidate that is farthest away from any existing sample
            maxMinDistance = 0

            new_samples = np.vstack((old_ED_X, new_samples))
            # find min distorted distance from all other samples
            euclidean_dist = self._build_dist_matrix_point(
                new_samples, candidate, do_sqrt=True
                )
            min_euclidean_dist = np.min(euclidean_dist)
            min_intersite_dist[i] = min_euclidean_dist

            # Check if this is the maximum minimum distance from all other
            # samples
            if min_euclidean_dist >= maxMinDistance:
                maxMinDistance = min_euclidean_dist

            # Projected distance
            projected_dist = distance.cdist(
                new_samples, [candidate], 'chebyshev'
                )
            min_projected_dist[i] = np.min(projected_dist)

        if mc_criterion == 'mc-intersite-proj':
            weight_euclidean_dist = ((n_candidate+1)**(1/ndim) - 1) * 0.5
            weight_projected_dist = (n_candidate+1) * 0.5
            total_dist_scores = weight_euclidean_dist * min_intersite_dist
            total_dist_scores += weight_projected_dist * min_projected_dist

        elif mc_criterion == 'mc-intersite-proj-th':
            alpha = 0.5  # chosen (tradeoff)
            d_min = 2 * alpha / n_candidate
            if any(min_projected_dist < d_min):
                all_candidates = np.delete(
                    all_candidates, [min_projected_dist < d_min], axis=0
                    )
                total_dist_scores = np.delete(
                    min_intersite_dist, [min_projected_dist < d_min], axis=0
                    )
            else:
                total_dist_scores = min_intersite_dist
        else:
            raise NameError('The MC-Criterion you requested is not available.')

        self.new_samples = all_candidates
        self.exploration_scores = total_dist_scores
        self.exploration_scores /= np.nansum(total_dist_scores)

        return self.new_samples, self.exploration_scores

    # -------------------------------------------------------------------------
    def approximate_voronoi(self, w, samples):
        """
        An approximate (monte carlo) version of Matlab's voronoi command.

        Arguments
        ---------
        samples : array
            Old experimental design to be used as center points for voronoi
            cells.

        Returns
        -------
        areas : array
            An approximation of the voronoi cells' areas.
        all_candidates: list of arrays
            A list of samples in each voronoi cell.
        """
        MetaModel = self.MetaModel

        n_samples = samples.shape[0]
        ndim = samples.shape[1]

        # Compute the number of random points
        n_points = w * samples.shape[0]
        # Generate w random points in the domain for each sample
        points = MetaModel.ExpDesign.generate_samples(n_points, 'random')
        self.all_candidates = points

        # Calculate the nearest sample to each point
        self.areas = np.zeros((n_samples))
        self.closest_points = [np.empty((0, ndim)) for i in range(n_samples)]

        # Compute the minimum distance from all the samples of old_ED_X for
        # each test point
        for idx in range(n_points):
            # calculate the minimum distance
            distances = self._build_dist_matrix_point(
                samples, points[idx], do_sqrt=True
                )
            closest_sample = np.argmin(distances)

            # Add to the voronoi list of the closest sample
            self.areas[closest_sample] = self.areas[closest_sample] + 1
            prev_closest_points = self.closest_points[closest_sample]
            self.closest_points[closest_sample] = np.vstack(
                (prev_closest_points, points[idx])
                )

        # Divide by the amount of points to get the estimated volume of each
        # voronoi cell
        self.areas /= n_points

        self.perc = np.max(self.areas * 100)

        self.errors = self.areas

        return self.areas, self.all_candidates

    # -------------------------------------------------------------------------
    def _build_dist_matrix_point(self, samples, point, do_sqrt=False):
        """
        Calculates the intersite distance of all points in samples from point.

        Parameters
        ----------
        samples : array of shape (n_samples, n_params)
            The old experimental design.
        point : array
            A candidate point.
        do_sqrt : bool, optional
            Whether to return distances or squared distances. The default is
            `False`.

        Returns
        -------
        distances : array
            Distances.

        """
        distances = distance.cdist(samples, np.array([point]), 'euclidean')

        # do square root?
        if do_sqrt:
            return distances
        else:
            return distances**2

#if __name__ == "__main__":
#    import scipy.stats as stats
#    import matplotlib.pyplot as plt
#    import matplotlib as mpl
#    import matplotlib.cm as cm
#    plt.rc('font', family='sans-serif', serif='Arial')
#    plt.rc('figure', figsize = (12, 8))
#    
#    def plotter(old_ED_X, all_candidates, exploration_scores):
#        global Bounds
#        
#        from scipy.spatial import Voronoi, voronoi_plot_2d
#        vor = Voronoi(old_ED_X)
#        
#        fig = voronoi_plot_2d(vor)
#        
#        # find min/max values for normalization
##        minima = min(exploration_scores)
##        maxima = max(exploration_scores)
##        
##        # normalize chosen colormap
##        norm = mpl.colors.Normalize(vmin=minima, vmax=maxima, clip=True)
##        mapper = cm.ScalarMappable(norm=norm, cmap=cm.Blues_r)
##        
##        for r in range(len(vor.point_region)):
##            region = vor.regions[vor.point_region[r]]
##            if not -1 in region:
##                polygon = [vor.vertices[i] for i in region]
##                plt.fill(*zip(*polygon), color=mapper.to_rgba(exploration_scores[r]))
#        
#        
#        ax1 = fig.add_subplot(111)
#        
#        ax1.scatter(old_ED_X[:,0], old_ED_X[:,1], s=10, c='r', marker="s", label='Old Design Points')
#        for i in range(old_ED_X.shape[0]):
#            txt = 'p'+str(i+1)
#            ax1.annotate(txt, (old_ED_X[i,0],old_ED_X[i,1]))
#            
##        for i in range(NrofCandGroups):
##            Candidates = all_candidates['group_'+str(i+1)]
##            ax1.scatter(Candidates[:,0],Candidates[:,1], s=10, c='b', marker="o", label='Design candidates')
#        ax1.scatter(all_candidates[:,0],all_candidates[:,1], s=10, c='b', marker="o", label='Design candidates')
#        
#        ax1.set_xlim(Bounds[0][0], Bounds[0][1])
#        ax1.set_ylim(Bounds[1][0], Bounds[1][1])
#        
#        plt.legend(loc='best');
#        plt.show()
#        
#    def voronoi_volumes(points):
#        from scipy.spatial import Voronoi, ConvexHull
#        v = Voronoi(points)
#        vol = np.zeros(v.npoints)
#        
#        for i, reg_num in enumerate(v.point_region):
#            indices = v.regions[reg_num]
#            if -1 in indices: # some regions can be opened
#                vol[i] = np.inf
#            else:
#                
#                #print("reg_num={0: 3.3f} X1={1: 3.3f} X2={2: 3.3f}".format(reg_num, v.points[reg_num-1, 0], v.points[reg_num-1, 1]))
#                vol[i] = ConvexHull(v.vertices[indices]).volume
#        
#        print('-'*40)
#        for i in range(nrofSamples):
#            print("idx={0:d} X1={1: 3.3f} X2={2: 3.3f} Volume={3: 3.3f}".format(i+1, v.points[i, 0], v.points[i, 1], vol[i]))
#        
#        return vol    
#    
#    NofPa = 2
#    
#    Bounds = ((-5,10), (0,15))
#    
#    nrofSamples = 10
#    old_ED_X = np.zeros((nrofSamples, NofPa))
#    for idx in range(NofPa):
#        Loc = Bounds[idx][0]
#        Scale = Bounds[idx][1] - Bounds[idx][0]
#        old_ED_X[:,idx] = stats.uniform(loc=Loc, scale=Scale).rvs(size=nrofSamples)
#    
#    
#    nNewCandidate = 40
#    
#    # New Function
#    volumes = voronoi_volumes(old_ED_X)
#    
#    
#    # SUMO
#    Exploration = Exploration(Bounds, old_ED_X, nNewCandidate)
#    
#    #all_candidates, Score = Exploration.get_vornoi_samples()
#    all_candidates, Score = Exploration.get_mc_samples()
#    
#    print('-'*40)
##    for i in range(nrofSamples):
##        print("idx={0:d} X1={1: 3.3f} X2={2: 3.3f} Volume={3: 3.3f}".format(i+1, old_ED_X[i,0], old_ED_X[i,1], vornoi.areas[i]))
#        
#    plotter(old_ED_X, all_candidates, volumes)
    

Classes

class Exploration (MetaModel, n_candidate, mc_criterion='mc-intersite-proj-th')

Created based on the Surrogate Modeling Toolbox (SUMO) [1].

[1] Gorissen, D., Couckuyt, I., Demeester, P., Dhaene, T. and Crombecq, K., 2010. A surrogate modeling and adaptive sampling toolbox for computer based design. Journal of machine learning research.-Cambridge, Mass., 11, pp.2051-2055. sumo@sumo.intec.ugent.be - http://sumo.intec.ugent.be

Attributes

MetaModel : obj
MetaModel object.
n_candidate : int
Number of candidate samples.
mc_criterion : str
Selection crieterion. The default is 'mc-intersite-proj-th'. Another option is 'mc-intersite-proj'.
w : int
Number of random points in the domain for each sample of the training set.
Expand source code
class Exploration:
    """
    Created based on the Surrogate Modeling Toolbox (SUMO) [1].

    [1] Gorissen, D., Couckuyt, I., Demeester, P., Dhaene, T. and Crombecq, K.,
        2010. A surrogate modeling and adaptive sampling toolbox for computer
        based design. Journal of machine learning research.-Cambridge, Mass.,
        11, pp.2051-2055. sumo@sumo.intec.ugent.be - http://sumo.intec.ugent.be

    Attributes
    ----------
    MetaModel : obj
        MetaModel object.
    n_candidate : int
        Number of candidate samples.
    mc_criterion : str
        Selection crieterion. The default is `'mc-intersite-proj-th'`. Another
        option is `'mc-intersite-proj'`.
    w : int
        Number of random points in the domain for each sample of the
        training set.
    """

    def __init__(self, MetaModel, n_candidate,
                 mc_criterion='mc-intersite-proj-th'):
        self.MetaModel = MetaModel
        self.Marginals = []
        self.n_candidate = n_candidate
        self.mc_criterion = mc_criterion
        self.w = 100

    def get_exploration_samples(self):
        """
        This function generates candidates to be selected as new design and
        their associated exploration scores.

        Returns
        -------
        all_candidates : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """
        MetaModel = self.MetaModel
        explore_method = MetaModel.ExpDesign.explore_method

        print("\n")
        print(f' The {explore_method}-Method is selected as the exploration '
              'method.')
        print("\n")

        if explore_method == 'Voronoi':
            # Generate samples using the Voronoi method
            all_candidates, exploration_scores = self.get_vornoi_samples()
        else:
            # Generate samples using the MC method
            all_candidates, exploration_scores = self.get_mc_samples()

        return all_candidates, exploration_scores

    # -------------------------------------------------------------------------
    def get_vornoi_samples(self):
        """
        This function generates samples based on voronoi cells and their
        corresponding scores

        Returns
        -------
        new_samples : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """

        mc_criterion = self.mc_criterion
        n_candidate = self.n_candidate
        # Get the Old ExpDesign #samples
        old_ED_X = self.MetaModel.ExpDesign.X
        ndim = old_ED_X.shape[1]

        # calculate error #averageErrors
        error_voronoi, all_candidates = self.approximate_voronoi(
            self.w, old_ED_X
            )

        # Pick the best candidate point in the voronoi cell
        # for each best sample
        selected_samples = np.empty((0, ndim))
        bad_samples = []

        for index in range(len(error_voronoi)):

            # get candidate new samples from voronoi tesselation
            candidates = self.closest_points[index]

            # get total number of candidates
            n_new_samples = candidates.shape[0]

            # still no candidate samples around this one, skip it!
            if n_new_samples == 0:
                print('The following sample has been skipped because there '
                      'were no candidate samples around it...')
                print(old_ED_X[index])
                bad_samples.append(index)
                continue

            # find candidate that is farthest away from any existing sample
            max_min_distance = 0
            best_candidate = 0
            min_intersite_dist = np.zeros((n_new_samples))
            min_projected_dist = np.zeros((n_new_samples))

            for j in range(n_new_samples):

                new_samples = np.vstack((old_ED_X, selected_samples))

                # find min distorted distance from all other samples
                euclidean_dist = self._build_dist_matrix_point(
                    new_samples, candidates[j], do_sqrt=True)
                min_euclidean_dist = np.min(euclidean_dist)
                min_intersite_dist[j] = min_euclidean_dist

                # Check if this is the maximum minimum distance from all other
                # samples
                if min_euclidean_dist >= max_min_distance:
                    max_min_distance = min_euclidean_dist
                    best_candidate = j

                # Projected distance
                projected_dist = distance.cdist(
                    new_samples, [candidates[j]], 'chebyshev')
                min_projected_dist[j] = np.min(projected_dist)

            if mc_criterion == 'mc-intersite-proj':
                weight_euclidean_dist = 0.5 * ((n_new_samples+1)**(1/ndim) - 1)
                weight_projected_dist = 0.5 * (n_new_samples+1)
                total_dist_scores = weight_euclidean_dist * min_intersite_dist
                total_dist_scores += weight_projected_dist * min_projected_dist

            elif mc_criterion == 'mc-intersite-proj-th':
                alpha = 0.5  # chosen (tradeoff)
                d_min = 2 * alpha / n_new_samples
                if any(min_projected_dist < d_min):
                    candidates = np.delete(
                        candidates, [min_projected_dist < d_min], axis=0
                        )
                    total_dist_scores = np.delete(
                        min_intersite_dist, [min_projected_dist < d_min],
                        axis=0
                        )
                else:
                    total_dist_scores = min_intersite_dist
            else:
                raise NameError(
                    'The MC-Criterion you requested is not available.'
                    )

            # Add the best candidate to the list of new samples
            best_candidate = np.argsort(total_dist_scores)[::-1][:n_candidate]
            selected_samples = np.vstack(
                (selected_samples, candidates[best_candidate])
                )

        self.new_samples = selected_samples
        self.exploration_scores = np.delete(error_voronoi, bad_samples, axis=0)

        return self.new_samples, self.exploration_scores

    # -------------------------------------------------------------------------
    def get_mc_samples(self, all_candidates=None):
        """
        This function generates random samples based on Global Monte Carlo
        methods and their corresponding scores, based on [1].

        [1] Crombecq, K., Laermans, E. and Dhaene, T., 2011. Efficient
            space-filling and non-collapsing sequential design strategies for
            simulation-based modeling. European Journal of Operational Research
            , 214(3), pp.683-696.
            DOI: https://doi.org/10.1016/j.ejor.2011.05.032

        Implemented methods to compute scores:
            1) mc-intersite-proj
            2) mc-intersite-proj-th

        Arguments
        ---------
        all_candidates : array, optional
            Samples to compute the scores for. The default is `None`. In this
            case, samples will be generated by defined model input marginals.

        Returns
        -------
        new_samples : array of shape (n_candidate, n_params)
            A list of samples.
        exploration_scores: arrays of shape (n_candidate)
            Exploration scores.
        """
        MetaModel = self.MetaModel
        explore_method = MetaModel.ExpDesign.explore_method
        mc_criterion = self.mc_criterion
        if all_candidates is None:
            n_candidate = self.n_candidate
        else:
            n_candidate = all_candidates.shape[0]

        # Get the Old ExpDesign #samples
        old_ED_X = MetaModel.ExpDesign.X
        ndim = old_ED_X.shape[1]

        # ----- Compute the number of random points -----
        if all_candidates is None:
            # Generate MC Samples
            all_candidates = MetaModel.ExpDesign.generate_samples(
                self.n_candidate, explore_method
                )
        self.all_candidates = all_candidates

        # initialization
        new_samples = np.empty((0, ndim))
        min_intersite_dist = np.zeros((n_candidate))
        min_projected_dist = np.zeros((n_candidate))

        for i, candidate in enumerate(all_candidates):

            # find candidate that is farthest away from any existing sample
            maxMinDistance = 0

            new_samples = np.vstack((old_ED_X, new_samples))
            # find min distorted distance from all other samples
            euclidean_dist = self._build_dist_matrix_point(
                new_samples, candidate, do_sqrt=True
                )
            min_euclidean_dist = np.min(euclidean_dist)
            min_intersite_dist[i] = min_euclidean_dist

            # Check if this is the maximum minimum distance from all other
            # samples
            if min_euclidean_dist >= maxMinDistance:
                maxMinDistance = min_euclidean_dist

            # Projected distance
            projected_dist = distance.cdist(
                new_samples, [candidate], 'chebyshev'
                )
            min_projected_dist[i] = np.min(projected_dist)

        if mc_criterion == 'mc-intersite-proj':
            weight_euclidean_dist = ((n_candidate+1)**(1/ndim) - 1) * 0.5
            weight_projected_dist = (n_candidate+1) * 0.5
            total_dist_scores = weight_euclidean_dist * min_intersite_dist
            total_dist_scores += weight_projected_dist * min_projected_dist

        elif mc_criterion == 'mc-intersite-proj-th':
            alpha = 0.5  # chosen (tradeoff)
            d_min = 2 * alpha / n_candidate
            if any(min_projected_dist < d_min):
                all_candidates = np.delete(
                    all_candidates, [min_projected_dist < d_min], axis=0
                    )
                total_dist_scores = np.delete(
                    min_intersite_dist, [min_projected_dist < d_min], axis=0
                    )
            else:
                total_dist_scores = min_intersite_dist
        else:
            raise NameError('The MC-Criterion you requested is not available.')

        self.new_samples = all_candidates
        self.exploration_scores = total_dist_scores
        self.exploration_scores /= np.nansum(total_dist_scores)

        return self.new_samples, self.exploration_scores

    # -------------------------------------------------------------------------
    def approximate_voronoi(self, w, samples):
        """
        An approximate (monte carlo) version of Matlab's voronoi command.

        Arguments
        ---------
        samples : array
            Old experimental design to be used as center points for voronoi
            cells.

        Returns
        -------
        areas : array
            An approximation of the voronoi cells' areas.
        all_candidates: list of arrays
            A list of samples in each voronoi cell.
        """
        MetaModel = self.MetaModel

        n_samples = samples.shape[0]
        ndim = samples.shape[1]

        # Compute the number of random points
        n_points = w * samples.shape[0]
        # Generate w random points in the domain for each sample
        points = MetaModel.ExpDesign.generate_samples(n_points, 'random')
        self.all_candidates = points

        # Calculate the nearest sample to each point
        self.areas = np.zeros((n_samples))
        self.closest_points = [np.empty((0, ndim)) for i in range(n_samples)]

        # Compute the minimum distance from all the samples of old_ED_X for
        # each test point
        for idx in range(n_points):
            # calculate the minimum distance
            distances = self._build_dist_matrix_point(
                samples, points[idx], do_sqrt=True
                )
            closest_sample = np.argmin(distances)

            # Add to the voronoi list of the closest sample
            self.areas[closest_sample] = self.areas[closest_sample] + 1
            prev_closest_points = self.closest_points[closest_sample]
            self.closest_points[closest_sample] = np.vstack(
                (prev_closest_points, points[idx])
                )

        # Divide by the amount of points to get the estimated volume of each
        # voronoi cell
        self.areas /= n_points

        self.perc = np.max(self.areas * 100)

        self.errors = self.areas

        return self.areas, self.all_candidates

    # -------------------------------------------------------------------------
    def _build_dist_matrix_point(self, samples, point, do_sqrt=False):
        """
        Calculates the intersite distance of all points in samples from point.

        Parameters
        ----------
        samples : array of shape (n_samples, n_params)
            The old experimental design.
        point : array
            A candidate point.
        do_sqrt : bool, optional
            Whether to return distances or squared distances. The default is
            `False`.

        Returns
        -------
        distances : array
            Distances.

        """
        distances = distance.cdist(samples, np.array([point]), 'euclidean')

        # do square root?
        if do_sqrt:
            return distances
        else:
            return distances**2

Methods

def get_exploration_samples(self)

This function generates candidates to be selected as new design and their associated exploration scores.

Returns

all_candidates : array of shape (n_candidate, n_params)
A list of samples.
exploration_scores : arrays of shape (n_candidate)
Exploration scores.
Expand source code
def get_exploration_samples(self):
    """
    This function generates candidates to be selected as new design and
    their associated exploration scores.

    Returns
    -------
    all_candidates : array of shape (n_candidate, n_params)
        A list of samples.
    exploration_scores: arrays of shape (n_candidate)
        Exploration scores.
    """
    MetaModel = self.MetaModel
    explore_method = MetaModel.ExpDesign.explore_method

    print("\n")
    print(f' The {explore_method}-Method is selected as the exploration '
          'method.')
    print("\n")

    if explore_method == 'Voronoi':
        # Generate samples using the Voronoi method
        all_candidates, exploration_scores = self.get_vornoi_samples()
    else:
        # Generate samples using the MC method
        all_candidates, exploration_scores = self.get_mc_samples()

    return all_candidates, exploration_scores
def get_vornoi_samples(self)

This function generates samples based on voronoi cells and their corresponding scores

Returns

new_samples : array of shape (n_candidate, n_params)
A list of samples.
exploration_scores : arrays of shape (n_candidate)
Exploration scores.
Expand source code
def get_vornoi_samples(self):
    """
    This function generates samples based on voronoi cells and their
    corresponding scores

    Returns
    -------
    new_samples : array of shape (n_candidate, n_params)
        A list of samples.
    exploration_scores: arrays of shape (n_candidate)
        Exploration scores.
    """

    mc_criterion = self.mc_criterion
    n_candidate = self.n_candidate
    # Get the Old ExpDesign #samples
    old_ED_X = self.MetaModel.ExpDesign.X
    ndim = old_ED_X.shape[1]

    # calculate error #averageErrors
    error_voronoi, all_candidates = self.approximate_voronoi(
        self.w, old_ED_X
        )

    # Pick the best candidate point in the voronoi cell
    # for each best sample
    selected_samples = np.empty((0, ndim))
    bad_samples = []

    for index in range(len(error_voronoi)):

        # get candidate new samples from voronoi tesselation
        candidates = self.closest_points[index]

        # get total number of candidates
        n_new_samples = candidates.shape[0]

        # still no candidate samples around this one, skip it!
        if n_new_samples == 0:
            print('The following sample has been skipped because there '
                  'were no candidate samples around it...')
            print(old_ED_X[index])
            bad_samples.append(index)
            continue

        # find candidate that is farthest away from any existing sample
        max_min_distance = 0
        best_candidate = 0
        min_intersite_dist = np.zeros((n_new_samples))
        min_projected_dist = np.zeros((n_new_samples))

        for j in range(n_new_samples):

            new_samples = np.vstack((old_ED_X, selected_samples))

            # find min distorted distance from all other samples
            euclidean_dist = self._build_dist_matrix_point(
                new_samples, candidates[j], do_sqrt=True)
            min_euclidean_dist = np.min(euclidean_dist)
            min_intersite_dist[j] = min_euclidean_dist

            # Check if this is the maximum minimum distance from all other
            # samples
            if min_euclidean_dist >= max_min_distance:
                max_min_distance = min_euclidean_dist
                best_candidate = j

            # Projected distance
            projected_dist = distance.cdist(
                new_samples, [candidates[j]], 'chebyshev')
            min_projected_dist[j] = np.min(projected_dist)

        if mc_criterion == 'mc-intersite-proj':
            weight_euclidean_dist = 0.5 * ((n_new_samples+1)**(1/ndim) - 1)
            weight_projected_dist = 0.5 * (n_new_samples+1)
            total_dist_scores = weight_euclidean_dist * min_intersite_dist
            total_dist_scores += weight_projected_dist * min_projected_dist

        elif mc_criterion == 'mc-intersite-proj-th':
            alpha = 0.5  # chosen (tradeoff)
            d_min = 2 * alpha / n_new_samples
            if any(min_projected_dist < d_min):
                candidates = np.delete(
                    candidates, [min_projected_dist < d_min], axis=0
                    )
                total_dist_scores = np.delete(
                    min_intersite_dist, [min_projected_dist < d_min],
                    axis=0
                    )
            else:
                total_dist_scores = min_intersite_dist
        else:
            raise NameError(
                'The MC-Criterion you requested is not available.'
                )

        # Add the best candidate to the list of new samples
        best_candidate = np.argsort(total_dist_scores)[::-1][:n_candidate]
        selected_samples = np.vstack(
            (selected_samples, candidates[best_candidate])
            )

    self.new_samples = selected_samples
    self.exploration_scores = np.delete(error_voronoi, bad_samples, axis=0)

    return self.new_samples, self.exploration_scores
def get_mc_samples(self, all_candidates=None)

This function generates random samples based on Global Monte Carlo methods and their corresponding scores, based on [1].

[1] Crombecq, K., Laermans, E. and Dhaene, T., 2011. Efficient space-filling and non-collapsing sequential design strategies for simulation-based modeling. European Journal of Operational Research , 214(3), pp.683-696. DOI: https://doi.org/10.1016/j.ejor.2011.05.032

Implemented methods to compute scores: 1) mc-intersite-proj 2) mc-intersite-proj-th

Arguments

all_candidates : array, optional
Samples to compute the scores for. The default is None. In this case, samples will be generated by defined model input marginals.

Returns

new_samples : array of shape (n_candidate, n_params)
A list of samples.
exploration_scores : arrays of shape (n_candidate)
Exploration scores.
Expand source code
def get_mc_samples(self, all_candidates=None):
    """
    This function generates random samples based on Global Monte Carlo
    methods and their corresponding scores, based on [1].

    [1] Crombecq, K., Laermans, E. and Dhaene, T., 2011. Efficient
        space-filling and non-collapsing sequential design strategies for
        simulation-based modeling. European Journal of Operational Research
        , 214(3), pp.683-696.
        DOI: https://doi.org/10.1016/j.ejor.2011.05.032

    Implemented methods to compute scores:
        1) mc-intersite-proj
        2) mc-intersite-proj-th

    Arguments
    ---------
    all_candidates : array, optional
        Samples to compute the scores for. The default is `None`. In this
        case, samples will be generated by defined model input marginals.

    Returns
    -------
    new_samples : array of shape (n_candidate, n_params)
        A list of samples.
    exploration_scores: arrays of shape (n_candidate)
        Exploration scores.
    """
    MetaModel = self.MetaModel
    explore_method = MetaModel.ExpDesign.explore_method
    mc_criterion = self.mc_criterion
    if all_candidates is None:
        n_candidate = self.n_candidate
    else:
        n_candidate = all_candidates.shape[0]

    # Get the Old ExpDesign #samples
    old_ED_X = MetaModel.ExpDesign.X
    ndim = old_ED_X.shape[1]

    # ----- Compute the number of random points -----
    if all_candidates is None:
        # Generate MC Samples
        all_candidates = MetaModel.ExpDesign.generate_samples(
            self.n_candidate, explore_method
            )
    self.all_candidates = all_candidates

    # initialization
    new_samples = np.empty((0, ndim))
    min_intersite_dist = np.zeros((n_candidate))
    min_projected_dist = np.zeros((n_candidate))

    for i, candidate in enumerate(all_candidates):

        # find candidate that is farthest away from any existing sample
        maxMinDistance = 0

        new_samples = np.vstack((old_ED_X, new_samples))
        # find min distorted distance from all other samples
        euclidean_dist = self._build_dist_matrix_point(
            new_samples, candidate, do_sqrt=True
            )
        min_euclidean_dist = np.min(euclidean_dist)
        min_intersite_dist[i] = min_euclidean_dist

        # Check if this is the maximum minimum distance from all other
        # samples
        if min_euclidean_dist >= maxMinDistance:
            maxMinDistance = min_euclidean_dist

        # Projected distance
        projected_dist = distance.cdist(
            new_samples, [candidate], 'chebyshev'
            )
        min_projected_dist[i] = np.min(projected_dist)

    if mc_criterion == 'mc-intersite-proj':
        weight_euclidean_dist = ((n_candidate+1)**(1/ndim) - 1) * 0.5
        weight_projected_dist = (n_candidate+1) * 0.5
        total_dist_scores = weight_euclidean_dist * min_intersite_dist
        total_dist_scores += weight_projected_dist * min_projected_dist

    elif mc_criterion == 'mc-intersite-proj-th':
        alpha = 0.5  # chosen (tradeoff)
        d_min = 2 * alpha / n_candidate
        if any(min_projected_dist < d_min):
            all_candidates = np.delete(
                all_candidates, [min_projected_dist < d_min], axis=0
                )
            total_dist_scores = np.delete(
                min_intersite_dist, [min_projected_dist < d_min], axis=0
                )
        else:
            total_dist_scores = min_intersite_dist
    else:
        raise NameError('The MC-Criterion you requested is not available.')

    self.new_samples = all_candidates
    self.exploration_scores = total_dist_scores
    self.exploration_scores /= np.nansum(total_dist_scores)

    return self.new_samples, self.exploration_scores
def approximate_voronoi(self, w, samples)

An approximate (monte carlo) version of Matlab's voronoi command.

Arguments

samples : array
Old experimental design to be used as center points for voronoi cells.

Returns

areas : array
An approximation of the voronoi cells' areas.
all_candidates : list of arrays
A list of samples in each voronoi cell.
Expand source code
def approximate_voronoi(self, w, samples):
    """
    An approximate (monte carlo) version of Matlab's voronoi command.

    Arguments
    ---------
    samples : array
        Old experimental design to be used as center points for voronoi
        cells.

    Returns
    -------
    areas : array
        An approximation of the voronoi cells' areas.
    all_candidates: list of arrays
        A list of samples in each voronoi cell.
    """
    MetaModel = self.MetaModel

    n_samples = samples.shape[0]
    ndim = samples.shape[1]

    # Compute the number of random points
    n_points = w * samples.shape[0]
    # Generate w random points in the domain for each sample
    points = MetaModel.ExpDesign.generate_samples(n_points, 'random')
    self.all_candidates = points

    # Calculate the nearest sample to each point
    self.areas = np.zeros((n_samples))
    self.closest_points = [np.empty((0, ndim)) for i in range(n_samples)]

    # Compute the minimum distance from all the samples of old_ED_X for
    # each test point
    for idx in range(n_points):
        # calculate the minimum distance
        distances = self._build_dist_matrix_point(
            samples, points[idx], do_sqrt=True
            )
        closest_sample = np.argmin(distances)

        # Add to the voronoi list of the closest sample
        self.areas[closest_sample] = self.areas[closest_sample] + 1
        prev_closest_points = self.closest_points[closest_sample]
        self.closest_points[closest_sample] = np.vstack(
            (prev_closest_points, points[idx])
            )

    # Divide by the amount of points to get the estimated volume of each
    # voronoi cell
    self.areas /= n_points

    self.perc = np.max(self.areas * 100)

    self.errors = self.areas

    return self.areas, self.all_candidates