Ejemplo n.º 1
0
    def testContours(self):
        exp = get_branin_experiment(with_str_choice_param=True,
                                    with_batch=True)
        exp.trials[0].run()
        model = Models.BOTORCH(
            # Model bridge kwargs
            experiment=exp,
            data=exp.fetch_data(),
        )
        # Assert that each type of plot can be constructed successfully
        plot = plot_contour_plotly(model, model.parameters[0],
                                   model.parameters[1],
                                   list(model.metric_names)[0])
        self.assertIsInstance(plot, go.Figure)
        plot = interact_contour_plotly(model, list(model.metric_names)[0])
        self.assertIsInstance(plot, go.Figure)
        plot = interact_contour(model, list(model.metric_names)[0])
        self.assertIsInstance(plot, AxPlotConfig)
        plot = plot = plot_contour(model, model.parameters[0],
                                   model.parameters[1],
                                   list(model.metric_names)[0])
        self.assertIsInstance(plot, AxPlotConfig)

        # Make sure all parameters and metrics are displayed in tooltips
        tooltips = list(exp.parameters.keys()) + list(exp.metrics.keys())
        for d in plot.data["data"]:
            # Only check scatter plots hoverovers
            if d["type"] != "scatter":
                continue
            for text in d["text"]:
                for tt in tooltips:
                    self.assertTrue(tt in text)
Ejemplo n.º 2
0
def get_modelbridge() -> ModelBridge:
    exp = get_branin_experiment(with_batch=True)
    exp.trials[0].run()
    return Models.BOTORCH(
        # Model bridge kwargs
        experiment=exp,
        data=exp.fetch_data(),
    )
Ejemplo n.º 3
0
 def test_get_standard_plots(self):
     exp = get_branin_experiment()
     self.assertEqual(
         len(
             get_standard_plots(experiment=exp,
                                model=get_generation_strategy().model)),
         0,
     )
     exp = get_branin_experiment(with_batch=True, minimize=True)
     exp.trials[0].run()
     gs = choose_generation_strategy(search_space=exp.search_space)
     gs._model = Models.BOTORCH(experiment=exp, data=exp.fetch_data())
     plots = get_standard_plots(experiment=exp, model=gs.model)
     self.assertEqual(len(plots), 5)
     self.assertTrue(all(isinstance(plot, go.Figure) for plot in plots))
     exp = get_branin_experiment_with_multi_objective(with_batch=True)
     exp.trials[0].run()
     gs = choose_generation_strategy(
         search_space=exp.search_space,
         optimization_config=exp.optimization_config)
     gs._model = Models.BOTORCH(experiment=exp, data=exp.fetch_data())
     plots = get_standard_plots(experiment=exp, model=gs.model)
     self.assertEqual(len(plots), 6)
Ejemplo n.º 4
0
 def test_cross_validation(self):
     exp = get_branin_experiment(with_batch=True)
     exp.trials[0].run()
     model = Models.BOTORCH(
         # Model bridge kwargs
         experiment=exp,
         data=exp.fetch_data(),
     )
     cv = cross_validate(model)
     # Assert that each type of plot can be constructed successfully
     plot = interact_cross_validation_plotly(cv)
     self.assertIsInstance(plot, go.Figure)
     plot = interact_cross_validation(cv)
     self.assertIsInstance(plot, AxPlotConfig)
Ejemplo n.º 5
0
 def testSlices(self):
     exp = get_branin_experiment(with_batch=True)
     exp.trials[0].run()
     model = Models.BOTORCH(
         # Model bridge kwargs
         experiment=exp,
         data=exp.fetch_data(),
     )
     # Assert that each type of plot can be constructed successfully
     plot = plot_slice_plotly(model, model.parameters[0],
                              list(model.metric_names)[0])
     self.assertIsInstance(plot, go.Figure)
     plot = interact_slice_plotly(model)
     self.assertIsInstance(plot, go.Figure)
     plot = plot_slice(model, model.parameters[0],
                       list(model.metric_names)[0])
     self.assertIsInstance(plot, AxPlotConfig)
     plot = interact_slice(model)
     self.assertIsInstance(plot, AxPlotConfig)
Ejemplo n.º 6
0
def get_GPEI(
    experiment: Experiment,
    data: Data,
    search_space: Optional[SearchSpace] = None,
    dtype: torch.dtype = torch.double,
    device: torch.device = DEFAULT_TORCH_DEVICE,
) -> TorchModelBridge:
    """Instantiates a GP model that generates points with EI."""
    if data.df.empty:  # pragma: no cover
        raise ValueError("GP+EI BotorchModel requires non-empty data.")
    return checked_cast(
        TorchModelBridge,
        Models.BOTORCH(
            experiment=experiment,
            data=data,
            search_space=search_space or experiment.search_space,
            torch_dtype=dtype,
            torch_device=device,
        ),
    )
Ejemplo n.º 7
0
 def testTraces(self):
     exp = get_branin_experiment(with_batch=True)
     exp.trials[0].run()
     model = Models.BOTORCH(
         # Model bridge kwargs
         experiment=exp,
         data=exp.fetch_data(),
     )
     # Assert that each type of plot can be constructed successfully
     plot = optimization_trace_single_method_plotly(
         np.array([[1, 2, 3], [4, 5, 6]]),
         list(model.metric_names)[0],
         optimization_direction="minimize",
     )
     self.assertIsInstance(plot, go.Figure)
     plot = optimization_trace_single_method(
         np.array([[1, 2, 3], [4, 5, 6]]),
         list(model.metric_names)[0],
         optimization_direction="minimize",
     )
     self.assertIsInstance(plot, AxPlotConfig)
