Пример #1
0
def test_tsemo(test_num_improve_iter=2, save=False):
    num_inputs = 2
    num_objectives = 2
    lab = VLMOP2()
    strategy = TSEMO(lab.domain)
    experiments = strategy.suggest_experiments(5 * num_inputs)
    warnings.filterwarnings("ignore", category=RuntimeWarning)
    warnings.filterwarnings("ignore", category=DeprecationWarning)

    num_improve_iter = 0
    best_hv = None
    pb = progress_bar(range(20))
    for i in pb:
        # Run experiments
        experiments = lab.run_experiments(experiments)

        # Get suggestions
        experiments = strategy.suggest_experiments(1, experiments)

        if save:
            strategy.save("tsemo_settings.json")
        y_pareto, _ = pareto_efficient(lab.data[["y_0", "y_1"]].to_numpy(),
                                       maximize=False)
        hv = hypervolume(y_pareto, [11, 11])
        if best_hv == None:
            best_hv = hv
        elif hv > best_hv:
            best_hv = hv
            num_improve_iter += 1
        pb.comment = f"Hypervolume: {hv}"
        if num_improve_iter >= test_num_improve_iter:
            print(
                "Requirement to improve fbest in at least {} satisfied, test stopped."
                .format(test_num_improve_iter))
            break
Пример #2
0
    def _create_param_df(self, reference=[-2957, 10.7]):
        """Create a parameters dictionary
        
        Parameters
        ----------
        reference : array-like, optional
            Reference for the hypervolume calculatio

        """
        records = []
        for experiment_id, r in self.runners.items():
            record = {}
            record["experiment_id"] = experiment_id

            # Transform
            transform_name = r.strategy.transform.__class__.__name__
            transform_params = r.strategy.transform.to_dict(
            )["transform_params"]
            record["transform_name"] = transform_name
            if transform_name == "Chimera":
                hierarchy = transform_params["hierarchy"]
                for objective_name, v in hierarchy.items():
                    key = f"{objective_name}_tolerance"
                    record[key] = v["tolerance"]
            elif transform_name == "MultitoSingleObjective":
                record.update(transform_params)

            # Strategy
            record["strategy_name"] = r.strategy.__class__.__name__

            # Batch size
            record["batch_size"] = r.batch_size

            # Number of initial experiments
            try:
                record["num_initial_experiments"] = r.n_init
            except AttributeError:
                pass

            # Terminal hypervolume
            data = r.experiment.data[["sty", "e_factor"]].to_numpy()
            data[:, 0] *= -1  # make it a minimzation problem
            y_front, _ = pareto_efficient(data[:self.trajectory_length, :],
                                          maximize=False)
            hv = hypervolume(y_front, ref=reference)
            record["terminal_hypervolume"] = hv

            # Computation time
            time = (r.experiment.data["computation_t"].
                    iloc[0:self.trajectory_length].sum())
            record["computation_t"] = time

            record["noise_level"] = r.experiment.noise_level

            records.append(record)

        # Make pandas dataframe
        self.df = pd.DataFrame.from_records(records)
        return self.df
