Ejemplo n.º 1
0
    def propose_rand_samples_probe(self, nums_samples, path, lb, ub):

        seed   = np.random.randint(int(1e6))
        sobol  = SobolEngine(dimension = self.dims, scramble=True, seed=seed)

        center = np.mean(self.X, axis = 0)
        #check if the center located in the region
        ratio, tmp = self.get_sample_ratio_in_region( np.reshape(center, (1, len(center) ) ), path )
        if ratio == 0:
            print("==>center not in the region, using random samples")
            return self.propose_rand_samples(nums_samples, lb, ub)
        # it is possible that the selected region has no points,
        # so we need check here

        axes    = len( center )
        
        final_L = []
        for axis in range(0, axes):
            L       = np.zeros( center.shape )
            L[axis] = 0.01
            ratio   = 1
            
            while ratio >= 0.9:
                L[axis] = L[axis]*2
                if L[axis] >= (ub[axis] - lb[axis]):
                    break
                lb_     = np.clip( center - L/2, lb, ub )
                ub_     = np.clip( center + L/2, lb, ub )
                cands_  = sobol.draw(10000).to(dtype=torch.float64).cpu().detach().numpy()
                cands_  = (ub_ - lb_)*cands_ + lb_
                ratio, tmp = self.get_sample_ratio_in_region(cands_, path )
            final_L.append( L[axis] )

        final_L   = np.array( final_L )
        lb_       = np.clip( center - final_L/2, lb, ub )
        ub_       = np.clip( center + final_L/2, lb, ub )
        print("center:", center)
        print("final lb:", lb_)
        print("final ub:", ub_)
    
        count         = 0
        cands         = np.array([])
        while len(cands) < 10000:
            count    += 10000
            cands     = sobol.draw(count).to(dtype=torch.float64).cpu().detach().numpy()
        
            cands     = (ub_ - lb_)*cands + lb_
            ratio, cands = self.get_sample_ratio_in_region(cands, path)
            samples_count = len( cands )
        
        #extract candidates 
        
        return cands
Ejemplo n.º 2
0
def draw_sobol(lb: Tensor,
               ub: Tensor,
               n: int,
               seed: Optional[int] = None) -> Tensor:
    """Draws samples from Sobol Sequence.

    Parameters
    ----------
    lb : Tensor
        Lower bound.
    ub : Tensor
        Upper bound.
    n : int
        Number of samples to draw.
    seed : int
        Seed to use for SobolEngine.

    Returns
    -------
    Tensor
        Samples drawn from Sobol sequence.
    """

    dim = lb.shape[0]

    engine = SobolEngine(dim, scramble=True, seed=seed)
    samples = engine.draw(n, dtype=lb.dtype).view(n, dim)

    return lb + (ub - lb) * samples
Ejemplo n.º 3
0
 def propose_rand_samples_sobol(self, nums_samples, path, lb, ub):
     
     #rejected sampling
     selected_cands = np.zeros((1, self.dims))
     seed   = np.random.randint(int(1e6))
     sobol  = SobolEngine(dimension = self.dims, scramble=True, seed=seed)
     
     # scale the samples to the entire search space
     # ----------------------------------- #
     # while len(selected_cands) <= nums_samples:
     #     cands  = sobol.draw(100000).to(dtype=torch.float64).cpu().detach().numpy()
     #     cands  = (ub - lb)*cands + lb
     #     for node in path:
     #         boundary = node[0].classifier.svm
     #         if len(cands) == 0:
     #             return []
     #         cands = cands[ boundary.predict(cands) == node[1] ] # node[1] store the direction to go
     #     selected_cands = np.append( selected_cands, cands, axis= 0)
     #     print("total sampled:", len(selected_cands) )
     # return cands
     # ----------------------------------- #
     #shrink the cands region
     
     ratio_check, centers = self.get_sample_ratio_in_region(self.X, path)
     # no current samples located in the region
     # should not happen
     # print("ratio check:", ratio_check, len(self.X) )
     # assert ratio_check > 0
     if ratio_check == 0 or len(centers) == 0:
         return self.propose_rand_samples( nums_samples, lb, ub )
     
     lb_    = None
     ub_    = None
     
     final_cands = []
     for center in centers:
         center = self.X[ np.random.randint( len(self.X) ) ]
         cands  = sobol.draw(2000).to(dtype=torch.float64).cpu().detach().numpy()
         ratio  = 1
         L      = 0.0001
         Blimit = np.max(ub - lb)
         
         while ratio == 1 and L < Blimit:                    
             lb_    = np.clip( center - L/2, lb, ub )
             ub_    = np.clip( center + L/2, lb, ub )
             cands_ = cp.deepcopy( cands )
             cands_ = (ub_ - lb_)*cands_ + lb_
             ratio, cands_ = self.get_sample_ratio_in_region(cands_, path)
             if ratio < 1:
                 final_cands.extend( cands_.tolist() )
             L = L*2
     final_cands      = np.array( final_cands )
     if len(final_cands) > nums_samples:
         final_cands_idx  = np.random.choice( len(final_cands), nums_samples )
         return final_cands[final_cands_idx]
     else:
         if len(final_cands) == 0:
             return self.propose_rand_samples( nums_samples, lb, ub )
         else:
             return final_cands
Ejemplo n.º 4
0
def heston_monte_carlo(model: HestonModel, option, num_steps, num_paths):
    # Generate brownian motion increments from a sobol sequence
    num_stochastic_processes = 2
    sobol = SobolEngine(num_stochastic_processes * num_steps)
    samples = norm.ppf(sobol.draw(num_paths))
    increment1, increment2 = np.hsplit(samples, num_stochastic_processes)

    kappa, theta, sigma, rho = model.kappa, model.theta, model.sigma, model.rho

    dt = 1 / num_steps

    ones = np.ones(num_paths).reshape(-1, 1)
    trunc_vol = ones * model.vol
    true_vol = trunc_vol
    moved_forward = ones * model.forward

    dw1 = np.array(np.sqrt(dt) * increment1)
    dw2 = np.array(rho * np.sqrt(dt) * increment1 + \
                   np.sqrt(1.0 - rho**2) * np.sqrt(dt) * increment2)

    for num_step in range(int(num_steps * option.tau)):
        moved_forward *= np.exp(np.sqrt(true_vol) * dw1[:, num_step, None])

        positive_part_trunc_vol = np.maximum(trunc_vol, 0)
        trunc_vol += kappa * (theta - positive_part_trunc_vol) * dt + \
            sigma * np.sqrt(positive_part_trunc_vol) * dw2[:, num_step, None]

        true_vol = np.maximum(trunc_vol, 0)

    return np.exp(-model.rate * option.tau) * np.mean(option(moved_forward))
    def generate_bm_sobolpca(self, n_process, n_steps, n_sim, end_time):
        #generate normals
        Sobol = SobolEngine(n_process * n_steps, scramble=True, seed=None)
        Z = norm.ppf(np.array(Sobol.draw(n_sim)) * (1 - 2e-7) + 1e-7)

        split = [
            list(range(n_process * n_steps))[i::n_process]
            for i in range(n_process)
        ]
        Z = np.dstack(tuple([Z[:, i] for i in split]))

        #pca computations
        V = np.ones(shape=(n_steps, n_steps))
        for i in range(1, n_steps + 1):
            V[i:, i:] += np.ones(shape=(n_steps - i, n_steps - i))
        V = V / n_steps * end_time

        lambdas, W = linalg.eig(V)
        D = np.diag(np.real(lambdas))

        A = W @ D**0.5

        #pca brownian motion
        pca_bm = np.dstack(
            tuple([Z[:, :, i] @ A[:, :].T for i in range(n_process)]))
        pca_dw = pca_bm
        pca_bm = np.concatenate((np.zeros((n_sim, 1, n_process)), pca_bm),
                                axis=1)
        pca_dw = pca_bm[:, 1:, :] - pca_bm[:, :-1, :]

        return pca_dw
Ejemplo n.º 6
0
def draw_sobol_samples(bounds: Tensor,
                       n: int,
                       q: int,
                       seed: Optional[int] = None) -> Tensor:
    r"""Draw qMC samples from the box defined by bounds.

    Args:
        bounds: A `2 x d` dimensional tensor specifying box constraints on a
            `d`-dimensional space, where bounds[0, :] and bounds[1, :] correspond
            to lower and upper bounds, respectively.
        n: The number of (q-batch) samples.
        q: The size of each q-batch.
        seed: The seed used for initializing Owen scrambling. If None (default),
            use a random seed.

    Returns:
        A `n x q x d`-dim tensor of qMC samples from the box defined by bounds.

    Example:
        >>> bounds = torch.stack([torch.zeros(3), torch.ones(3)])
        >>> samples = draw_sobol_samples(bounds, 10, 2)a
    """
    d = bounds.shape[-1]
    lower = bounds[0]
    rng = bounds[1] - bounds[0]
    sobol_engine = SobolEngine(d, scramble=True, seed=seed)
    samples_raw = sobol_engine.draw(n * q, dtype=lower.dtype).view(n, q, d)
    samples_raw = samples_raw.to(device=lower.device)
    return lower + rng * samples_raw
Ejemplo n.º 7
0
def get_candidate(model, acq, full_train_Y, q, bounds, dim):
    if acq == 'EI':
        if q == 1:
            EI = ExpectedImprovement(model, full_train_Y.max().item())
        else:
            EI = qExpectedImprovement(model, full_train_Y.max().item())

        bounds_t = torch.FloatTensor([[bounds[0]] * dim, [bounds[1]] * dim])
        candidate, acq_value = optimize_acqf(
            EI,
            bounds=bounds_t,
            q=q,
            num_restarts=15,
            raw_samples=5000,
        )

    elif acq == 'TS':
        sobol = SobolEngine(dim, scramble=True)
        n_candidates = min(5000, max(20000, 2000 * dim))
        pert = sobol.draw(n_candidates)
        X_cand = (bounds[1] - bounds[0]) * pert + bounds[0]
        thompson_sampling = MaxPosteriorSampling(model=model,
                                                 replacement=False)
        candidate = thompson_sampling(X_cand, num_samples=q)

    else:
        raise NotImplementedError('Only TS and EI are implemented')

    return candidate, EI if acq == 'EI' else None