Ejemplo n.º 8
0
def get_botorch(
    experiment: Experiment,
    data: Data,
    search_space: Optional[SearchSpace] = None,
    dtype: torch.dtype = torch.double,
    device: torch.device = DEFAULT_TORCH_DEVICE,
    transforms: List[Type[Transform]] = Cont_X_trans + Y_trans,
    model_constructor: TModelConstructor = get_and_fit_model,  # pyre-ignore[9]
    model_predictor: TModelPredictor = predict_from_model,
    acqf_constructor: TAcqfConstructor = get_NEI,  # pyre-ignore[9]
    acqf_optimizer: TOptimizer = scipy_optimizer,  # pyre-ignore[9]
    refit_on_cv: bool = False,
    refit_on_update: bool = True,
    optimization_config: Optional[OptimizationConfig] = None,
) -> TorchModelBridge:
    """Instantiates a BotorchModel."""
    if data.df.empty:  # pragma: no cover
        raise ValueError("`BotorchModel` requires non-empty data.")
    logger.info(
        "Factory functions (like `get_botorch`) will soon be deprecated). Use "
        "the model registry instead (`Models.BOTORCH(...)`).")
    return checked_cast(
        TorchModelBridge,
        Models.BOTORCH(
            experiment=experiment,
            data=data,
            search_space=search_space or experiment.search_space,
            torch_dtype=dtype,
            torch_device=device,
            transforms=transforms,
            model_constructor=model_constructor,
            model_predictor=model_predictor,
            acqf_constructor=acqf_constructor,
            acqf_optimizer=acqf_optimizer,
            refit_on_cv=refit_on_cv,
            refit_on_update=refit_on_update,
            optimization_config=optimization_config,
        ),
    )
Ejemplo n.º 9
0
def get_GPEI(
    experiment: Experiment,
    data: Data,
    search_space: Optional[SearchSpace] = None,
    dtype: torch.dtype = torch.double,
    device: torch.device = DEFAULT_TORCH_DEVICE,
) -> TorchModelBridge:
    """Instantiates a GP model that generates points with EI."""
    if data.df.empty:  # pragma: no cover
        raise ValueError("GP+EI BotorchModel requires non-empty data.")
    logger.info(
        "Factory functions (like `get_GPEI`) will soon be deprecated). Use "
        "the model registry instead (`Models.GPEI(...)`).")
    return checked_cast(
        TorchModelBridge,
        Models.BOTORCH(
            experiment=experiment,
            data=data,
            search_space=search_space or experiment.search_space,
            torch_dtype=dtype,
            torch_device=device,
        ),
    )
Ejemplo n.º 10
0
def get_MOO_RS(
    experiment: Experiment,
    data: Data,
    ref_point: Optional[List[float]] = None,
    search_space: Optional[SearchSpace] = None,
    dtype: torch.dtype = torch.double,
    device: torch.device = DEFAULT_TORCH_DEVICE,
) -> TorchModelBridge:
    """Instantiates a Linear Random Scalarization multi-objective model.

    Chooses a different random linear scalarization of the objectives
    for generating each new candidate arm. This will only explore the
    convex hull of the pareto frontier.
    """
    # pyre-ignore: [16] `Optional` has no attribute `objective`.
    if not isinstance(experiment.optimization_config.objective,
                      MultiObjective):
        raise ValueError(
            "Multi-Objective optimization requires multiple objectives")
    if data.df.empty:
        raise ValueError("MultiObjectiveOptimization requires non-empty data.")
    return checked_cast(
        TorchModelBridge,
        Models.BOTORCH(
            experiment=experiment,
            data=data,
            search_space=search_space or experiment.search_space,
            torch_dtype=dtype,
            torch_device=device,
            default_model_gen_options={
                "acquisition_function_kwargs": {
                    "random_scalarization": True,
                    "sequential": True,
                }
            },
        ),
    )
Ejemplo n.º 11
0
def get_MOO_PAREGO(
    experiment: Experiment,
    data: Data,
    ref_point: Optional[List[float]] = None,
    search_space: Optional[SearchSpace] = None,
    dtype: torch.dtype = torch.double,
    device: torch.device = DEFAULT_TORCH_DEVICE,
) -> TorchModelBridge:
    """Instantiates a multi-objective model that generates points with ParEGO.

    qParEGO optimizes random augmented chebyshev scalarizations of the multiple
    objectives. This allows it to explore non-convex pareto frontiers.
    """
    # pyre-ignore: [16] `Optional` has no attribute `objective`.
    if not isinstance(experiment.optimization_config.objective,
                      MultiObjective):
        raise ValueError(
            "Multi-Objective optimization requires multiple objectives")
    if data.df.empty:
        raise ValueError("MultiObjectiveOptimization requires non-empty data.")
    return checked_cast(
        TorchModelBridge,
        Models.BOTORCH(
            experiment=experiment,
            data=data,
            search_space=search_space or experiment.search_space,
            torch_dtype=dtype,
            torch_device=device,
            ref_point=None,
            default_model_gen_options={
                "acquisition_function_kwargs": {
                    "chebyshev_scalarization": True,
                    "sequential": True,
                }
            },
        ),
    )