Пример #3
0
    def run(self, **kwargs):
        """  Run the closed loop experiment cycle

        Parameters
        ----------
        save_freq : int, optional
            The frequency with which to checkpoint the state of the optimization. Defaults to None.
        save_at_end : bool, optional
            Save the state of the optimization at the end of a run, even if it is stopped early.
            Default is True.
        save_dir : str, optional
            The directory to save checkpoints locally. Defaults to `~/.summit/runner`.
        """
        # Set parameters
        prev_res = None
        self.restarts = 0
        n_objs = len(self.experiment.domain.output_variables)
        fbest_old = np.zeros(n_objs)
        fbest = np.zeros(n_objs)

        # Serialization
        save_freq = kwargs.get("save_freq")
        save_dir = kwargs.get("save_dir", str(get_summit_config_path()))
        self.uuid_val = uuid.uuid4()
        save_dir = pathlib.Path(save_dir) / "runner" / str(self.uuid_val)
        if not os.path.isdir(save_dir):
            os.makedirs(save_dir)
        save_at_end = kwargs.get("save_at_end", True)

        # Create neptune experiment

        if self.neptune_exp is None:
            session = Session(backend=HostedNeptuneBackend())
            proj = session.get_project(self.neptune_project)
            neptune_exp = proj.create_experiment(
                name=self.neptune_experiment_name,
                description=self.neptune_description,
                upload_source_files=self.neptune_files,
                logger=self.logger,
                tags=self.neptune_tags,
            )
        else:
            neptune_exp = self.neptune_exp

        # Run optimization loop
        for i in progress_bar(range(self.max_iterations)):
            # Get experiment suggestions
            if i == 0:
                k = self.n_init if self.n_init is not None else self.batch_size
                next_experiments = self.strategy.suggest_experiments(
                    num_experiments=k)
            else:
                next_experiments = self.strategy.suggest_experiments(
                    num_experiments=self.batch_size, prev_res=prev_res)
            prev_res = self.experiment.run_experiments(next_experiments)

            # Send best objective values to Neptune
            for j, v in enumerate(self.experiment.domain.output_variables):
                if i > 0:
                    fbest_old[j] = fbest[j]
                if v.maximize:
                    fbest[j] = self.experiment.data[v.name].max()
                elif not v.maximize:
                    fbest[j] = self.experiment.data[v.name].min()

                neptune_exp.send_metric(v.name + "_best", fbest[j])

            # Send hypervolume for multiobjective experiments
            if n_objs > 1:
                output_names = [
                    v.name for v in self.experiment.domain.output_variables
                ]
                data = self.experiment.data[output_names].copy()
                for v in self.experiment.domain.output_variables:
                    if v.maximize:
                        data[(v.name, "DATA")] = -1.0 * data[v.name]
                y_pareto, _ = pareto_efficient(data.to_numpy(), maximize=False)
                hv = hypervolume(y_pareto, self.ref)
                neptune_exp.send_metric("hypervolume", hv)

            # Save state
            if save_freq is not None:
                file = save_dir / f"iteration_{i}.json"
                if i % save_freq == 0:
                    self.save(file)
                    neptune_exp.send_artifact(str(file))
                if not save_dir:
                    os.remove(file)

            # Stop if no improvement
            compare = np.abs(fbest - fbest_old) > self.f_tol
            if all(compare) or i <= 1:
                nstop = 0
            else:
                nstop += 1

            if self.max_same is not None:
                if nstop >= self.max_same and self.restarts >= self.max_restarts:
                    self.logger.info(
                        f"{self.strategy.__class__.__name__} stopped after {i+1} iterations and {self.restarts} restarts."
                    )
                    break
                elif nstop >= self.max_same:
                    nstop = 0
                    prev_res = None
                    self.strategy.reset()
                    self.restarts += 1

        # Save at end
        if save_at_end:
            file = save_dir / f"iteration_{i}.json"
            self.save(file)
            neptune_exp.send_artifact(str(file))
            if not save_dir:
                os.remove(file)

        # Stop the neptune experiment
        neptune_exp.stop()
Пример #4
0
    def pareto_plot(self, objectives=None, colorbar=False, ax=None):
        """Make a 2D pareto plot of the experiments thus far

        Parameters
        ----------
        objectives: array-like, optional
            List of names of objectives to plot.
            By default picks the first two objectives
        ax: `matplotlib.pyplot.axes`, optional
            An existing axis to apply the plot to

        Returns
        -------
        if ax is None returns a tuple with the first component
        as the a new figure and the second component the axis

        if ax is a matplotlib axis, returns only the axis

        Raises
        ------
        ValueError
            If the number of objectives is not equal to two


        """
        if objectives is None:
            objectives = [
                v.name for v in self.domain.variables if v.is_objective
            ]
            objectives = objectives[0:2]

        if len(objectives) != 2:
            raise ValueError("Can only plot 2 objectives")

        data = self._data[objectives].copy()

        # Handle minimize objectives
        for objective in objectives:
            if not self.domain[objective].maximize:
                data[objective] = -1.0 * data[objective]

        values, indices = pareto_efficient(data.to_numpy(), maximize=True)

        if ax is None:
            fig, ax = plt.subplots(1)
            return_fig = True
        else:
            return_fig = False

        # Plot all data
        if len(self.data) > 0:
            strategies = pd.unique(self.data["strategy"])
            markers = ["o", "x"]
            for strategy, marker in zip(strategies, markers):
                strat_data = self.data[self.data["strategy"] == strategy]
                c = strat_data.index.values if colorbar else "k"
                cmap = ListedColormap(COLORS[:len(c)])
                im = ax.scatter(
                    strat_data[objectives[0]],
                    strat_data[objectives[1]],
                    cmap=cmap,
                    c=c,
                    alpha=1 if colorbar else 0.5,
                    marker=marker,
                    s=100,
                    label=strategy,
                )

            # Sort data so get nice pareto plot
            self.pareto_data = self.data.iloc[indices].copy()
            self.pareto_data = self.pareto_data.sort_values(by=objectives[0])
            if len(self.pareto_data) > 2:
                ax.plot(
                    self.pareto_data[objectives[0]],
                    self.pareto_data[objectives[1]],
                    c=(165 / 256, 0, 38 / 256),
                    label="Pareto Front",
                    linewidth=3,
                )
                ax.set_xlabel(objectives[0])
                ax.set_ylabel(objectives[1])
                if return_fig and colorbar:
                    fig.colorbar(im)
                ax.tick_params(direction="in")
            ax.legend()

        if return_fig:
            return fig, ax
        elif return_fig and colorbar:
            return fig, ax, im
        elif not return_fig and colorbar:
            return ax, im
        else:
            return ax