Ejemplo n.º 8
0
 def _generate(self):
     skip = self.rng.randint(int(1e6))
     try:
         from torch.quasirandom import SobolEngine
         sobol = SobolEngine(dimension=len(self.search_dims),
                             scramble=True,
                             seed=skip)
         X = sobol.draw(n=self.size).numpy()
     except ImportError:
         sobol = Sobol(min_skip=skip, max_skip=skip)
         X = sobol.generate(self.search_dims, self.size)
     return X
    def generate_bm_sobol(self, n_process, n_steps, n_sim, end_time):
        #generate normals
        Sobol = SobolEngine(n_process * n_steps, scramble=True, seed=None)
        Z = norm.ppf(np.array(Sobol.draw(n_sim)) * (1 - 2e-7) + 1e-7)

        split = [
            list(range(n_process * n_steps))[i::n_process]
            for i in range(n_process)
        ]
        Z = np.dstack(tuple([Z[:, i] for i in split]))

        dw = Z / np.sqrt(n_steps / end_time)
        return dw
Ejemplo n.º 10
0
    def random_sample(self, node):
        """Naive random sample."""
        path = []
        n = node
        while n.parent:
            path.insert(0, (n.parent, n))
            n = n.parent

        if path:
            X = np.zeros((0, self.dim))
            is_left = np.array([(child == parent.left)
                                for parent, child in path])
            for x in node.x_samples:
                lb = np.clip(x - 1e-4, self.lb, self.ub)
                ub = np.clip(x + 1e-4, self.lb, self.ub)
                sobol = SobolEngine(dimension=self.dim, scramble=True)
                x_new = lb + (ub - lb) * sobol.draw(10).numpy()
                d = x_new - x

                for _ in range(16):
                    x_new += d
                    within_bound = ((self.lb <= x_new) &
                                    (x_new <= self.ub)).all(1)
                    is_good = np.array([
                        parent.latent_action.svc.predict(x_new) ==
                        parent.latent_action.good_label for parent, _ in path
                    ]).transpose()
                    mask = within_bound & (is_left == is_good).all(1)
                    cnt = np.count_nonzero(mask)
                    if cnt > 0:
                        x_new = x_new[mask]
                    if cnt <= 9:
                        break
                    d = d[mask] * 2

                X = np.vstack((X, x_new))
            X = X[np.random.choice(X.shape[0], size=10, replace=False)]

        else:
            X = np.random.uniform(self.lb, self.ub, (10, self.dim))

        Y = np.array([[self.f(x)] for x in X])
        best_index = Y.argmin()
        if Y[best_index][0] < self.f_best:
            self.f_best = Y[best_index][0]
            self.x_best = X[best_index]
            print("f_best: %s at \n%s" % (self.f_best, self.x_best))
        return X, Y
Ejemplo n.º 11
0
class SobolGenerator(AEPsychGenerator):
    """Generator that generates points from the Sobol Sequence."""

    _requires_model = False

    def __init__(
        self,
        lb: Union[np.ndarray, torch.Tensor],
        ub: Union[np.ndarray, torch.Tensor],
        dim: Optional[int] = None,
        seed: Optional[int] = None,
    ):
        """Iniatialize SobolGenerator.
        Args:
            lb (Union[np.ndarray, torch.Tensor]): Lower bounds of each parameter.
            ub (Union[np.ndarray, torch.Tensor]): Upper bounds of each parameter.
            dim (int, optional): Dimensionality of the parameter space. If None, it is inferred from lb and ub.
            seed (int, optional): Random seed.
        """
        self.lb, self.ub, self.dim = _process_bounds(lb, ub, dim)
        self.seed = seed
        self.engine = SobolEngine(dimension=self.dim, scramble=True, seed=self.seed)

    def gen(
        self,
        num_points: int = 1,
        model: AEPsychMixin = None,  # included for API compatibility
    ):
        """Query next point(s) to run by quasi-randomly sampling the parameter space.
        Args:
            num_points (int, optional): Number of points to query.
        Returns:
            np.ndarray: Next set of point(s) to evaluate, [num_points x dim].
        """
        grid = self.engine.draw(num_points)
        grid = self.lb + (self.ub - self.lb) * grid
        return grid

    @classmethod
    def from_config(cls, config: Config):
        classname = cls.__name__

        lb = config.gettensor(classname, "lb")
        ub = config.gettensor(classname, "ub")
        dim = config.getint(classname, "dim", fallback=None)
        seed = config.getint(classname, "seed", fallback=None)

        return cls(lb=lb, ub=ub, dim=dim, seed=seed)
Ejemplo n.º 12
0
    def sobol_sample(self, x_center, n_cand, nlb, nub, device, dtype):
        # Draw a Sobolev sequence in [lb, ub]
        seed = np.random.randint(int(1e6))
        sobol = SobolEngine(self.dim, scramble=True, seed=seed)
        pert = sobol.draw(n_cand).to(dtype=dtype, device=device).cpu().detach().numpy()
        pert = nlb + (nub - nlb) * pert

        # Create a perturbation mask
        prob_perturb = min(20.0 / self.dim, 1.0)
        mask = np.random.rand(n_cand, self.dim) <= prob_perturb
        ind = np.where(np.sum(mask, axis=1) == 0)[0]
        mask[ind, np.random.randint(0, self.dim - 1, size=len(ind))] = 1

        # Create candidate points
        X_cand = x_center.copy() * np.ones((n_cand, self.dim))
        X_cand[mask] = pert[mask]

        # TODO: what if no x_cand is within region??
        return X_cand
    def generate_bm_sobolbb_legacy(self, n_process, n_steps, n_sim, end_time):
        m = int(np.ceil(np.log(n_steps) / np.log(2)))
        h = int(2**m)
        times = np.linspace(0, end_time, h + 1)
        W = np.zeros(shape=(n_sim, h + 1, n_process))

        #Generate normal variables
        Sobol = SobolEngine(n_process * h, scramble=True, seed=None)
        Z = norm.ppf(np.array(Sobol.draw(n_sim)) * (1 - 2e-7) + 1e-7)
        split = [
            list(range(n_process * h))[i::n_process] for i in range(n_process)
        ]
        Z = np.dstack(tuple([Z[:, i] for i in split]))

        #Create bm via brownian bridge - see almgorithm from fig 3.2 in Glasserman2010
        j_max = 1
        W[:, h, :] = np.sqrt(times[h]) * Z[:, 0, :]
        cur_z_nr = 1
        for k in range(1, m + 1):
            i_min = int(h / 2)
            i = i_min
            l = 0
            r = h

            for j in range(1, j_max + 1):
                a = (
                    (times[r] - times[i]) * W[:, l, :] +
                    (times[i] - times[l]) * W[:, r, :]) / (times[r] - times[l])
                b = np.sqrt((times[i] - times[l]) * (times[r] - times[i]) /
                            (times[r] - times[l]))
                W[:, i, :] = a + b * Z[:, cur_z_nr, :]

                i += h
                l += h
                r += h

                cur_z_nr += 1

            j_max *= 2
            h = i_min

        dw = W[:, 1:, :] - W[:, :-1, :]
        return dw
Ejemplo n.º 14
0
def draw_sobol_samples(
    bounds: Tensor,
    n: int,
    q: int,
    batch_shape: Optional[Iterable[int], torch.Size] = None,
    seed: Optional[int] = None,
) -> Tensor:
    r"""Draw qMC samples from the box defined by bounds.

    Args:
        bounds: A `2 x d` dimensional tensor specifying box constraints on a
            `d`-dimensional space, where bounds[0, :] and bounds[1, :] correspond
            to lower and upper bounds, respectively.
        n: The number of (q-batch) samples.
        q: The size of each q-batch.
        batch_shape: The batch shape of the samples. If given, returns samples
            of shape `n x batch_shape x q x d`, where each batch is an
            `n x q x d`-dim tensor of qMC samples.
        seed: The seed used for initializing Owen scrambling. If None (default),
            use a random seed.

    Returns:
        A `n x batch_shape x q x d`-dim tensor of qMC samples from the box
        defined by bounds.

    Example:
        >>> bounds = torch.stack([torch.zeros(3), torch.ones(3)])
        >>> samples = draw_sobol_samples(bounds, 10, 2)
    """
    batch_shape = batch_shape or torch.Size()
    batch_size = int(torch.prod(torch.tensor(batch_shape)))
    d = bounds.shape[-1]
    lower = bounds[0]
    rng = bounds[1] - bounds[0]
    sobol_engine = SobolEngine(q * d, scramble=True, seed=seed)
    samples_raw = sobol_engine.draw(batch_size * n, dtype=lower.dtype)
    samples_raw = samples_raw.view(*batch_shape, n, q,
                                   d).to(device=lower.device)
    if batch_shape != torch.Size():
        samples_raw = samples_raw.permute(-3, *range(len(batch_shape)), -2, -1)
    return lower + rng * samples_raw