def bayes_opt_loop(experiment,
                   model_constructor,
                   acquisition_function,
                   evaluation_budget,
                   n_initial_evaluations=0,
                   batch_size=1,
                   visualise=False,
                   vis_start=None,
                   vis_end=None,
                   vis_density=None,
                   function_name=None,
                   kernel_name=None,
                   log_dir=Path('./'),
                   observer=None):
    print(f'Running Bayesian Optimisation')
    # acqf_constructor = get_acqf_constructor(acquisition_function)
    for i in range(n_initial_evaluations, evaluation_budget):
        print(f'BO: {i + 1}/{evaluation_budget}')
        model = Models.BOTORCH(
            experiment=experiment,
            data=experiment.eval(),
            search_space=experiment.search_space,
            model_constructor=model_constructor,
            # acqf_constructor=acqf_constructor,
        )

        # Work out regrets, and add to observer
        if observer is not None:
            loc_regret, fun_regret = get_current_regrets(experiment, observer)
            observer.current['results'][-1]['regret']['location'].append(
                loc_regret.item())
            observer.current['results'][-1]['regret']['function'].append(
                fun_regret.item())

        if visualise:
            # go.Figure(
            #     data=plot_slice(model, 'x', 'function').data
            # ).write_image(str(log_dir_i / 'posterior.png'))
            mod_vis.posterior(model,
                              experiment,
                              vis_start,
                              vis_end,
                              vis_density,
                              function_name=function_name,
                              kernel_name=kernel_name,
                              log_dir=log_dir,
                              point_num=i)
            mod_vis.kernel(model,
                           vis_start,
                           vis_end,
                           vis_density,
                           function_name=function_name,
                           kernel_name=kernel_name,
                           log_dir=log_dir,
                           point_num=i)
            bo_vis.acquisition_function(model,
                                        experiment,
                                        vis_start,
                                        vis_end,
                                        vis_density,
                                        log_dir=log_dir,
                                        point_num=i)
            vis.board(observer.current['function'],
                      model,
                      experiment,
                      observer.current['results'][-1]['regret']['location'],
                      observer.current['results'][-1]['regret']['function'],
                      n_initial_evaluations,
                      vis_start,
                      vis_end,
                      vis_density,
                      function_name=function_name,
                      kernel_name=kernel_name,
                      log_dir=log_dir,
                      point_num=i)

        # Generate new point.
        experiment.new_trial(generator_run=model.gen(batch_size))
Ejemplo n.º 13
0
def main():

    args = parse_args()

    function_list = [get_function_by_name[name]
            for name in args.function_name_list.split(',')]
    weight_list = list(map(float, args.weight_list.split(',')))
    covariance_matrix = json.loads(args.covariance_matrix)
    evaluate_covariance = args.evaluate_covariance

    init_iter = args.init_iter
    # if init_iter > 1:
    #     raise ValueError("init_iter should be 1.")
    init_batch_size = args.init_batch_size
    update_iter = args.update_iter
    batch_size = args.batch_size
    var_coef = args.var_coef

    var_compute_type = args.var_compute_type
    num_random = args.num_random
    num_bucket = args.num_bucket

    save_path = args.save_path

    # num_control = args.num_control

    minimize = True

    groundtruth_function = get_groundtruth_function(function_list, weight_list)
    #evaluation_function = get_evaluation_function(
    #        function_list, weight_list, covariance_matrix,
    #        evaluate_covariance, var_coef)
    evaluation_function = get_evaluation_function(
            function_list, weight_list, covariance_matrix,
            var_compute_type, num_random, num_bucket)

    exp = SimpleExperiment(
        name=args.function_name_list + args.weight_list,
        search_space=get_search_space(function_list),
        evaluation_function=evaluation_function,
        objective_name="objective_name",
        minimize=minimize,
    )
    t_start = time.time()
    print(f"Start time: {t_start}")
    print(f"Sobol iteration begin...{time.time() - t_start}")
    sobol = Models.SOBOL(exp.search_space)
    for i in range(init_iter):
        if init_batch_size == 1:
            exp.new_trial(generator_run=sobol.gen(init_batch_size))
        else:
            exp.new_batch_trial(generator_run=sobol.gen(init_batch_size))
        print(f"Running sobol optimization trial {i+1}/{init_iter}..."
              f"{time.time() - t_start}")
    print(f"GPEI iteration begin...{time.time() - t_start}")
    for i in range(update_iter):
        gpei = Models.BOTORCH(experiment=exp, data=exp.eval())
        if batch_size == 1:
            exp.new_trial(generator_run=gpei.gen(batch_size))
        else:
            exp.new_batch_trial(generator_run=gpei.gen(batch_size))
        print(f"Running GPEI optimization trial {i+1}/{update_iter}..."
              f"{time.time() - t_start}")

    # Construct Result.
    ## origin data.
    data_df = copy.deepcopy(exp.eval().df)
    compare_func = min if minimize else max
    
    arm_name2mean = {}
    for _, row in data_df.iterrows():
        arm_name2mean[row["arm_name"]] = row["mean"]
    ## parameters true_mean.
    other_columns = {
        "arm_name": [], "parameters": [], "true_mean": [],
        "cur_trial_best_mean": [], "accum_trials_best_mean": []}
    atbm = None # accum_trial_best_mean
    for trial in exp.trials.values():
        ctbm = None # cur_trial_best_mean
        for arm in trial.arms:
            other_columns['arm_name'].append(arm.name)
            other_columns['parameters'].append(json.dumps(arm.parameters))
            other_columns['true_mean'].append(
                    groundtruth_function(arm.parameters))
            if ctbm is None:
                ctbm = arm_name2mean[arm.name]
            ctbm = compare_func(ctbm, arm_name2mean[arm.name])
        if atbm is None:
            atbm = ctbm
        atbm = compare_func(atbm, ctbm)
        other_columns['cur_trial_best_mean'].extend([ctbm] * len(trial.arms))
        other_columns['accum_trials_best_mean'].extend([atbm] * len(trial.arms))
    other_df = DataFrame(other_columns)

    result_df = data_df.set_index('arm_name').join(
            other_df.set_index('arm_name')).reset_index()
    
    # Save to file.
    print("Save to file.")
    sub_dir_name = "_".join([
        "ax", args.function_name_list.replace(",", "_"),
        args.weight_list.replace(",", "_"), args.covariance_matrix.replace(
            "[", "_").replace("]", "_").replace(",", "_").replace(" ", ""),
        str(args.evaluate_covariance), str(args.init_iter), str(init_batch_size),
        str(args.update_iter), str(args.batch_size), str(args.var_coef),
        str(minimize), str(var_compute_type), str(num_random), str(num_bucket)
        ])
    abs_dir_path = os.path.join(save_path, sub_dir_name)
    Path(abs_dir_path).mkdir(parents=True, exist_ok=True)
    task_id = os.environ.get('TASK_INDEX')
    cur_time = pd.Timestamp.now().strftime('%Y%m%d%H%M%S')
    filename = cur_time + "_" + str(task_id) + ".csv"
    print(os.path.join(abs_dir_path, filename))
    result_df.to_csv(os.path.join(abs_dir_path, filename))
    print("2021-01-19 19:48:00")
    print("Done...")