Пример #5
0
    def plot_hv_trajectories(
        self,
        reference=[-2957, 10.7],
        plot_type="matplotlib",
        include_experiment_ids=False,
        min_terminal_hv_avg=0,
        ax=None,
    ):
        """ Plot the hypervolume trajectories with repeats as 95% confidence interval
        
        Parameters
        ----------
        reference : array-like, optional
            Reference for the hypervolume calculation. Defaults to -2957, 10.7
        plot_type : str, optional
            Plotting backend to use: matplotlib or plotly. Defaults to matplotlib.
        include_experiment_ids : bool, optional
            Whether to include experiment ids in the plot labels
        min_terminal_hv_avg : float, optional`
            Minimum terminal average hypervolume cutoff for inclusion in the plot. Defaults to 0.
        """
        # Create figure
        if plot_type == "matplotlib":
            if ax is not None:
                fig = None
            else:
                fig, ax = plt.subplots(1)
        elif plot_type == "plotly":
            fig = go.Figure()
        else:
            raise ValueError(
                f"{plot_type} is not a valid plot type. Must be matplotlib or plotly."
            )

        # Group experiment repeats
        df = self.df.copy()
        df = df.set_index("experiment_id")
        df = df.drop(columns=["terminal_hypervolume", "computation_t"])
        uniques = df.drop_duplicates(keep="last")  # This actually groups them
        df_new = self.df.copy()

        if plot_type == "plotly":
            colors = px.colors.qualitative.Plotly
        else:
            colors = COLORS
        cycle = len(colors)
        c_num = 0
        self.hv = {}
        for index, unique in uniques.iterrows():
            # Find number of matching rows to this unique row
            temp_df = df_new.merge(unique.to_frame().transpose(), how="inner")
            ids = temp_df["experiment_id"].values

            # Calculate hypervolume trajectories
            ids = ids if len(ids) < self.num_repeats else ids[:self.
                                                              num_repeats]
            hv_trajectories = np.zeros([self.trajectory_length, len(ids)])
            for j, experiment_id in enumerate(ids):
                r = self.runners[experiment_id]
                data = r.experiment.data[["sty", "e_factor"]].to_numpy()
                data[:, 0] *= -1  # make it a minimzation problem
                for i in range(self.trajectory_length):
                    y_front, _ = pareto_efficient(data[0:i + 1, :],
                                                  maximize=False)
                    hv_trajectories[i, j] = hypervolume(y_front, ref=reference)

            # Mean and standard deviation
            hv_mean_trajectory = np.mean(hv_trajectories, axis=1)
            hv_std_trajectory = np.std(hv_trajectories, axis=1)

            if hv_mean_trajectory[-1] < min_terminal_hv_avg:
                continue

            # Update plot
            t = np.arange(1, self.trajectory_length + 1)
            # label = self._create_label(unique)
            transform = unique["transform_name"]
            if transform == "MultitoSingleObjective":
                transform = "Custom"

            label = (f"""{unique["strategy_name"]} ({transform})"""
                     if transform is not "Transform" else
                     f"""{unique["strategy_name"]}""")

            if include_experiment_ids:
                label += f" ({ids[0]}-{ids[-1]})"

            lower = hv_mean_trajectory - 1.96 * hv_std_trajectory
            lower = np.clip(lower, 0, None)
            upper = hv_mean_trajectory + 1.96 * hv_std_trajectory
            if plot_type == "matplotlib":
                ax.plot(t,
                        hv_mean_trajectory,
                        label=label,
                        color=colors[c_num])
                ax.fill_between(t,
                                lower,
                                upper,
                                alpha=0.1,
                                color=colors[c_num])
            elif plot_type == "plotly":
                r, g, b = hex_to_rgb(colors[c_num])
                color = lambda alpha: f"rgba({r},{g},{b},{alpha})"
                fig.add_trace(
                    go.Scatter(
                        x=t,
                        y=hv_mean_trajectory,
                        mode="lines",
                        name=label,
                        line=dict(color=color(1)),
                        legendgroup=label,
                    ))
                fig.add_trace(
                    go.Scatter(
                        x=t,
                        y=lower,
                        mode="lines",
                        fill="tonexty",
                        line=dict(width=0),
                        fillcolor=color(0.1),
                        showlegend=False,
                        legendgroup=label,
                    ))
                fig.add_trace(
                    go.Scatter(
                        x=t,
                        y=upper,
                        mode="lines",
                        fill="tozeroy",
                        line=dict(width=0),
                        fillcolor=color(0.1),
                        showlegend=False,
                        legendgroup=label,
                    ))
            if cycle == c_num + 1:
                c_num = 0
            elif plot_type == "plotly":
                c_num += 1
            elif plot_type == "matplotlib":
                c_num += 2

        # Plot formattting
        if plot_type == "matplotlib":
            ax.set_xlabel("Experiments")
            ax.set_ylabel("Hypervolume")
            legend = ax.legend(loc="upper left")
            ax.tick_params(direction="in")
            ax.set_xlim(1, self.trajectory_length)
            if fig is None:
                return ax, legend
            else:
                return fig, ax, legend
        elif plot_type == "plotly":
            fig.update_layout(xaxis=dict(title="Experiments"),
                              yaxis=dict(title="Hypervolume"))
            fig.show()
            return fig