Ejemplo n.º 15
0
def sample_simplex(
    d: int,
    n: int = 1,
    qmc: bool = False,
    seed: Optional[int] = None,
    device: Optional[torch.device] = None,
    dtype: Optional[torch.dtype] = None,
) -> Tensor:
    r"""Sample uniformly from a d-simplex.

    Args:
        d: The dimension of the simplex.
        n: The number of samples to return.
        qmc: If True, use QMC Sobol sampling (instead of i.i.d. uniform).
        seed: If provided, use as a seed for the RNG.
        device: The torch device.
        dtype: The torch dtype.

    Returns:
        An `n x d` tensor of uniform samples from from the d-simplex.

    Example:
        >>> sample_simplex(d=3, n=10)
    """
    dtype = torch.float if dtype is None else dtype
    if d == 1:
        return torch.ones(n, 1, device=device, dtype=dtype)
    if qmc:
        sobol_engine = SobolEngine(d - 1, scramble=True, seed=seed)
        rnd = sobol_engine.draw(n, dtype=dtype)
    else:
        with manual_seed(seed=seed):
            rnd = torch.rand(n, d - 1, dtype=dtype)
    srnd, _ = torch.sort(rnd, dim=-1)
    zeros = torch.zeros(n, 1, dtype=dtype)
    ones = torch.ones(n, 1, dtype=dtype)
    srnd = torch.cat([zeros, srnd, ones], dim=-1)
    if device is not None:
        srnd = srnd.to(device)
    return srnd[..., 1:] - srnd[..., :-1]
Ejemplo n.º 16
0
def run(options):

    # pr = cProfile.Profile()
    # pr.enable()

    # Config
    config = SU2.io.Config(options.filename)
    config.NUMBER_PART = options.partitions
    config.NZONES = int(options.nzones)
    if options.quiet: config.CONSOLE = 'CONCISE'
    config.GRADIENT_METHOD = options.gradient

    its = int(config.OPT_ITERATIONS)  # number of opt iterations
    bound_upper = float(config.OPT_BOUND_UPPER
                        )  # variable bound to be scaled by the line search
    bound_lower = float(config.OPT_BOUND_LOWER
                        )  # variable bound to be scaled by the line search
    relax_factor = float(config.OPT_RELAX_FACTOR)  # line search scale
    gradient_factor = float(
        config.OPT_GRADIENT_FACTOR)  # objective function and gradient scale
    def_dv = config.DEFINITION_DV  # complete definition of the desing variable
    n_dv = sum(def_dv['SIZE'])  # number of design variables
    accu = float(config.OPT_ACCURACY) * gradient_factor  # optimizer accuracy
    x0 = [0.0] * n_dv  # initial design
    xb_low = [
        float(bound_lower) / float(relax_factor)
    ] * n_dv  # lower dv bound it includes the line search acceleration factor
    xb_up = [
        float(bound_upper) / float(relax_factor)
    ] * n_dv  # upper dv bound it includes the line search acceleration fa
    xb = list(zip(xb_low, xb_up))  # design bounds

    # State
    state = SU2.io.State()
    state.find_files(config)

    # add restart files to state.FILES
    if config.get('TIME_DOMAIN', 'NO') == 'YES' and config.get(
            'RESTART_SOL', 'NO') == 'YES' and gradient != 'CONTINUOUS_ADJOINT':
        restart_name = config['RESTART_FILENAME'].split('.')[0]
        restart_filename = restart_name + '_' + str(
            int(config['RESTART_ITER']) - 1).zfill(5) + '.dat'
        if not os.path.isfile(
                restart_filename):  # throw, if restart files does not exist
            sys.exit("Error: Restart file <" + restart_filename +
                     "> not found.")
        state['FILES']['RESTART_FILE_1'] = restart_filename

        # use only, if time integration is second order
        if config.get('TIME_MARCHING', 'NO') == 'DUAL_TIME_STEPPING-2ND_ORDER':
            restart_filename = restart_name + '_' + str(
                int(config['RESTART_ITER']) - 2).zfill(5) + '.dat'
            if not os.path.isfile(restart_filename
                                  ):  # throw, if restart files does not exist
                sys.exit("Error: Restart file <" + restart_filename +
                         "> not found.")
            state['FILES']['RESTART_FILE_2'] = restart_filename

    # Project

    if os.path.exists(options.projectname):
        project = SU2.io.load_data(options.projectname)
        project.config = config
    else:
        project = SU2.opt.Project(config, state)

    print(project)
    #print(config)

    n_dv = len(project.config['DEFINITION_DV']['KIND'])
    project.n_dv = n_dv

    soboleng = SobolEngine(n_dv, True)

    n_samples = 1000

    scale = 1.e-04
    X = scale * (soboleng.draw(n_samples) - 0.5)

    dv_list = []
    for i in range(n_samples):
        dv_list.append(X[i, :].numpy().tolist())

    print(dv_list[:3])
    pickle.dump(dv_list, open("dv_list.p", "wb"))

    #inputs = [(copy.deepcopy(project), dvs) for dvs in dv_list]

    print(len(dv_list))

    inputs = []
    for i, dvs in enumerate(dv_list):
        #dsn = project.new_design(project.config)
        config = copy.deepcopy(project.config)
        dsn = SU2.eval.design.Design(config,
                                     folder='DESIGNS/DSN_{:03d}'.format(i))
        inputs.append({"design": dsn, "dvs": dvs})

    with Pool(processes=2) as pool:
        outputs = pool.map(run_one_design, inputs)

    print(outputs)

    return
Ejemplo n.º 17
0
class EvolutionOpt:
    def __init__(self, design_space: DesignSpace, acq: Acquisition, es,
                 **conf):
        self.space = design_space
        self.es = es
        self.acq = acq
        self.pop = conf.get('pop', 100)
        self.iter = conf.get('iters', 500)
        self.verbose = conf.get('verbose', False)
        assert (self.acq.num_obj > 0)

    def get_init_pop(self, initial_suggest: pd.DataFrame = None) -> np.ndarray:
        # init_pop = self.space.sample(self.pop)
        self.eng = SobolEngine(self.space.num_paras, scramble=True)
        sobol_samp = self.eng.draw(self.pop)
        sobol_samp = sobol_samp * (self.space.opt_ub -
                                   self.space.opt_lb) + self.space.opt_lb
        x = sobol_samp[:, :self.space.num_numeric]
        xe = sobol_samp[:, self.space.num_numeric:]
        init_pop = self.space.inverse_transform(x, xe)
        if initial_suggest is not None:
            init_pop = pd.concat([initial_suggest, init_pop],
                                 axis=0).head(self.pop)
        x, xe = self.space.transform(init_pop)
        return np.hstack([x.numpy(), xe.numpy().astype(float)])

    def get_mutation(self):
        mask = []
        for name in (self.space.numeric_names + self.space.enum_names):
            if self.space.paras[name].is_discrete:
                mask.append('int')
            else:
                mask.append('real')

        mutation = MixedVariableMutation(
            mask, {
                'real': get_mutation('real_pm', eta=20),
                'int': get_mutation('int_pm', eta=20)
            })
        return mutation

    def get_crossover(self):
        mask = []
        for name in (self.space.numeric_names + self.space.enum_names):
            if self.space.paras[name].is_discrete:
                mask.append('int')
            else:
                mask.append('real')

        crossover = MixedVariableCrossover(
            mask, {
                'real': get_crossover('real_sbx', eta=15, prob=0.9),
                'int': get_crossover('int_sbx', eta=15, prob=0.9)
            })
        return crossover

    def optimize(self,
                 initial_suggest: pd.DataFrame = None,
                 fix_input: dict = None) -> pd.DataFrame:
        lb = self.space.opt_lb.numpy()
        ub = self.space.opt_ub.numpy()
        prob = BOProblem(lb, ub, self.acq, self.space, fix_input)
        init_pop = self.get_init_pop(initial_suggest)
        mutation = self.get_mutation()
        crossover = self.get_crossover()
        try:
            algo = get_algorithm(self.es,
                                 pop_size=self.pop,
                                 sampling=init_pop,
                                 mutation=mutation,
                                 crossover=crossover)
            res = minimize(prob,
                           algo, ('n_gen', self.iter),
                           verbose=self.verbose)
        except:
            if self.acq.num_obj > 1:
                algo = get_algorithm('nsga2',
                                     pop_size=self.pop,
                                     sampling=init_pop,
                                     mutation=mutation,
                                     crossover=crossover)
            else:
                algo = get_algorithm('ga',
                                     pop_size=self.pop,
                                     sampling=init_pop,
                                     mutation=mutation,
                                     crossover=crossover)
            res = minimize(prob,
                           algo, ('n_gen', self.iter),
                           verbose=self.verbose)
        opt_x = res.X.reshape(-1, len(lb)).astype(float)

        opt_xcont = torch.from_numpy(opt_x[:, :self.space.num_numeric])
        opt_xenum = torch.from_numpy(opt_x[:, self.space.num_numeric:])
        df_opt = self.space.inverse_transform(opt_xcont, opt_xenum)
        if fix_input is not None:
            for k, v in fix_input.items():
                df_opt[k] = v
        return df_opt