Ejemplo n.º 14
0
    def test_get_standard_plots(self):
        exp = get_branin_experiment()
        self.assertEqual(
            len(
                get_standard_plots(experiment=exp,
                                   model=get_generation_strategy().model)),
            0,
        )
        exp = get_branin_experiment(with_batch=True, minimize=True)
        exp.trials[0].run()
        plots = get_standard_plots(
            experiment=exp,
            model=Models.BOTORCH(experiment=exp, data=exp.fetch_data()),
        )
        self.assertEqual(len(plots), 6)
        self.assertTrue(all(isinstance(plot, go.Figure) for plot in plots))
        exp = get_branin_experiment_with_multi_objective(with_batch=True)
        exp.optimization_config.objective.objectives[0].minimize = False
        exp.optimization_config.objective.objectives[1].minimize = True
        exp.optimization_config._objective_thresholds = [
            ObjectiveThreshold(metric=exp.metrics["branin_a"],
                               op=ComparisonOp.GEQ,
                               bound=-100.0),
            ObjectiveThreshold(metric=exp.metrics["branin_b"],
                               op=ComparisonOp.LEQ,
                               bound=100.0),
        ]
        exp.trials[0].run()
        plots = get_standard_plots(experiment=exp,
                                   model=Models.MOO(experiment=exp,
                                                    data=exp.fetch_data()))
        self.assertEqual(len(plots), 7)

        # All plots are successfully created when objective thresholds are absent
        exp.optimization_config._objective_thresholds = []
        plots = get_standard_plots(experiment=exp,
                                   model=Models.MOO(experiment=exp,
                                                    data=exp.fetch_data()))
        self.assertEqual(len(plots), 7)

        exp = get_branin_experiment_with_timestamp_map_metric(
            with_status_quo=True)
        exp.new_trial().add_arm(exp.status_quo)
        exp.trials[0].run()
        exp.new_trial(generator_run=Models.SOBOL(
            search_space=exp.search_space).gen(n=1))
        exp.trials[1].run()
        plots = get_standard_plots(
            experiment=exp,
            model=Models.BOTORCH(experiment=exp, data=exp.fetch_data()),
            true_objective_metric_name="branin",
        )

        self.assertEqual(len(plots), 9)
        self.assertTrue(all(isinstance(plot, go.Figure) for plot in plots))
        self.assertIn(
            "Objective branin_map vs. True Objective Metric branin",
            [p.layout.title.text for p in plots],
        )

        with self.assertRaisesRegex(
                ValueError, "Please add a valid true_objective_metric_name"):
            plots = get_standard_plots(
                experiment=exp,
                model=Models.BOTORCH(experiment=exp, data=exp.fetch_data()),
                true_objective_metric_name="not_present",
            )
Ejemplo n.º 15
0
    def test_best_from_model_prediction(self):
        exp = get_branin_experiment()

        for _ in range(3):
            sobol = Models.SOBOL(search_space=exp.search_space)
            generator_run = sobol.gen(n=1)
            trial = exp.new_trial(generator_run=generator_run)
            trial.run()
            trial.mark_completed()
            exp.attach_data(exp.fetch_data())

        gpei = Models.BOTORCH(experiment=exp, data=exp.lookup_data())
        generator_run = gpei.gen(n=1)
        trial = exp.new_trial(generator_run=generator_run)
        trial.run()
        trial.mark_completed()

        with patch.object(
            ArrayModelBridge,
            "model_best_point",
            return_value=(
                (
                    Arm(
                        name="0_0",
                        parameters={"x1": -4.842811906710267, "x2": 11.887089014053345},
                    ),
                    (
                        {"branin": 34.76260622783635},
                        {"branin": {"branin": 0.00028306433439807734}},
                    ),
                )
            ),
        ) as mock_model_best_point, self.assertLogs(
            logger="ax.service.utils.best_point", level="WARN"
        ) as lg:
            # Test bad model fit causes function to resort back to raw data
            with patch(
                "ax.service.utils.best_point.assess_model_fit",
                return_value=AssessModelFitResult(
                    good_fit_metrics_to_fisher_score={},
                    bad_fit_metrics_to_fisher_score={
                        "branin": 0,
                    },
                ),
            ):
                self.assertIsNotNone(get_best_parameters(exp, Models))
                self.assertTrue(
                    any("Model fit is poor" in warning for warning in lg.output),
                    msg=lg.output,
                )
                mock_model_best_point.assert_not_called()

            # Test model best point is used when fit is good
            with patch(
                "ax.service.utils.best_point.assess_model_fit",
                return_value=AssessModelFitResult(
                    good_fit_metrics_to_fisher_score={
                        "branin": 0,
                    },
                    bad_fit_metrics_to_fisher_score={},
                ),
            ):
                self.assertIsNotNone(get_best_parameters(exp, Models))
                mock_model_best_point.assert_called()

        # Assert the non-mocked method works correctly as well
        self.assertIsNotNone(get_best_parameters(exp, Models))