Пример #6
0
    def _select_max_hvi(self, y, samples, num_evals=1):
        """Returns the point(s) that maximimize hypervolume improvement

        Parameters
        ----------
        samples: np.ndarray
             The samples on which hypervolume improvement is calculated
        num_evals: `int`
            The number of points to return (with top hypervolume improvement)

        Returns
        -------
        hv_imp, index
            Returns a tuple with lists of the best hypervolume improvement
            and the indices of the corresponding points in samples

        """
        samples_original = samples.copy()
        samples = samples.copy()
        y = y.copy()

        # Set up maximization and minimization
        for v in self.domain.variables:
            if v.is_objective and v.maximize:
                y[v.name] = -1 * y[v.name]
                samples[v.name] = -1 * samples[v.name]

        # samples, mean, std = samples.standardize(return_mean=True, return_std=True)
        samples = samples.data_to_numpy()
        Ynew = y.data_to_numpy()

        # Reference
        Yfront, _ = pareto_efficient(Ynew, maximize=False)
        r = np.max(
            Yfront,
            axis=0) + 0.01 * (np.max(Yfront, axis=0) - np.min(Yfront, axis=0))

        indices = []
        n = samples.shape[1]
        mask = np.ones(samples.shape[0], dtype=bool)
        samples_indices = np.arange(0, samples.shape[0])

        for i in range(num_evals):
            masked_samples = samples[mask, :]
            Yfront, _ = pareto_efficient(Ynew, maximize=False)
            if len(Yfront) == 0:
                raise ValueError("Pareto front length too short")

            hv_improvement = []
            hvY = hypervolume(Yfront, r)
            # Determine hypervolume improvement by including
            # each point from samples (masking previously selected poonts)
            for sample in masked_samples:
                sample = sample.reshape(1, n)
                A = np.append(Ynew, sample, axis=0)
                Afront, _ = pareto_efficient(A, maximize=False)
                hv = hypervolume(Afront, r)
                hv_improvement.append(hv - hvY)

            hvY0 = hvY if i == 0 else hvY0
            hv_improvement = np.array(hv_improvement)
            masked_index = np.argmax(hv_improvement)

            # Housekeeping: find the max HvI point and mask out for next round
            original_index = samples_indices[mask][masked_index]
            new_point = samples[original_index, :].reshape(1, n)
            Ynew = np.append(Ynew, new_point, axis=0)
            mask[original_index] = False
            indices.append(original_index)

            # Append current estimate of the pareto front to sample_paretos
            samples_copy = samples_original.copy()
            samples_copy = samples_copy * self.output_std + self.output_mean
            samples_copy[("hvi", "DATA")] = hv_improvement
            self.samples.append(samples_copy)

        if len(hv_improvement) == 0:
            hv_imp = 0
        elif len(indices) == 0:
            indices = []
            hv_imp = 0
        else:
            # Total hypervolume improvement
            # Includes all points added to batch (hvY + last hv_improvement)
            # Subtracts hypervolume without any points added (hvY0)
            hv_imp = hv_improvement[masked_index] + hvY - hvY0
        return hv_imp, indices