Ejemplo n.º 18
0
class MACEBO(AbstractOptimizer):
    support_parallel_opt  = True
    support_combinatorial = True
    support_contextual    = True
    def __init__(self, space, model_name = 'gpy', rand_sample = None):
        """
        model_name : surrogate model to be used
        rand_iter  : iterations to perform random sampling
        """
        super().__init__(space)
        self.space       = space
        self.X           = pd.DataFrame(columns = self.space.para_names)
        self.y           = np.zeros((0, 1))
        self.model_name  = model_name
        self.rand_sample = 1 + self.space.num_paras if rand_sample is None else max(2, rand_sample)
        self.sobol       = SobolEngine(self.space.num_paras, scramble = False)

    def quasi_sample(self, n, fix_input = None): 
        samp    = self.sobol.draw(n)
        samp    = samp * (self.space.opt_ub - self.space.opt_lb) + self.space.opt_lb
        x       = samp[:, :self.space.num_numeric]
        xe      = samp[:, self.space.num_numeric:]
        df_samp = self.space.inverse_transform(x, xe)
        if fix_input is not None:
            for k, v in fix_input.items():
                df_samp[k] = v
        return df_samp

    @property
    def model_config(self):
        if self.model_name == 'gp':
            cfg = {
                    'lr'           : 0.01,
                    'num_epochs'   : 100,
                    'verbose'      : False,
                    'noise_lb'     : 8e-4, 
                    'pred_likeli'  : False
                    }
        elif self.model_name == 'gpy':
            cfg = {
                    'verbose' : False,
                    'warp'    : True,
                    'space'   : self.space
                    }
        elif self.model_name == 'gpy_mlp':
            cfg = {
                    'verbose' : False
                    }
        elif self.model_name == 'rf':
            cfg =  {
                    'n_estimators' : 20
                    }
        else:
            cfg = {}
        if self.space.num_categorical > 0:
            cfg['num_uniqs'] = [len(self.space.paras[name].categories) for name in self.space.enum_names]
        return cfg
            
    def suggest(self, n_suggestions=1, fix_input = None):
        if self.X.shape[0] < self.rand_sample:
            sample = self.quasi_sample(n_suggestions, fix_input)
            return sample
        else:
            X, Xe = self.space.transform(self.X)
            try:
                if self.y.min() <= 0:
                    y = torch.FloatTensor(power_transform(self.y / self.y.std(), method = 'yeo-johnson'))
                else:
                    y = torch.FloatTensor(power_transform(self.y / self.y.std(), method = 'box-cox'))
                    if y.std() < 0.5:
                        y = torch.FloatTensor(power_transform(self.y / self.y.std(), method = 'yeo-johnson'))
                if y.std() < 0.5:
                    raise RuntimeError('Power transformation failed')
                model = get_model(self.model_name, self.space.num_numeric, self.space.num_categorical, 1, **self.model_config)
                model.fit(X, Xe, y)
            except:
                y     = torch.FloatTensor(self.y).clone()
                model = get_model(self.model_name, self.space.num_numeric, self.space.num_categorical, 1, **self.model_config)
                model.fit(X, Xe, y)

            best_id = np.argmin(self.y.squeeze())
            best_x  = self.X.iloc[[best_id]]
            best_y  = y.min()
            py_best, ps2_best = model.predict(*self.space.transform(best_x))
            py_best = py_best.detach().numpy().squeeze()
            ps_best = ps2_best.sqrt().detach().numpy().squeeze()

            iter  = max(1, self.X.shape[0] // n_suggestions)
            upsi  = 0.5
            delta = 0.01
            # kappa = np.sqrt(upsi * 2 * np.log(iter **  (2.0 + self.X.shape[1] / 2.0) * 3 * np.pi**2 / (3 * delta)))
            kappa = np.sqrt(upsi * 2 * ((2.0 + self.X.shape[1] / 2.0) * np.log(iter) + np.log(3 * np.pi**2 / (3 * delta))))

            acq = MACE(model, py_best, kappa = kappa) # LCB < py_best
            mu  = Mean(model)
            sig = Sigma(model, linear_a = -1.)
            opt = EvolutionOpt(self.space, acq, pop = 100, iters = 100, verbose = False)
            rec = opt.optimize(initial_suggest = best_x, fix_input = fix_input).drop_duplicates()
            rec = rec[self.check_unique(rec)]

            cnt = 0
            while rec.shape[0] < n_suggestions:
                rand_rec = self.quasi_sample(n_suggestions - rec.shape[0], fix_input)
                rand_rec = rand_rec[self.check_unique(rand_rec)]
                rec      = rec.append(rand_rec, ignore_index = True)
                cnt +=  1
                if cnt > 3:
                    # sometimes the design space is so small that duplicated sampling is unavoidable
                    break 
            if rec.shape[0] < n_suggestions:
                rand_rec = self.quasi_sample(n_suggestions - rec.shape[0], fix_input)
                rec      = rec.append(rand_rec, ignore_index = True)

            select_id = np.random.choice(rec.shape[0], n_suggestions, replace = False).tolist()
            x_guess   = []
            with torch.no_grad():
                py_all       = mu(*self.space.transform(rec)).squeeze().numpy()
                ps_all       = -1 * sig(*self.space.transform(rec)).squeeze().numpy()
                best_pred_id = np.argmin(py_all)
                best_unce_id = np.argmax(ps_all)
                if best_unce_id not in select_id and n_suggestions > 2:
                    select_id[0]= best_unce_id
                if best_pred_id not in select_id and n_suggestions > 2:
                    select_id[1]= best_pred_id
                rec_selected = rec.iloc[select_id].copy()
            return rec_selected

    def check_unique(self, rec : pd.DataFrame) -> [bool]:
        return (~pd.concat([self.X, rec], axis = 0).duplicated().tail(rec.shape[0]).values).tolist()

    def observe(self, X, y):
        """Feed an observation back.

        Parameters
        ----------
        X : pandas DataFrame
            Places where the objective function has already been evaluated.
            Each suggestion is a dictionary where each key corresponds to a
            parameter being optimized.
        y : array-like, shape (n,1)
            Corresponding values where objective has been evaluated
        """
        valid_id = np.where(np.isfinite(y.reshape(-1)))[0].tolist()
        XX       = X.iloc[valid_id]
        yy       = y[valid_id].reshape(-1, 1)
        self.X   = self.X.append(XX, ignore_index = True)
        self.y   = np.vstack([self.y, yy])
Ejemplo n.º 19
0
class NormalQMCEngine:
    r"""Engine for qMC sampling from a Multivariate Normal `N(0, I_d)`.

    By default, this implementation uses Box-Muller transformed Sobol samples
    following pg. 123 in [Pages2018numprob]_. To use the inverse transform
    instead, set `inv_transform=True`.

    Example:
        >>> engine = NormalQMCEngine(3)
        >>> samples = engine.draw(10)
    """
    def __init__(self,
                 d: int,
                 seed: Optional[int] = None,
                 inv_transform: bool = False) -> None:
        r"""Engine for drawing qMC samples from a multivariate normal `N(0, I_d)`.

        Args:
            d: The dimension of the samples.
            seed: The seed with which to seed the random number generator of the
                underlying SobolEngine.
            inv_transform: If True, use inverse transform instead of Box-Muller.
        """
        self._d = d
        self._seed = seed
        self._inv_transform = inv_transform
        if inv_transform:
            sobol_dim = d
        else:
            # to apply Box-Muller, we need an even number of dimensions
            sobol_dim = 2 * math.ceil(d / 2)
        self._sobol_engine = SobolEngine(dimension=sobol_dim,
                                         scramble=True,
                                         seed=seed)

    def draw(self,
             n: int = 1,
             out: Optional[Tensor] = None,
             dtype: torch.dtype = torch.float) -> Optional[Tensor]:
        r"""Draw `n` qMC samples from the standard Normal.

        Args:
            n: The number of samples to draw.
            out: An option output tensor. If provided, draws are put into this
                tensor, and the function returns None.
            dtype: The desired torch data type (ignored if `out` is provided).

        Returns:
            A `n x d` tensor of samples if `out=None` and `None` otherwise.
        """
        # get base samples
        samples = self._sobol_engine.draw(n, dtype=dtype)
        if self._inv_transform:
            # apply inverse transform (values to close to 0/1 result in inf values)
            v = 0.5 + (1 - 1e-10) * (samples - 0.5)
            samples_tf = torch.erfinv(2 * v - 1) * math.sqrt(2)
        else:
            # apply Box-Muller transform (note: [1] indexes starting from 1)
            even = torch.arange(0, samples.shape[-1], 2)
            Rs = (-2 * torch.log(samples[:, even])).sqrt()
            thetas = 2 * math.pi * samples[:, 1 + even]
            cos = torch.cos(thetas)
            sin = torch.sin(thetas)
            samples_tf = torch.stack([Rs * cos, Rs * sin], -1).reshape(n, -1)
            # make sure we only return the number of dimension requested
            samples_tf = samples_tf[:, :self._d]
        if out is None:
            return samples_tf
        else:
            out.copy_(samples_tf)
Ejemplo n.º 20
0
class BO_methods(object):
    def __init__(self, model, acq_name, bounds, y_max, epsilon, X, Y,
                 beta_sqrt):

        self.model = model
        self.acq_name = acq_name
        self.bounds = bounds
        self.y_max = y_max
        self.epsilon = epsilon
        self.dim = len(self.bounds)
        self.sobol = SobolEngine(self.dim, scramble=True)
        self.lb = np.array(self.bounds)[:, 0]
        self.ub = np.array(self.bounds)[:, 1]
        self.center = (self.lb + self.ub) / 2  # The center of the unit domain
        self.X = X
        self.Y = Y
        self.beta_sqrt = beta_sqrt

    def method_val(self):

        if self.acq_name in 'pi':
            x_init, acq_max = self.acq_maximize(self.pi_acq)
            x_return = self.multi_restart_maximize(self.pi_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'pg':
            x_init, acq_max = self.acq_maximize(self.pg_acq)
            x_return = self.multi_restart_maximize(self.pg_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'ei':
            x_init, acq_max = self.acq_maximize(self.ei_acq)
            x_return = self.multi_restart_maximize(self.ei_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'eg':
            x_init, acq_max = self.acq_maximize(self.eg_acq)
            x_return = self.multi_restart_maximize(self.eg_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'gpucb':
            x_init, acq_max = self.acq_maximize(self.gpucb_acq)
            x_return = self.multi_restart_maximize(self.gpucb_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'ts':
            x_init, acq_max = self.acq_maximize(self.ts_acq)
            x_return = self.multi_restart_maximize(self.ts_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'sts':
            x_tries = self.sobol.draw(100 * self.dim).cpu().numpy()
            samples = self.model.sample(x_tries, size=1)
            valid_idx = np.where(samples >= self.epsilon)[0]

            if valid_idx.shape[0]:
                X_good = x_tries[valid_idx]
                origin = np.linalg.norm(X_good - self.center, axis=-1)
                return X_good[np.argmin(origin)]

            else:
                x_init, acq_max = self.acq_maximize(self.ts_acq)
                x_return = self.multi_restart_maximize(self.ts_acq, x_init,
                                                       acq_max)
                return x_return

        elif self.acq_name == 'mes':
            self.y_maxes = self.sample_maxes_G()
            x_tries = self.sobol.draw(100 * self.dim).cpu().numpy()
            ys = np.array([])
            for x_try in x_tries:
                saved = self.mes_acq(x_try.reshape(1, -1))
                ys = np.append(ys, saved)
            x_init = x_tries[np.random.choice(np.where(ys == ys.max())[0])]
            acq_max = ys.max()

            x_return = self.multi_restart_maximize(self.mes_acq, x_init,
                                                   acq_max)
            return x_return

        elif self.acq_name == 'gs':
            x_tries = self.sobol.draw(100 * self.dim).cpu().numpy()
            samples = self.model.sample(x_tries, size=100)

            if np.all(samples < self.epsilon):
                max_idx, _, _ = np.unravel_index(samples.argmax(),
                                                 samples.shape)
                return x_tries[max_idx]

            else:
                ys = (samples >= self.epsilon).mean(axis=-1)
                x_init = x_tries[np.random.choice(np.where(ys == ys.max())[0])]
                acq_max = ys.max()

                x_return = self.multi_restart_maximize(self.gs_acq, x_init,
                                                       acq_max)
                return x_return

        else:
            err = "The acquisition function " \
                  "{} has not been implemented, " \
                  "please choose one from the given list".format(acq_name)
            raise NotImplementedError(err)

    def pi_acq(self, x):
        mean, var = self.model.predict(x)
        std = np.sqrt(var)
        z = (mean - self.y_max) / np.maximum(std, 1e-8)
        prob = norm.cdf(z)
        return prob

    def pg_acq(self, x):
        mean, var = self.model.predict(x)
        std = np.sqrt(var)
        z = (mean - self.epsilon) / np.maximum(std, 1e-8)
        return z

    def ei_acq(self, x):
        mean, var = self.model.predict(x)
        std = np.sqrt(var)
        a = (mean - self.y_max)
        z = a / np.maximum(std, 1e-8)
        improve = a * norm.cdf(z) + std * norm.pdf(z)
        return improve

    def eg_acq(self, x):
        mean, var = self.model.predict(x)
        std = np.sqrt(var)
        a = (mean - self.epsilon)
        z = a / np.maximum(std, 1e-8)
        improve = a * norm.cdf(z) + std * norm.pdf(z)
        return improve

    def gpucb_acq(self, x):
        mean, var = self.model.predict(x)
        val = mean + self.beta_sqrt * np.sqrt(var)
        return val

    def ts_acq(self, x):
        return self.model.sample(x, size=1)

    def mes_acq(self, x):
        x = np.atleast_2d(x)
        mu, var = self.model.predict(x)
        std = np.sqrt(var)
        mu = mu.flatten()
        std = std.flatten()
        gamma_maxes = (self.y_maxes - mu) / np.maximum(std[:, None], 1e-8)
        tmp = 0.5 * gamma_maxes * norm.pdf(gamma_maxes) / np.maximum(norm.cdf(gamma_maxes), 1e-8) - \
            np.log(np.maximum(norm.cdf(gamma_maxes), 1e-8))
        mes = np.mean(tmp, axis=1, keepdims=True)
        mes = np.nan_to_num(mes)
        return mes

    def gs_acq(self, x):
        samples = self.model.sample(x, size=100)
        gs_score = (samples >= self.epsilon)
        return gs_score.mean()

    # Gumble sampling for sampling max values in MES
    def sample_maxes_G(self):
        x_grid = self.sobol.draw(100 * self.dim).cpu().numpy()
        mu, var = self.model.predict(x_grid)
        std = np.sqrt(var)

        def cdf_approx(z):
            z = np.atleast_1d(z)
            ret_val = np.zeros(z.shape)
            for i, zi in enumerate(z):
                ret_val[i] = np.prod(
                    norm.cdf((zi - mu) / np.maximum(std, 1e-8)))
            return ret_val

        lower = np.max(self.Y)
        upper = np.max(mu + 5 * std)
        if cdf_approx(upper) <= 0.75:
            upper += 1

        grid = np.linspace(lower, upper, 100)

        cdf_grid = cdf_approx(grid)
        r1, r2 = 0.25, 0.75

        y1 = grid[np.argmax(cdf_grid >= r1)]
        y2 = grid[np.argmax(cdf_grid >= r2)]

        beta = (y1 - y2) / (np.log(-np.log(r2)) - np.log(-np.log(r1)))
        alpha = y1 + (beta * np.log(-np.log(r1)))
        maxes = alpha - beta * np.log(-np.log(np.random.rand(1000, )))
        return maxes

    # Thompsons sampling for finding max values in MES
    def sample_maxes_T(self):
        X_tries = self.sobol.draw(100 * self.dim).cpu().numpy()
        samples = self.model.sample(X_tries, size=100)
        samples = samples.detach().cpu().numpy()
        maxs = np.max(samples, axis=0)
        percentiles = np.linspace(50, 95, 1000)
        reduced_maxes = np.percentile(maxs, percentiles)
        print(reduced_maxes)
        return reduced_maxes

    def acq_maximize(self, acq):
        x_tries = self.sobol.draw(1000).cpu().numpy()
        ys = acq(x_tries)
        x_max = x_tries[np.random.choice(np.where(ys == ys.max())[0])]
        acq_max = ys.max()
        return x_max, acq_max

    # Explore the parameter space more throughly
    def multi_restart_maximize(self, acq_func, x_max, acq_max, seed_num=10):
        x_seeds = self.sobol.draw(seed_num).cpu().numpy()
        for x_try in x_seeds:
            res = minimize(lambda x: -acq_func(x.reshape(1, -1)).squeeze(),\
                    x_try.reshape(1, -1),\
                    bounds=self.bounds,\
                    method="L-BFGS-B")
            if not res.success:
                continue
            if acq_max is None or -res.fun >= acq_max:
                x_max = res.x
                acq_max = -res.fun
        return x_max
Ejemplo n.º 21
0
    def _create_candidates(self, X, fX, length, n_training_steps, hypers, used_budget=None):
        """Generate candidates assuming X has been scaled to [0,1]^d."""
        # Pick the center as the point with the smallest function values
        # NOTE: This may not be robust to noise, in which case the posterior mean of the GP can be used instead
        if used_budget is not None:
            self.used_budget = used_budget
        assert X.min() >= 0.0 and X.max() <= 1.0

        # Standardize function values.
        mu, sigma = np.median(fX), fX.std()
        sigma = 1.0 if sigma < 1e-6 else sigma
        fX = (deepcopy(fX) - mu) / sigma

        # Figure out what device we are running on
        if len(X) < self.min_cuda:
            device, dtype = torch.device("cpu"), torch.float64
        else:
            device, dtype = self.device, self.dtype

        # We use CG + Lanczos for training if we have enough data
        with gpytorch.settings.max_cholesky_size(self.max_cholesky_size):
            X_torch = torch.tensor(X).to(device=device, dtype=dtype)
            y_torch = torch.tensor(fX).to(device=device, dtype=dtype)
            gp = train_gp(
                train_x=X_torch, train_y=y_torch, use_ard=self.use_ard, num_steps=n_training_steps, hypers=hypers,
                use_cylinder=self.use_cylinder, dim=self.dim
            )

            # Save state dict
            hypers = gp.state_dict()
        self._errors = self.fX - np.array(self._predictions)
        # Create the trust region boundaries
        x_center = X[fX.argmin().item(), :][None, :]
        if not self.use_cylinder:
            weights = gp.covar_module.base_kernel.lengthscale.cpu().detach().numpy().ravel()
        else:
            #weights = gp.covar_module.base_kernel.radial_base_kernel.lengthscale.cpu().detach().numpy().ravel()
            weights = gp.covar_module.base_kernel.angular_weights.cpu().detach().numpy().ravel()
        weights = weights / weights.mean()  # This will make the next line more stable
        weights = weights / np.prod(np.power(weights, 1.0 / len(weights)))  # We now have weights.prod() = 1
        #print('weights', weights)
        # TODO: REMOVE
        #prob_pert = np.log(self.budget - len(self.fX)) / np.log(self.budget)
        #print('prob of pulling appliance:', prob_pert)
       # appliance = np.random.choice((1, 0), p=(prob_pert, 1 - prob_pert))
       # print('pull or not: ', appliance)
        if self.use_pull == 1:
            print("Applying pulling...")
            if self.pull:
                print('Prob of pulling:', self.prob_pull)
                to_pull = np.random.choice(range(0,self.dim), size=min(self.dim, 2), p=self.prob_pull.flatten())
                weights[to_pull] *= 2
            else:
                print('Prob of pushing:', self.prob_push)
                to_push = np.random.choice(range(0, self.dim), size=min(self.dim, 2), p=self.prob_push.flatten())
                weights[to_push] /= 2
        lb = np.clip(x_center - weights * length / 2.0, 0.0, 1.0)
        ub = np.clip(x_center + weights * length / 2.0, 0.0, 1.0)
        #print('lb', lb)
        #print('ub', ub)
        self.cand_lb = lb
        self.cand_ub = ub

        # Draw a Sobolev sequence in [lb, ub]
        seed = np.random.randint(int(1e6))
        sobol = SobolEngine(self.dim, scramble=True, seed=seed)
        pert = sobol.draw(self.n_cand).to(dtype=dtype, device=device).cpu().detach().numpy()
        pert = lb + (ub - lb) * pert

        # Create a perturbation mask
        prob_perturb = min(20.0 / self.dim, 1.0)
        mask = np.random.rand(self.n_cand, self.dim) <= prob_perturb
        ind = np.where(np.sum(mask, axis=1) == 0)[0]
        mask[ind, np.random.randint(0, self.dim - 1, size=len(ind))] = 1

        # Create candidate points
        X_cand = x_center.copy() * np.ones((self.n_cand, self.dim))
        X_cand[mask] = pert[mask]

        # Figure out what device we are running on
        if len(X_cand) < self.min_cuda:
            device, dtype = torch.device("cpu"), torch.float64
        else:
            device, dtype = self.device, self.dtype

        # We may have to move the GP to a new device
        gp = gp.to(dtype=dtype, device=device)

        # We use Lanczos for sampling if we have enough data
        with torch.no_grad(), gpytorch.settings.max_cholesky_size(self.max_cholesky_size):
            X_cand_torch = torch.tensor(X_cand).to(device=device, dtype=dtype)
            f_preds = gp.likelihood(gp(X_cand_torch))
            self.f_var = f_preds.variance.cpu().detach().numpy()
            #print(self.f_var.shape)
            y_cand = f_preds.sample(torch.Size([self.batch_size])).t().cpu().detach().numpy()
            #print(y_cand.shape)
        self.gp = deepcopy(gp)
        self.init_iter = False
        # Remove the torch variables
        del X_torch, y_torch, X_cand_torch, gp

        # De-standardize the sampled values
        y_cand = mu + sigma * y_cand
        #print(y_cand.shape)
        return X_cand, y_cand, hypers
Ejemplo n.º 22
0
    def _create_candidates(self, X, fX, length, n_training_steps, hypers):
        """Generate candidates assuming X has been scaled to [0,1]^d."""
        # Pick the center as the point with the smallest function values
        # NOTE: This may not be robust to noise, in which case the posterior mean of the GP can be used instead
        assert X.min() >= 0.0 and X.max() <= 1.0

        # Standardize function values.
        mu, sigma = np.median(fX, axis=0), fX.std(axis=0)
        fX = (deepcopy(fX) - mu) / sigma

        # Figure out what device we are running on
        if len(X) < self.min_cuda:
            device, dtype = torch.device("cpu"), torch.float64
        else:
            device, dtype = self.device, self.dtype

        # We use CG + Lanczos for training if we have enough data
        with gpytorch.settings.max_cholesky_size(self.max_cholesky_size):
            X_torch = torch.tensor(X).to(device=device, dtype=dtype)
            y_torch = torch.tensor(fX).to(device=device, dtype=dtype)
            gp = train_gp(
                train_x=X_torch, train_y=y_torch, use_ard=self.use_ard, num_steps=n_training_steps, hypers=hypers
            )

            # Save state dict
            hypers = gp.state_dict()

        # Create the trust region boundaries
        x_center = X[fX[:, 0].argmin().item(), :][None, :]
        weights = gp.covar_module.base_kernel.lengthscale.cpu().detach().numpy().ravel()
        weights = weights / weights.mean()  # This will make the next line more stable
        weights = weights / np.prod(np.power(weights, 1.0 / len(weights)))  # We now have weights.prod() = 1
        lb = np.clip(x_center - weights * length / 2.0, 0.0, 1.0)
        ub = np.clip(x_center + weights * length / 2.0, 0.0, 1.0)

        # Draw a Sobolev sequence in [lb, ub]
        seed = np.random.randint(int(1e6))
        sobol = SobolEngine(self.dim, scramble=True, seed=seed)
        pert = sobol.draw(self.n_cand).to(dtype=dtype, device=device).cpu().detach().numpy()
        pert = lb + (ub - lb) * pert

        # Create a perturbation mask
        prob_perturb = min(20.0 / self.dim, 1.0)
        mask = np.random.rand(self.n_cand, self.dim) <= prob_perturb
        ind = np.where(np.sum(mask, axis=1) == 0)[0]
        mask[ind, np.random.randint(0, self.dim - 1, size=len(ind))] = 1

        # Create candidate points
        X_cand = x_center.copy() * np.ones((self.n_cand, self.dim))
        X_cand[mask] = pert[mask]

        # Figure out what device we are running on
        if len(X_cand) < self.min_cuda:
            device, dtype = torch.device("cpu"), torch.float64
        else:
            device, dtype = self.device, self.dtype

        # We may have to move the GP to a new device
        gp = gp.to(dtype=dtype, device=device)

        # We use Lanczos for sampling if we have enough data
        with torch.no_grad(), gpytorch.settings.max_cholesky_size(self.max_cholesky_size):
            X_cand_torch = torch.tensor(X_cand).to(device=device, dtype=dtype)
            y_cand = gp.likelihood(gp(X_cand_torch)).sample(torch.Size([self.batch_size])).permute(2, 0, 1)

            mu_th = torch.from_numpy(mu).to(device=y_cand.device, dtype=y_cand.dtype)
            sigma_th = torch.from_numpy(sigma).to(device=y_cand.device, dtype=y_cand.dtype)

            y_cand = mu_th.view(self.dim + 1, 1, 1) + sigma_th.view(self.dim + 1, 1, 1) * y_cand
            y_cand = y_cand.cpu().detach().numpy()

        # Remove the torch variables
        del X_torch, y_torch, X_cand_torch, gp, mu_th, sigma_th

        return X_cand, y_cand, hypers
def create_general_brownian_bridge(n_sim, n_steps, n_process, end_time):
    #Create instructions for the creation of the brownian bridge

    ex_list = []  #The instructions
    stage_list = []
    temp_high = n_steps
    temp_low = 0

    compute_mid_step = lambda low, high: int(np.ceil((high - low) / 2) + low)

    temp_step = compute_mid_step(temp_low, temp_high)
    temp_list = [temp_step, temp_low, temp_high]

    stage_list.append(temp_list)
    ex_list.append(temp_list)

    while len(ex_list) < n_steps - 1:
        new_stage_list = []
        for item in stage_list:
            temp_step, temp_low, temp_high = item

            if temp_step - temp_low > 1:
                new_step = compute_mid_step(temp_low, temp_step)
                new_list = [new_step, temp_low, temp_step]

                new_stage_list.append(new_list)
                ex_list.append(new_list)

            if temp_high - temp_step > 1:
                new_step = compute_mid_step(temp_step, temp_high)
                new_list = [new_step, temp_step, temp_high]

                new_stage_list.append(new_list)
                ex_list.append(new_list)

            stage_list = copy.deepcopy(new_stage_list)

    #Create Brownian bridge
    #Modified algorithm from Glasserman 2009
    times = np.linspace(0, end_time, n_steps + 1)
    W = np.zeros(shape=(n_sim, n_steps + 1, n_process))

    #Generate normal variables
    Sobol = SobolEngine(n_process * n_steps, scramble=True, seed=None)
    Z = norm.ppf(np.array(Sobol.draw(n_sim)) * (1 - 2e-7) + 1e-7)
    split = [
        list(range(n_process * n_steps))[i::n_process]
        for i in range(n_process)
    ]
    Z = np.dstack(tuple([Z[:, i] for i in split]))

    #Create bm via brownian bridge
    W[:, n_steps, :] = np.sqrt(times[n_steps]) * Z[:, 0, :]

    if n_steps > 1:
        cur_z_nr = 1
        for item in ex_list:
            i, l, r = item
            a = ((times[r] - times[i]) * W[:, l, :] +
                 (times[i] - times[l]) * W[:, r, :]) / (times[r] - times[l])
            b = np.sqrt((times[i] - times[l]) * (times[r] - times[i]) /
                        (times[r] - times[l]))
            W[:, i, :] = a + b * Z[:, cur_z_nr, :]

            cur_z_nr += 1

    dw = W[:, 1:, :] - W[:, :-1, :]
    return dw
Ejemplo n.º 24
0
class EvolutionOpt:
    def __init__(self, design_space: DesignSpace, acq: Acquisition, **conf):
        self.space = design_space
        self.acq = acq
        self.pop = conf.get('pop', 100)
        self.iter = conf.get('iters', 500)
        self.verbose = conf.get('verbose', False)
        self.num_obj = self.acq.num_obj
        self.num_constr = self.acq.num_constr
        assert (self.num_obj > 0)

    def callback(self):
        pass

    def pymoo_obj(self, x: np.array, out: dict, *args, **kwargs):
        num_x = x.shape[0]
        xcont = torch.from_numpy(x[:, :self.space.num_numeric].astype(float))
        xenum = torch.from_numpy(x[:, self.space.num_numeric:].astype(float))
        df_x = self.space.inverse_transform(xcont, xenum)
        if self.fix is not None:  # invalidate fixed input, replace with fixed values
            for k, v in self.fix.items():
                df_x[k] = v
        xcont, xenum = self.space.transform(df_x)

        with torch.no_grad():
            acq_eval = self.acq(xcont, xenum).numpy().reshape(
                num_x, self.num_obj + self.num_constr)
            out['F'] = acq_eval[:, :self.num_obj]

            if self.num_constr > 0:
                out['G'] = acq_eval[:, -1 * self.num_constr:]

        self.callback()

    def get_init_pop(self, initial_suggest: pd.DataFrame = None) -> np.ndarray:
        # init_pop = self.space.sample(self.pop)
        self.eng = SobolEngine(self.space.num_paras, scramble=True)
        sobol_samp = self.eng.draw(self.pop)
        sobol_samp = sobol_samp * (self.space.opt_ub -
                                   self.space.opt_lb) + self.space.opt_lb
        x = sobol_samp[:, :self.space.num_numeric]
        xe = sobol_samp[:, self.space.num_numeric:]
        init_pop = self.space.inverse_transform(x, xe)
        if initial_suggest is not None:
            init_pop = pd.concat([initial_suggest, init_pop],
                                 axis=0).head(self.pop)
        x, xe = self.space.transform(init_pop)
        return np.hstack([x.numpy(), xe.numpy().astype(float)])

    def get_mutation(self):
        mask = []
        for name in (self.space.numeric_names + self.space.enum_names):
            if self.space.paras[name].is_discrete:
                mask.append('int')
            else:
                mask.append('real')

        mutation = MixedVariableMutation(
            mask, {
                'real': get_mutation('real_pm', eta=20),
                'int': get_mutation('int_pm', eta=20)
            })
        return mutation

    def get_crossover(self):
        mask = []
        for name in (self.space.numeric_names + self.space.enum_names):
            if self.space.paras[name].is_discrete:
                mask.append('int')
            else:
                mask.append('real')

        crossover = MixedVariableCrossover(
            mask, {
                'real': get_crossover('real_sbx', eta=15, prob=0.9),
                'int': get_crossover('int_sbx', eta=15, prob=0.9)
            })
        return crossover

    def optimize(self,
                 initial_suggest: pd.DataFrame = None,
                 fix_input: dict = None) -> pd.DataFrame:
        self.fix = fix_input
        lb = self.space.opt_lb.numpy()
        ub = self.space.opt_ub.numpy()
        prob = get_problem_from_func(self.pymoo_obj,
                                     xl=lb,
                                     xu=ub,
                                     n_var=len(lb))
        init_pop = self.get_init_pop(initial_suggest)
        mutation = self.get_mutation()
        crossover = self.get_crossover()
        if self.num_obj > 1:
            algo = NSGA2(pop_size=self.pop,
                         sampling=init_pop,
                         mutation=mutation,
                         crossover=crossover)
        else:
            algo = GA(pop_size=self.pop,
                      sampling=init_pop,
                      mutation=mutation,
                      crossover=crossover)
        res = minimize(prob, algo, ('n_gen', self.iter), verbose=self.verbose)
        if res.X is None:  # no feasible solution founc
            opt_x = np.array([ind.X for ind in res.pop]).astype(float)
        else:
            opt_x = res.X.reshape(-1, len(lb)).astype(float)

        opt_xcont = torch.from_numpy(opt_x[:, :self.space.num_numeric])
        opt_xenum = torch.from_numpy(opt_x[:, self.space.num_numeric:])
        df_opt = self.space.inverse_transform(opt_xcont, opt_xenum)
        if self.fix is not None:
            for k, v in self.fix.items():
                df_opt[k] = v
        return df_opt
Ejemplo n.º 25
0
def get_initial_points(dim, n_pts):
    sobol = SobolEngine(dimension=dim, scramble=True)
    X_init = sobol.draw(n=n_pts).to(dtype=dtype, device=device)
    return X_init
Ejemplo n.º 26
0
class NormalQMCEngine:
    r"""Engine for qMC sampling from a Multivariate Normal `N(0, I_d)`.

    By default, this implementation uses Box-Muller transformed Sobol samples
    following pg. 123 in [Pages2018numprob]_. To use the inverse transform
    instead, set `inv_transform=True`.

    Example:
        >>> engine = NormalQMCEngine(3)
        >>> samples = engine.draw(10)
    """

    def __init__(
        self, d: int, seed: Optional[int] = None, inv_transform: bool = False
    ) -> None:
        r"""Engine for drawing qMC samples from a multivariate normal `N(0, I_d)`.

        Args:
            d: The dimension of the samples.
            seed: The seed with which to seed the random number generator of the
                underlying SobolEngine.
            inv_transform: If True, use inverse transform instead of Box-Muller.
        """
        self._d = d
        self._seed = seed
        self._inv_transform = inv_transform
        if inv_transform:
            sobol_dim = d
        else:
            # to apply Box-Muller, we need an even number of dimensions
            sobol_dim = 2 * math.ceil(d / 2)
        self._sobol_engine = SobolEngine(dimension=sobol_dim, scramble=True, seed=seed)

    def draw(
        self, n: int = 1, out: Optional[Tensor] = None, dtype: torch.dtype = torch.float
    ) -> Optional[Tensor]:
        r"""Draw `n` qMC samples from the standard Normal.

        Args:
            n: The number of samples to draw.
            out: An option output tensor. If provided, draws are put into this
                tensor, and the function returns None.
            dtype: The desired torch data type (ignored if `out` is provided).

        Returns:
            A `n x d` tensor of samples if `out=None` and `None` otherwise.
        """
        # get base samples
        samples = self._sobol_engine.draw(n, dtype=dtype)
        if self._inv_transform:
            # apply inverse transform (values to close to 0/1 result in inf values)
            v = 0.5 + (1 - 1e-10) * (samples - 0.5)
            samples_tf = torch.erfinv(2 * v - 1) * math.sqrt(2)
        else:
            # apply Box-Muller transform (note: [1] indexes starting from 1)
            even = torch.arange(0, samples.shape[-1], 2)
            Rs = (-2 * torch.log(samples[:, even])).sqrt()
            thetas = 2 * math.pi * samples[:, 1 + even]
            cos = torch.cos(thetas)
            sin = torch.sin(thetas)
            samples_tf = torch.stack([Rs * cos, Rs * sin], -1).reshape(n, -1)
            # make sure we only return the number of dimension requested
            samples_tf = samples_tf[:, : self._d]
        if out is None:
            return samples_tf
        else:
            out.copy_(samples_tf)
Ejemplo n.º 27
0
Archivo: main.py Proyecto: stys/albo
def generate_initial_data(nsamples=10, noise_std=None, seed=None):
    sobol_engine = SobolEngine(dimension=2, scramble=True, seed=seed)
    x = 6.0 * sobol_engine.draw(n=nsamples, dtype=torch.float)
    function = GardnerTestFunction(noise_std=noise_std)
    z = function(x)
    return x, z
Ejemplo n.º 28
0
def generate_batch(
    state,
    model,  # GP model
    X,  # Evaluated points on the domain [0, 1]^d
    Y,  # Function values
    batch_size,
    n_candidates=None,  # Number of candidates for Thompson sampling
    num_restarts=10,
    raw_samples=512,
    acqf="ts",  # "ei" or "ts"
    deup=False,
    turbo=True,
):
    dim = X.shape[-1]
    assert acqf in ("ts", "ei")
    assert X.min() >= 0.0 and X.max() <= 1.0 and torch.all(torch.isfinite(Y))
    if n_candidates is None:
        n_candidates = min(5000, max(2000, 200 * X.shape[-1]))

    # Scale the TR to be proportional to the lengthscales
    x_center = X[Y.argmax(), :].clone()
    if not deup:
        weights = model.covar_module.base_kernel.lengthscale.squeeze().detach()
    else:
        weights = model.f_predictor.covar_module.base_kernel.lengthscale.squeeze(
        ).detach()
    weights = weights / weights.mean()
    weights = weights / torch.prod(weights.pow(1.0 / len(weights)))
    tr_lb = torch.clamp(x_center - weights * state.length / 2.0, 0.0, 1.0)
    tr_ub = torch.clamp(x_center + weights * state.length / 2.0, 0.0, 1.0)
    if not turbo:
        tr_lb = torch.zeros(dim)
        tr_ub = torch.ones(dim)

    if acqf == "ts":
        sobol = SobolEngine(dim, scramble=True)
        pert = sobol.draw(n_candidates).to(dtype=dtype, device=device)
        pert = tr_lb + (tr_ub - tr_lb) * pert

        # Create a perturbation mask
        prob_perturb = min(20.0 / dim, 1.0)
        mask = (torch.rand(n_candidates, dim, dtype=dtype, device=device) <=
                prob_perturb)
        ind = torch.where(mask.sum(dim=1) == 0)[0]
        mask[ind,
             torch.randint(0, dim - 1, size=(len(ind), ), device=device)] = 1

        # Create candidate points from the perturbations and the mask
        X_cand = x_center.expand(n_candidates, dim).clone()
        X_cand[mask] = pert[mask]

        # Sample on the candidate points
        thompson_sampling = MaxPosteriorSampling(model=model,
                                                 replacement=False)
        X_next = thompson_sampling(X_cand, num_samples=batch_size)

    elif acqf == "ei":
        if batch_size > 1:
            ei = qExpectedImprovement(model, Y.max(), maximize=True)
        else:
            ei = ExpectedImprovement(model, Y.max(), maximize=True)
        try:
            X_next, acq_value = optimize_acqf(
                ei,
                bounds=torch.stack([tr_lb, tr_ub]),
                q=batch_size,
                num_restarts=num_restarts,
                raw_samples=raw_samples,
            )
        except NotPSDError:
            sobol = SobolEngine(dim, scramble=True)
            pert = sobol.draw(batch_size).to(dtype=dtype, device=device)
            pert = tr_lb + (tr_ub - tr_lb) * pert
            X_next = pert
            print(
                'Warning: NotPSDError, using {} purely random candidates for this step'
                .format(batch_size))

    return X_next
Ejemplo n.º 29
0
class MACEBO(AbstractOptimizer):
    # Unclear what is best package to list for primary_import here.
    primary_import = "bayesmark"

    def __init__(self, api_config, model_name='gpy'):
        AbstractOptimizer.__init__(self, api_config)
        self.api_config = api_config
        self.space = self.parse_space(api_config)
        self.X = pd.DataFrame(columns=self.space.para_names)
        self.y = np.zeros((0, 1))
        self.model_name = model_name
        for k in api_config:
            print(k, api_config[k])
        self.sobol = SobolEngine(self.space.num_paras, scramble=False)

    def filter(self, y: torch.Tensor) -> [bool]:
        if not (np.all(y.numpy() > 0) and (y.max() / y.min() > 20)):
            return [True for _ in range(y.shape[0])], np.inf
        else:
            data = y.numpy().reshape(-1)
            quant = min(data.min() * 20,
                        np.quantile(data, 0.95, interpolation='lower'))
            return (data <= quant).tolist(), quant

    def quasi_sample(self, n):
        samp = self.sobol.draw(n)
        # samp    = torch.FloatTensor(lhs(self.space.num_paras, n))
        samp = samp * (self.space.opt_ub -
                       self.space.opt_lb) + self.space.opt_lb
        x = samp[:, :self.space.num_numeric]
        xe = samp[:, self.space.num_numeric:]
        df_samp = self.space.inverse_transform(x, xe)
        return df_samp

    def parse_space(self, api_config):
        space = DesignSpace()
        params = []
        for param_name in api_config:
            param_conf = api_config[param_name]
            param_type = param_conf['type']
            param_space = param_conf.get('space', None)
            param_range = param_conf.get("range", None)
            param_values = param_conf.get("values", None)

            bo_param_conf = {'name': param_name}
            if param_type == 'int':  # ignore 'log' space # TODO: support log-scale int
                bo_param_conf['type'] = 'int'
                bo_param_conf['lb'] = param_range[0]
                bo_param_conf['ub'] = param_range[1]
            elif param_type == 'bool':
                bo_param_conf['type'] = 'bool'
            elif param_type in ('cat', 'ordinal'):
                bo_param_conf['type'] = 'cat'
                bo_param_conf['categories'] = list(set(param_values))
            elif param_type == 'real':
                if param_space in ('log', 'logit'):
                    bo_param_conf['type'] = 'pow'
                    bo_param_conf['base'] = 10
                    bo_param_conf['lb'] = param_range[0]
                    bo_param_conf['ub'] = param_range[1]
                else:
                    bo_param_conf['type'] = 'num'
                    bo_param_conf['lb'] = param_range[0]
                    bo_param_conf['ub'] = param_range[1]
            else:
                assert False, "type %s not handled in API" % param_type
            params.append(bo_param_conf)
        print(params)
        space.parse(params)
        return space

    @property
    def model_config(self):
        if self.model_name == 'gp':
            cfg = {
                'lr': 0.01,
                'num_epochs': 100,
                'verbose': True,
                'noise_lb': 8e-4,
                'pred_likeli': False
            }
        elif self.model_name == 'gpy':
            cfg = {'verbose': False, 'warp': True, 'space': self.space}
        elif self.model_name == 'gpy_mlp':
            cfg = {'verbose': False}
        elif self.model_name == 'rf':
            cfg = {'n_estimators': 20}
        else:
            cfg = {}
        if self.space.num_categorical > 0:
            cfg['num_uniqs'] = [
                len(self.space.paras[name].categories)
                for name in self.space.enum_names
            ]
        return cfg

    def suggest(self, n_suggestions=1):
        if self.X.shape[0] < 4 * n_suggestions:
            df_suggest = self.quasi_sample(n_suggestions)
            x_guess = []
            for i, row in df_suggest.iterrows():
                x_guess.append(row.to_dict())
        else:
            X, Xe = self.space.transform(self.X)
            try:
                if self.y.min() <= 0:
                    y = torch.FloatTensor(
                        power_transform(self.y / self.y.std(),
                                        method='yeo-johnson'))
                else:
                    y = torch.FloatTensor(
                        power_transform(self.y / self.y.std(),
                                        method='box-cox'))
                    if y.std() < 0.5:
                        y = torch.FloatTensor(
                            power_transform(self.y / self.y.std(),
                                            method='yeo-johnson'))
                if y.std() < 0.5:
                    raise RuntimeError('Power transformation failed')
                model = get_model(self.model_name, self.space.num_numeric,
                                  self.space.num_categorical, 1,
                                  **self.model_config)
                model.fit(X, Xe, y)
            except:
                print('Error fitting GP')
                y = torch.FloatTensor(self.y).clone()
                filt, q = self.filter(y)
                print('Q = %g, kept = %d/%d' %
                      (q, y.shape[0], self.y.shape[0]))
                X = X[filt]
                Xe = Xe[filt]
                y = y[filt]
                model = get_model(self.model_name, self.space.num_numeric,
                                  self.space.num_categorical, 1,
                                  **self.model_config)
                model.fit(X, Xe, y)
            print('Noise level: %g' % model.noise, flush=True)

            best_id = np.argmin(self.y.squeeze())
            best_x = self.X.iloc[[best_id]]
            best_y = y.min()
            py_best, ps2_best = model.predict(*self.space.transform(best_x))
            py_best = py_best.detach().numpy().squeeze()
            ps_best = ps2_best.sqrt().detach().numpy().squeeze()

            # XXX: minimize (mu, -1 * sigma)
            #      s.t.     LCB < best_y
            iter = max(1, self.X.shape[0] // n_suggestions)
            upsi = 0.5
            delta = 0.01
            kappa = np.sqrt(
                upsi * 2 *
                np.log(iter**(2.0 + self.X.shape[1] / 2.0) * 3 * np.pi**2 /
                       (3 * delta)))

            acq = MACE(model, py_best, kappa=kappa)  # LCB < py_best
            mu = Mean(model)
            sig = Sigma(model, linear_a=-1.)
            opt = EvolutionOpt(self.space,
                               acq,
                               pop=100,
                               iters=100,
                               verbose=True)
            rec = opt.optimize(initial_suggest=best_x).drop_duplicates()
            rec = rec[self.check_unique(rec)]

            cnt = 0
            while rec.shape[0] < n_suggestions:
                rand_rec = self.quasi_sample(n_suggestions - rec.shape[0])
                rand_rec = rand_rec[self.check_unique(rand_rec)]
                rec = rec.append(rand_rec, ignore_index=True)
                cnt += 1
                if cnt > 3:
                    break
            if rec.shape[0] < n_suggestions:
                rand_rec = self.quasi_sample(n_suggestions - rec.shape[0])
                rec = rec.append(rand_rec, ignore_index=True)

            select_id = np.random.choice(rec.shape[0],
                                         n_suggestions,
                                         replace=False).tolist()
            x_guess = []
            with torch.no_grad():
                py_all = mu(*self.space.transform(rec)).squeeze().numpy()
                ps_all = -1 * sig(*self.space.transform(rec)).squeeze().numpy()
                best_pred_id = np.argmin(py_all)
                best_unce_id = np.argmax(ps_all)
                if best_unce_id not in select_id and n_suggestions > 2:
                    select_id[0] = best_unce_id
                if best_pred_id not in select_id and n_suggestions > 2:
                    select_id[1] = best_pred_id
                rec_selected = rec.iloc[select_id].copy()
                py, ps2 = model.predict(*self.space.transform(rec_selected))
                rec_selected['py'] = py.squeeze().numpy()
                rec_selected['ps'] = ps2.sqrt().squeeze().numpy()
                print(rec_selected)
            print('Best y is %g %g %g %g' %
                  (self.y.min(), best_y, py_best, ps_best),
                  flush=True)
            for idx in select_id:
                x_guess.append(rec.iloc[idx].to_dict())

        for rec in x_guess:
            for name in rec:
                if self.api_config[name]['type'] == 'int':
                    rec[name] = int(rec[name])
        return x_guess

    def check_unique(self, rec: pd.DataFrame) -> [bool]:
        return (~pd.concat([self.X, rec], axis=0).duplicated().tail(
            rec.shape[0]).values).tolist()

    def observe(self, X, y):
        """Feed an observation back.

        Parameters
        ----------
        X : list of dict-like
            Places where the objective function has already been evaluated.
            Each suggestion is a dictionary where each key corresponds to a
            parameter being optimized.
        y : array-like, shape (n,)
            Corresponding values where objective has been evaluated
        """
        # Random search so don't do anything
        y = np.array(y).reshape(-1)
        valid_id = np.where(np.isfinite(y))[0].tolist()
        XX = [X[idx] for idx in valid_id]
        yy = y[valid_id].reshape(-1, 1)
        self.X = self.X.append(XX, ignore_index=True)
        self.y = np.vstack([self.y, yy])
        print(yy)
Ejemplo n.º 30
0
def create_general_brownian_bridge(n_sim, n_steps, n_process, end_time):
    #Inputs are intergers
    #Output is a numpy array with dimension [n_sim,n_steps + 1,n_process]
    #The output is n_sim of a n_process dimensional brownian motions contructed using Sobol Numbers and a Brownian Bridge construction.
    #There are no correlation between the different brownian motions
    
    #The function requires the package "torch"
    
    #Create instructions for the creation of the brownian bridge
    ex_list = [] #The instructions
    stage_list = []
    temp_high = n_steps
    temp_low = 0
    
    compute_mid_step = lambda low, high: int(np.ceil((high - low) / 2) + low)
    
    temp_step = compute_mid_step(temp_low, temp_high)
    temp_list = [temp_step,temp_low,temp_high]
    
    stage_list.append(temp_list)
    ex_list.append(temp_list)
    
    while len(ex_list) < n_steps - 1:
        new_stage_list = []
        for item in stage_list:
            temp_step, temp_low, temp_high = item
            
            if temp_step - temp_low > 1:
                new_step = compute_mid_step(temp_low, temp_step)
                new_list = [new_step, temp_low, temp_step]
                
                new_stage_list.append(new_list)
                ex_list.append(new_list)
        
            
            if temp_high - temp_step > 1:
                new_step = compute_mid_step(temp_step, temp_high)
                new_list = [new_step, temp_step, temp_high]
                
                new_stage_list.append(new_list)
                ex_list.append(new_list)
            
            stage_list = copy.deepcopy(new_stage_list)
    
    #Create Brownian bridge
    #Modified algorithm from Glasserman 2009
    times = np.linspace(0, end_time, n_steps + 1)
    W = np.zeros(shape = (n_sim, n_steps+1, n_process))
    
    #Generate normal variables
    Sobol = SobolEngine(n_process * n_steps,scramble=True,seed=None)
    Z = norm.ppf(np.array(Sobol.draw(n_sim))*(1-2e-7)+1e-7)
    split = [list(range(n_process * n_steps))[i::n_process] for i in range(n_process)]
    Z = np.dstack(tuple([Z[:,i] for i in split]))
    
    #Create bm via brownian bridge
    W[:,n_steps,:] = np.sqrt(times[n_steps]) * Z[:,0,:]
    
    if n_steps > 1:
        cur_z_nr = 1
        for item in ex_list:
            i, l, r = item
            a = ((times[r] - times[i])*W[:,l,:] + (times[i] - times[l])*W[:,r,:]) / (times[r] - times[l])
            b = np.sqrt((times[i] - times[l]) * (times[r] - times[i]) / (times[r] - times[l]))
            W[:,i,:] = a + b * Z[:,cur_z_nr,:]
            
            cur_z_nr += 1
    
    return W