Ejemplo n.º 16
0
        print(f" - {len(ran)} Parameters {p}")

    else:
        continue
    # ax_client.attach_trial(parameters=p)

for t in exp.trials:
    exp.trials[t].run()

# Pull out of API for more specific iteration
batch = 10
print(f"Running Batch GP+EI optimization of {batch} samples")
# Reinitialize GP+EI model at each step with updated data.
data = exp.fetch_data()
print(data.df)
gpei = Models.BOTORCH(experiment=exp, data=data)
generator = gpei.gen(batch)

exp.new_batch_trial(generator_run=generator)
new_trial = len(exp.trials) - 1
print(f"New Candidate Designs")
for arm in exp.trials[new_trial].arms:
    print(arm)

# generator_run = gpei.gen(5)
# experiment.new_batch_trial(generator_run=generator_run)

#
# quit()
# sobol = Models.SOBOL(search_space=experiment.search_space)
# generator_run = sobol.gen(5)
Ejemplo n.º 17
0
def pid(cfg):
    env_name = cfg.env.params.name
    env = gym.make(env_name)
    env.reset()
    full_rewards = []
    exp_cfg = cfg.experiment

    from learn.utils.plotly import hv_characterization
    hv_characterization()

    def compare_control(env, cfg, save=True):
        import torch
        from learn.control.pid import PidPolicy

        controllers = []
        labels = []
        metrics = []

        # PID  baselines
        # /Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/sweeps/2020-04-14/11-12-02

        # from learn.simulate_sac import *
        # Rotation policy
        sac_policy1 = torch.load(
            '/Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/outputs/2020-03-24/18-32-26/trial_70000.dat'
        )
        controllers.append(sac_policy1['policy'])
        labels.append("SAC - Rotation")
        metrics.append(0)

        # Living reward policy
        sac_policy2 = torch.load(
            '/Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/outputs/2020-03-24/18-31-45/trial_35000.dat'
        )
        controllers.append(sac_policy2['policy'])
        labels.append("SAC - Living")
        metrics.append(1)

        # Square cost policy
        # sac_policy2 = torch.load(
        #     '/Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/sweeps/2020-03-25/20-30-47/metric.name=Square,robot=iono_sim/26/trial_40000.dat')
        controllers.append(sac_policy2['policy'])
        labels.append("SAC - Square")
        metrics.append(2)

        # un-Optimized PID parameters
        pid_params = [[2531.917, 61.358, 33.762], [2531.917, 61.358, 33.762]]
        pid = PidPolicy(cfg)
        pid.set_params(pid_params)
        controllers.append(pid)
        labels.append("PID - temp")
        metrics.append(0)

        controllers.append(pid)
        labels.append("PID - temp")
        metrics.append(1)

        # Optimized PID parameters
        pid_params = [[2531.917, 61.358, 3333.762],
                      [2531.917, 61.358, 3333.762]]
        pid = PidPolicy(cfg)
        pid.set_params(pid_params)
        controllers.append(pid)
        labels.append("PID - improved")
        metrics.append(2)

        from learn.control.mpc import MPController
        cfg.policy.mode = 'mpc'
        # dynam_model = torch.load(
        #     '/Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/outputs/2020-03-25/10-45-17/trial_1.dat')
        dynam_model = torch.load(
            '/Users/nato/Documents/Berkeley/Research/Codebases/dynamics-learn/sweeps/2020-03-25/20-30-57/metric.name=Rotation,robot=iono_sim/14/trial_9.dat'
        )
        mpc = MPController(env, dynam_model['model'], cfg)

        controllers.append(mpc)
        labels.append("MPC - 1")
        metrics.append(0)
        controllers.append(mpc)
        labels.append("MPC - 2")
        metrics.append(1)
        controllers.append(mpc)
        labels.append("MPC - 3")
        metrics.append(2)

        import plotly.graph_objects as go
        import plotly

        colors = [
            '#1f77b4',  # muted blue
            '#ff7f0e',  # safety orange
            '#2ca02c',  # cooked asparagus green
            '#d62728',  # brick red
            '#9467bd',  # muted purple
            '#8c564b',  # chestnut brown
            '#e377c2',  # raspberry yogurt pink
            '#7f7f7f',  # middle gray
            '#bcbd22',  # curry yellow-green
            '#17becf'  # blue-teal
        ]

        markers = [
            "cross",
            "circle-open-dot",
            "x-open-dot",
            "triangle-up-open-dot",
            "y-down-open",
            "diamond-open-dot",
            "hourglass",
            "hash",
            "star",
            "square",
        ]

        m1 = living_reward
        m2 = rotation_mat
        m3 = squ_cost
        eval_metrics = [m1, m2, m3]
        metric_names = ["Living", "Rotation", "Square"]

        fig = plotly.subplots.make_subplots(
            rows=3,
            cols=2,
            # subplot_titles=["Living", "Rotation", "Square"],
            subplot_titles=[
                "Pitch",
                "Roll",
                " ",
                " ",
                " ",
                " ",
            ],
            vertical_spacing=0.03,
            horizontal_spacing=0.03,
            shared_xaxes=True,
        )  # go.Figure()

        fig_mpc = go.Figure()
        fig_sac = go.Figure()

        pry = [1, 0, 2]
        # state0 = 2*env.reset()
        # state0 = env.reset()
        state0 = np.array([0, np.deg2rad(15), 0, 0, 0, 0])
        for i, (con, lab, m) in enumerate(zip(controllers, labels, metrics)):
            print(f"Evaluating controller type {lab}")
            _ = env.reset()
            env.set_state(np.concatenate((np.zeros(6), state0)))
            state = state0
            states = []
            actions = []
            rews = []
            done = False
            # for t in range(cfg.experiment.r_len + 1):
            for t in range(500):
                if done:
                    break
                if "SAC" in lab:
                    with torch.no_grad():
                        with eval_mode(con):
                            action = con.select_action(state)
                            if i < 2:
                                action = np.array([65535, 65535, 65535, 65535
                                                   ]) * (action + 1) / 2
                            else:
                                action = np.array([3000, 3000, 3000, 3000
                                                   ]) * (action + 1) / 2

                else:
                    action = con.get_action(state, metric=eval_metrics[m])
                states.append(state)
                actions.append(action)

                state, rew, done, _ = env.step(action)
                done = done

            states = np.stack(states)
            actions = np.stack(actions)

            pitch = np.degrees(states[:, pry[0]])
            roll = np.degrees(states[:, pry[1]])

            # deal with markers
            num_mark = np.zeros(len(pitch))
            mark_every = 50
            m_size = 32
            start = np.random.randint(0, int(len(pitch) / 10))
            num_mark[start::mark_every] = m_size
            if "SAC" in lab:
                fig_sac.add_trace(
                    go.Scatter(
                        y=pitch,
                        name=metric_names[m],  # legendgroup=lab[:3],
                        # showlegend=(True if (i % 3 == 0) else False),
                        line=dict(color=colors[m], width=4),
                        cliponaxis=False,
                        mode='lines+markers',
                        marker=dict(color=colors[m],
                                    symbol=markers[-m],
                                    size=num_mark.tolist())))

            elif "MPC" in lab:
                fig_mpc.add_trace(
                    go.Scatter(
                        y=pitch,
                        name=metric_names[m],  # legendgroup=lab[:3],
                        # showlegend=(True if (i % 3 == 0) else False),
                        line=dict(color=colors[m], width=4),
                        cliponaxis=False,
                        mode='lines+markers',
                        marker=dict(color=colors[m],
                                    symbol=markers[-m],
                                    size=num_mark.tolist())))

            fig.add_trace(
                go.Scatter(
                    y=pitch,
                    name=lab[:3] + str(m),
                    legendgroup=lab[:3],
                    showlegend=(True if (i % 3 == 0) else False),
                    line=dict(color=colors[int(i / 3)],
                              width=2),  # mode='lines+markers',
                    # marker=dict(color=colors[i], symbol=markers[i], size=16)
                ),
                row=m + 1,
                col=1)

            fig.add_trace(
                go.Scatter(
                    y=roll,
                    name=lab[:3] + str(m),
                    legendgroup=lab[:3],
                    showlegend=(False),
                    line=dict(color=colors[int(i / 3)],
                              width=2),  # mode='lines+markers',
                    # marker=dict(color=colors[i], symbol=markers[i], size=16)
                ),
                row=m + 1,
                col=2)

        fig.update_layout(
            title='Comparison of Controllers and Reward Functions',
            font=dict(family="Times New Roman, Times, serif",
                      size=24,
                      color="black"),
            legend_orientation="h",
            legend=dict(
                x=.6,
                y=0.07,
                bgcolor='rgba(205, 223, 212, .4)',
                bordercolor="Black",
            ),
            # xaxis_title='Timestep',
            # yaxis_title='Angle (Degrees)',
            plot_bgcolor='white',
            width=1600,
            height=1000,
            # xaxis=dict(
            #     showline=True,
            #     showgrid=False,
            #     showticklabels=True, ),
            # yaxis=dict(
            #     showline=True,
            #     showgrid=False,
            #     showticklabels=True, ),
        )

        fig_sac.update_layout(  # title='Comparison of SAC Policies',
            font=dict(family="Times New Roman, Times, serif",
                      size=32,
                      color="black"),
            legend_orientation="h",
            legend=dict(
                x=.35,
                y=0.1,
                bgcolor='rgba(205, 223, 212, .4)',
                bordercolor="Black",
            ),
            # xaxis_title='Timestep',
            # yaxis_title='Angle (Degrees)',
            showlegend=False,
            plot_bgcolor='white',
            width=1600,
            height=800,
            margin=dict(t=5, r=5),
        )

        fig_mpc.update_layout(  # title='Comparison of MPC Policies',
            font=dict(family="Times New Roman, Times, serif",
                      size=32,
                      color="black"),
            legend_orientation="h",
            showlegend=False,
            legend=dict(
                x=.35,
                y=0.1,
                bgcolor='rgba(205, 223, 212, .4)',
                bordercolor="Black",
                # ncol= 2,
            ),
            # xaxis_title='Timestep',
            # yaxis_title='Angle (Degrees)',
            plot_bgcolor='white',
            width=1600,
            height=800,
            margin=dict(t=5, r=5),
        )

        reg_color = 'rgba(255,60,60,.15)'
        fig_sac.add_trace(
            go.Scatter(x=[0, 500],
                       y=[5, 5],
                       name='Living Region',
                       legendgroup='Living Region',
                       fill='tozeroy',
                       mode='lines',
                       fillcolor=reg_color,
                       line=dict(width=0.0,
                                 color=reg_color)))  # fill down to xaxis
        fig_sac.add_trace(
            go.Scatter(x=[0, 500],
                       y=[-5, -5],
                       showlegend=False,
                       legendgroup='Living Region',
                       fill='tozeroy',
                       mode='lines',
                       fillcolor=reg_color,
                       line=dict(width=0.0,
                                 color=reg_color)))  # fill down to xaxis

        fig_mpc.add_trace(
            go.Scatter(x=[0, 500],
                       y=[5, 5],
                       name='Living Region',
                       legendgroup='Living Region',
                       fill='tozeroy',
                       mode='lines',
                       fillcolor=reg_color,
                       line=dict(width=0.0,
                                 color=reg_color)))  # fill down to xaxis
        fig_mpc.add_trace(
            go.Scatter(x=[0, 500],
                       y=[-5, -5],
                       showlegend=False,
                       legendgroup='Living Region',
                       fill='tozeroy',
                       mode='lines',
                       fillcolor=reg_color,
                       line=dict(width=0.0,
                                 color=reg_color)))  # fill down to xaxis

        # SOLO
        rang_ind = [-20, 20]
        fig_sac.update_xaxes(
            title_text="Timestep",
            range=[0, 500],
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig_sac.update_yaxes(
            title_text="Pitch (degrees)",
            range=rang_ind,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig_sac.show()
        fig_sac.write_image(os.getcwd() + "/compare_sac.pdf")

        fig_mpc.update_xaxes(
            title_text="Timestep",
            range=[0, 500],
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig_mpc.update_yaxes(
            title_text="Pitch (degrees)",
            range=rang_ind,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig_mpc.show()
        fig_mpc.write_image(os.getcwd() + "/compare_mpc.pdf")

        # COMPARISON

        fig.update_xaxes(
            title_text="Timestep",
            row=3,
            col=1,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_xaxes(
            row=2,
            col=1,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_xaxes(
            row=1,
            col=1,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_xaxes(
            title_text="Timestep",
            row=3,
            col=2,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_xaxes(
            row=2,
            col=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_xaxes(
            row=1,
            col=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        # fig.update_xaxes(title_text="xaxis 1 title", row=1, col=1)
        # fig.update_yaxes(title_text="Roll (Degrees)", row=1, col=1)

        rang = [-30, 30]
        nticks = 6
        fig.update_yaxes(
            title_text="Living Rew.",
            range=rang,
            row=1,
            col=1,
            nticks=nticks,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_yaxes(
            title_text="Rotation Rew.",
            range=rang,
            row=2,
            col=1,
            nticks=nticks,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_yaxes(
            title_text="Square Cost",
            range=rang,
            row=3,
            col=1,
            nticks=nticks,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_yaxes(
            range=rang,
            row=1,
            col=2,
            nticks=nticks,
            showticklabels=False,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_yaxes(
            range=rang,
            row=2,
            col=2,
            nticks=nticks,
            showticklabels=False,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )
        fig.update_yaxes(
            range=rang,
            row=3,
            col=2,
            nticks=nticks,
            showticklabels=False,
            ticks="inside",
            tickwidth=2,
            zeroline=True,
            zerolinecolor='rgba(0,0,0,.5)',
            zerolinewidth=1,
        )

        print(f"Plotting {len(labels)} control responses")
        # save = False
        # if save:
        #     fig.write_image(os.getcwd() + "compare.png")
        # else:
        #     fig.show()
        #
        # return fig

    # compare_control(env, cfg, save=True)
    # quit()
    plot_results(logx=False, save=True, mpc=False)
    quit()

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # # # # # # # # # # # Evalutation Function  # # # # # # # # # # # # # # # # # # # #
    def bo_rollout_wrapper(params, weights=None):  # env, controller, exp_cfg):
        pid_1 = [params["pitch-p"], params["pitch-i"], params["pitch-d"]]
        # pid_1 = [params["roll-p"], params["roll-i"],
        #          params["roll-d"]]  # [params["pitch-p"], params["pitch-i"], params["pitch-d"]]
        pid_2 = [params["roll-p"], params["roll-i"], params["roll-d"]]
        print(
            f"Optimizing Parameters {np.round(pid_1, 3)},{np.round(pid_2, 3)}")
        pid_params = [[pid_1[0], pid_1[1], pid_1[2]],
                      [pid_2[0], pid_2[1], pid_2[2]]]
        # pid_params = [[1000, 0, 0], [1000, 0, 0]]
        pid = PidPolicy(cfg)

        pid.set_params(pid_params)

        cum_cost = []
        r = 0
        fncs = [squ_cost, living_reward, rotation_mat]
        mult_rewards = [[] for _ in range(len(fncs))]
        while r < cfg.experiment.repeat:
            pid.reset()
            states, actions, rews, sim_error = rollout(env, pid, exp_cfg)
            # plot_rollout(states, actions, pry=[1, 0, 2])
            rewards_full = get_rewards(states, actions, fncs=fncs)
            for i, vec in enumerate(rewards_full):
                mult_rewards[i].append(vec)

            # if sim_error:
            #     print("Repeating strange simulation")
            #     continue
            # if len(rews) < 400:
            #     cum_cost.append(-(cfg.experiment.r_len - len(rews)) / cfg.experiment.r_len)
            # else:
            rollout_cost = np.sum(rews) / cfg.experiment.r_len  # / len(rews)
            # if rollout_cost > max_cost:
            #      max_cost = rollout_cost
            # rollout_cost += get_reward_euler(states[-1], actions[-1])
            cum_cost.append(rollout_cost)
            r += 1

        std = np.std(cum_cost)
        cum_cost = np.mean(cum_cost)
        # print(f"Cum. Cost {cum_cost / max_cost}")
        # print(f"- Mean Cum. Cost / Rew: {cum_cost}, std dev: {std}")
        eval = {
            "Square": (np.mean(rewards_full[0]), np.std(rewards_full[0])),
            "Living": (np.mean(rewards_full[1]), np.std(rewards_full[1])),
            "Rotation": (np.mean(rewards_full[2]), np.std(rewards_full[2]))
        }

        for n, (key, value) in enumerate(eval.items()):
            if n == 0:
                print(f"- Square {np.round(value, 4)}")
            elif n == 1:
                print(f"- Living {np.round(value, 4)}")
            else:
                print(f"- Rotn {np.round(value, 4)}")
        return eval
        # return cum_cost.reshape(1, 1), std

    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

    from ax import (
        ComparisonOp,
        ParameterType,
        RangeParameter,
        SearchSpace,
        SimpleExperiment,
        OutcomeConstraint,
    )

    exp = SimpleExperiment(
        name="PID Control Robot",
        search_space=SearchSpace([
            RangeParameter(
                name=f"roll-p",
                parameter_type=ParameterType.FLOAT,
                lower=1.0,
                upper=10000.0,
                log_scale=True,
            ),
            # FixedParameter(name="roll-i", value=0.0, parameter_type=ParameterType.FLOAT),
            RangeParameter(
                name=f"roll-i",
                parameter_type=ParameterType.FLOAT,
                lower=0,
                upper=1000.0,
                log_scale=False,
            ),
            RangeParameter(
                name=f"roll-d",
                parameter_type=ParameterType.FLOAT,
                lower=.1,
                upper=5000.0,
                log_scale=True,
            ),
            RangeParameter(
                name=f"pitch-p",
                parameter_type=ParameterType.FLOAT,
                lower=1.0,
                upper=10000.0,
                log_scale=True,
            ),
            RangeParameter(
                name=f"pitch-d",
                parameter_type=ParameterType.FLOAT,
                lower=0,
                upper=1000.0,
                log_scale=False,
            ),
            RangeParameter(
                name=f"pitch-i",
                parameter_type=ParameterType.FLOAT,
                lower=.1,
                upper=5000.0,
                log_scale=True,
            ),
            # FixedParameter(name="pitch-i", value=0.0, parameter_type=ParameterType.FLOAT),
        ]),
        evaluation_function=bo_rollout_wrapper,
        objective_name=cfg.metric.name,
        minimize=cfg.metric.minimize,
        outcome_constraints=[],
    )

    from ax.storage.metric_registry import register_metric
    from ax.storage.runner_registry import register_runner

    class GenericMetric(Metric):
        def fetch_trial_data(self, trial):
            records = []
            for arm_name, arm in trial.arms_by_name.items():
                params = arm.parameters
                mean, sem = bo_rollout_wrapper(params)
                records.append({
                    "arm_name": arm_name,
                    "metric_name": self.name,
                    "mean": mean,
                    "sem": sem,
                    "trial_index": trial.index,
                })
            return Data(df=pd.DataFrame.from_records(records))

    class MyRunner(Runner):
        def run(self, trial):
            return {"name": str(trial.index)}

    optimization_config = OptimizationConfig(objective=Objective(
        metric=GenericMetric(name=cfg.metric.name),
        minimize=cfg.metric.minimize,
    ), )
    register_metric(GenericMetric)
    register_runner(MyRunner)

    exp.runner = MyRunner()
    exp.optimization_config = optimization_config

    log.info(f"Running experiment, metric name {cfg.metric.name}")
    log.info(f"Running Sobol initialization trials...")
    sobol = Models.SOBOL(exp.search_space)
    num_search = cfg.bo.random
    for i in range(num_search):
        exp.new_trial(generator_run=sobol.gen(1))
        exp.trials[len(exp.trials) - 1].run()

    import plotly.graph_objects as go

    gpei = Models.BOTORCH(experiment=exp, data=exp.eval())

    objectives = ["Living", "Square", "Rotation"]

    def plot_all(model, objectives, name="", rend=False):
        for o in objectives:
            plot = plot_contour(
                model=model,
                param_x="roll-p",
                param_y="roll-d",
                metric_name=o,
            )
            plot[0]['layout']['title'] = o
            data = plot[0]['data']
            lay = plot[0]['layout']

            for i, d in enumerate(data):
                if i > 1:
                    d['cliponaxis'] = False

            fig = {
                "data": data,
                "layout": lay,
            }
            go.Figure(fig).write_image(name + o + ".png")
            if rend: render(plot)

    plot_all(gpei, objectives, name="Random fit-")

    num_opt = cfg.bo.optimized
    for i in range(num_opt):
        log.info(f"Running GP+EI optimization trial {i + 1}/{num_opt}...")
        # Reinitialize GP+EI model at each step with updated data.
        batch = exp.new_trial(generator_run=gpei.gen(1))
        gpei = Models.BOTORCH(experiment=exp, data=exp.eval())

        if ((i + 1) % 10) == 0:
            plot_all(gpei,
                     objectives,
                     name=f"optimizing {str(i + 1)}-",
                     rend=False)

    from ax.plot.exp_utils import exp_to_df

    best_arm, _ = gpei.gen(1).best_arm_predictions
    best_parameters = best_arm.parameters
    log.info(f"Best parameters {best_parameters}")

    experiment_log = {
        "Exp": exp_to_df(exp=exp),
        "Cfg": cfg,
        "Best_param": best_parameters,
    }

    log.info("Printing Parameters")
    log.info(exp_to_df(exp=exp))
    save_log(cfg, exp, experiment_log)

    fig_learn = plot_learning(exp, cfg)
    fig_learn.write_image("learning" + ".png")
    fig_learn.show()
    plot_all(gpei, objectives, name=f"FINAL -", rend=True)