Beispiel #1
0
    def setUp(self):
        state_init = parameters.COBB_INIT_FIG4A

        setup = {'state_init': state_init, 'times': np.linspace(0, 100, 101)}

        self.model = ms_approx.MixedStandApprox(setup, ZERO_PARAMS,
                                                np.zeros(7))
Beispiel #2
0
    def min_func(factor):
        """Function to minimise, SSE between healthy tanoak over range of rates."""

        approx_tans = np.zeros_like(test_rates)

        setup, params = utils.get_setup_params(
            parameters.CORRECTED_PARAMS,
            scale_inf=True,
            host_props=parameters.COBB_PROP_FIG4A,
            extra_spread=False)
        params['max_budget'] = 1000

        beta_names = [
            'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12',
            'beta_21', 'beta_2'
        ]
        beta = np.array([scale_and_fit_results[x] for x in beta_names])

        for i, rate in enumerate(test_rates):
            params['rogue_rate'] = rate * factor
            approx_model = ms_approx.MixedStandApprox(setup, params, beta)
            approx_run = approx_model.run_policy(const_rogue_policy)

            approx_tans[i] = np.sum(approx_run[0][[6, 8, 9, 11], -1])

            logging.info("Approx run, Factor %f, Rate: %f, tans: %f", factor,
                         rate, approx_tans[i])

        return np.sum(np.square(approx_tans - sim_tans))
Beispiel #3
0
    def test_sis_accuracy(self):
        """Test non-spatial SIS setup matches analytic solution."""

        beta = 0.05
        mu = 1.0
        I0 = 0.1
        N = 100

        params = get_sis_params(beta, mu)

        state_init = np.zeros(15)
        state_init[0] = N - I0
        state_init[1] = I0

        setup = {'state_init': state_init, 'times': np.linspace(0, 10.0, 101)}

        inf_rates = np.zeros(7)
        inf_rates[0] = beta

        model = ms_approx.MixedStandApprox(setup, params, inf_rates)
        model_results, *_ = model.run_policy(None)

        analytic_results = sis_analytic(setup['times'], beta, mu, I0, N)

        print(analytic_results)
        print(model_results[1, :])
        print(np.isclose(model_results[1, :], analytic_results, atol=1e-5))
        self.assertTrue(
            np.allclose(model_results[1, :], analytic_results, atol=1e-5))
Beispiel #4
0
    def setUp(self):

        self.params = parameters.CORRECTED_PARAMS

        state_init = parameters.COBB_INIT_FIG4A
        state_init[1] = state_init[0]/400
        state_init[0] *= 399/400
        state_init[13] = state_init[12]/400
        state_init[12] *= 399/400

        # Now initialise space weights and recruitment rates
        self.params, state_init = utils.initialise_params(self.params, init_state=state_init)

        setup = {
            'state_init': state_init,
            'times': np.linspace(0, 50.0, 101)
        }

        beta = 0.5 * np.array([0.88, 0.81, 0.65, 0.48, 5.88, 0.0, 7.37])

        self.params['inf_tanoak_tanoak'] = beta[0:4]
        self.params['inf_bay_to_tanoak'] = beta[4]
        self.params['inf_tanoak_to_bay'] = beta[5]
        self.params['inf_bay_to_bay'] = beta[6]
        self.params['payoff_factor'] = 1.0
        self.params['treat_eff'] = 0.75
        self.params['max_budget'] = 100
        self.params['rel_small_cost'] = 0.5

        self.approx_model = ms_approx.MixedStandApprox(setup, self.params, beta)
Beispiel #5
0
def make_data(folder=None):
    """Scan over budgets to analyse change in OL control"""

    if folder is None:
        folder = os.path.join(os.path.realpath(__file__), '..', '..', 'data',
                              'budget_scan')

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    logging.info("Parameters: %s", params)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])
    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
    approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']

    approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)

    # budgets = np.array([8, 10, 12, 14, 16, 18, 20])
    budgets = np.array([36])

    for budget in budgets:
        logging.info("Budget: %f", budget)
        params['max_budget'] = budget
        approx_model.params['max_budget'] = budget
        approx_params['max_budget'] = budget

        _, control, _ = approx_model.optimise(n_stages=20,
                                              init_policy=even_policy)

        approx_model.save_optimisation(
            os.path.join(folder, "budget_" + str(int(budget)) + "_OL.pkl"))

        mpc_controller = mpc.Controller(setup,
                                        params,
                                        beta,
                                        approx_params=approx_params)
        mpc_controller.optimise(horizon=100,
                                time_step=0.5,
                                end_time=100,
                                update_period=20,
                                rolling_horz=False,
                                stage_len=5,
                                init_policy=control,
                                use_init_first=True)

        mpc_controller.save_optimisation(
            os.path.join(folder, "budget_" + str(int(budget)) + "_MPC.pkl"))
Beispiel #6
0
def run_optimisations(datapath):
    """Run OL and MPC frameworks using newly parameterised roguing rate."""

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    with open(os.path.join(datapath, "scaling_results.json"), "r") as infile:
        global_scaling = json.load(infile)

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])

    control_factor = global_scaling['roguing_factor']
    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= control_factor
    approx_params['rogue_cost'] /= control_factor

    approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)

    def even_policy(time):
        return np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])

    _ = approx_model.optimise(n_stages=20, init_policy=even_policy)
    approx_model.save_optimisation(
        os.path.join(datapath, "OL_GloballyScaledControl.pkl"))
    ol_control = interp1d(setup['times'][:-1],
                          approx_model.optimisation['control'],
                          kind="zero",
                          fill_value="extrapolate")

    mpc_controller = mpc.Controller(setup,
                                    params,
                                    beta,
                                    approx_params=approx_params)
    _ = mpc_controller.optimise(horizon=100,
                                time_step=0.5,
                                end_time=100,
                                update_period=20,
                                rolling_horz=False,
                                stage_len=5,
                                init_policy=ol_control,
                                use_init_first=True)
    mpc_controller.save_optimisation(
        os.path.join(datapath, "MPC_GloballyScaledControl.pkl"))
Beispiel #7
0
def make_plots(data_folder=None, fig_folder=None):
    """Generate plot of budget scan analysis"""

    if data_folder is None:
        data_folder = os.path.join(os.path.realpath(__file__), '..', '..',
                                   'data', 'budget_scan')

    if fig_folder is None:
        fig_folder = os.path.join(os.path.realpath(__file__), '..', '..',
                                  'figures', 'budget_scan')

    budgets = np.array([8, 10, 12, 14, 16, 18, 20])
    n_budgets = len(budgets)

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    model = ms_sim.MixedStandSimulator(setup, params)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])
    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
    approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']

    approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)

    # Collect control proportions
    order = np.array([3, 4, 5, 6, 0, 1, 2, 7, 8])
    control_abs_ol = np.zeros((n_budgets, 9))
    control_abs_mpc = np.zeros((n_budgets, 9))
    real_objectives_ol = np.zeros(n_budgets)
    real_objectives_mpc = np.zeros(n_budgets)
    approx_objectives_ol = np.zeros(n_budgets)
    approx_objectives_mpc = np.zeros(n_budgets)
    for i, budget in enumerate(budgets):
        logging.info("Budget: %f", budget)
        approx_model.load_optimisation(
            os.path.join(data_folder,
                         "budget_" + str(int(budget)) + "_OL.pkl"))

        control = approx_model.optimisation['control']
        control_policy = interp1d(setup['times'][:-1],
                                  control,
                                  kind="zero",
                                  fill_value="extrapolate")
        model.run_policy(control_policy)
        approx_model.run_policy(control_policy)

        control[0:3] *= params['rogue_rate'] * params['rogue_cost'] * 0.5
        control[3:7] *= params['thin_rate'] * params['thin_cost'] * 0.5
        control[7:] *= params['protect_rate'] * params['protect_cost'] * 0.5
        control[0] *= params['rel_small_cost']
        control[3] *= params['rel_small_cost']

        control_abs_ol[i] = (np.sum(control, axis=1))[order] / 100

        real_objectives_ol[i] = model.run['objective']
        approx_objectives_ol[i] = approx_model.run['objective']

        mpc_controller = mpc.Controller.load_optimisation(
            os.path.join("data", "budget_scan",
                         "budget_" + str(int(budget)) + "_MPC.pkl"))

        sim_run, approx_run = mpc_controller.run_control()
        approx_model.run['state'] = approx_run[0]
        approx_model.run['objective'] = approx_run[1]
        model.run['state'] = sim_run[0]
        model.run['objective'] = sim_run[1]

        control = mpc_controller.control

        control[0:3] *= params['rogue_rate'] * params['rogue_cost'] * 0.5
        control[3:7] *= params['thin_rate'] * params['thin_cost'] * 0.5
        control[7:] *= params['protect_rate'] * params['protect_cost'] * 0.5
        control[0] *= params['rel_small_cost']
        control[3] *= params['rel_small_cost']

        control_abs_mpc[i] = (np.sum(control, axis=1))[order] / 100

        real_objectives_mpc[i] = model.run['objective']
        approx_objectives_mpc[i] = approx_model.run['objective']

    # Make plot
    labels = [
        "Thin Tan (Small)", "Thin Tan (Large)", "Thin Bay", "Thin Red",
        "Rogue Tan (Small)", "Rogue Tan (Large)", "Rogue Bay",
        "Protect Tan (Small)", "Protect Tan (Large)"
    ]

    colors = visualisation.CONTROL_COLOURS

    # Open-loop plot
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax2 = ax.twinx()

    color = 'tab:red'
    ax2.set_ylabel('Objective', color=color)
    ax2.plot(budgets,
             -1 * approx_objectives_ol,
             '--',
             color=color,
             label='OL approximate model objective')
    ax2.tick_params(axis='y', labelcolor=color)

    bars = []

    for i in range(9):
        b = ax.bar(budgets - 10,
                   control_abs_ol[:, i],
                   bottom=np.sum(control_abs_ol[:, :i], axis=1),
                   color=colors[i],
                   width=15,
                   alpha=0.75)
        bars.append(b)

    ax2.legend(ncol=1)
    ax.set_xlabel("Budget")
    ax.set_ylabel("Expenditure")

    color = 'tab:red'
    ax2.set_ylabel('Objective', color=color)
    ax2.plot(budgets,
             -1 * real_objectives_mpc,
             '-',
             color=color,
             label='MPC simulation objective')
    ax2.tick_params(axis='y', labelcolor=color)

    bars = []

    for i in range(9):
        b = ax.bar(budgets + 10,
                   control_abs_mpc[:, i],
                   bottom=np.sum(control_abs_mpc[:, :i], axis=1),
                   color=colors[i],
                   width=15,
                   alpha=0.75)
        bars.append(b)

    ax.legend(bars,
              labels,
              bbox_to_anchor=(0.5, -0.15),
              loc="upper center",
              ncol=3,
              frameon=True)
    ax2.legend(ncol=1, loc='upper left')
    ax.set_xlabel("Budget")
    ax.set_ylabel("Expenditure")
    ax.set_title('Left: OL, right: MPC', fontsize=8)

    ax.set_ylim([0, 900])
    ax2.set_ylim([0.0, 1.8])
    ax.set_xticks([0, 50, 100, 200, 300, 400, 500, 600, 700])
    fig.tight_layout()
    fig.savefig(os.path.join(fig_folder, "BudgetScan.pdf"), dpi=300)
Beispiel #8
0
def main(n_reps=10,
         sigma=0.25,
         append=False,
         folder_name='parameter_sensitivity',
         run_mpc=False):
    """Run sensitivity tests."""

    os.makedirs(os.path.join('data', folder_name), exist_ok=True)

    # Analysis:
    # 1. First construct default parameters (corrected and scaled Cobb)
    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    mpc_args = {
        'horizon': 100,
        'time_step': 0.5,
        'end_time': 100,
        'update_period': 20,
        'rolling_horz': False,
        'stage_len': 5,
        'init_policy': None,
        'use_init_first': True
    }

    ncells = np.product(setup['landscape_dims'])

    # Baseline no control run
    model = ms_sim.MixedStandSimulator(setup, params)
    model.run_policy(control_policy=None, n_fixed_steps=None)

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    if not append:
        model.save_run(
            os.path.join("data", folder_name, "no_control_baseline.pkl"))

        beta_names = [
            'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12',
            'beta_21', 'beta_2'
        ]
        beta = np.array([scale_and_fit_results[x] for x in beta_names])

        approx_params = copy.deepcopy(params)
        approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
        approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']
        approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)

        logging.info("Running baseline OL control")
        _, baseline_ol_control_policy, exit_text = approx_model.optimise(
            n_stages=20, init_policy=even_policy)
        approx_model.save_optimisation(
            os.path.join("data", folder_name, "ol_control_baseline.pkl"))

        if run_mpc:
            logging.info("Running baseline MPC control")
            mpc_args['init_policy'] = baseline_ol_control_policy
            mpc_controller = mpc.Controller(setup,
                                            params,
                                            beta,
                                            approx_params=approx_params)
            mpc_controller.optimise(**mpc_args)
            mpc_controller.save_optimisation(
                os.path.join("data", folder_name, "mpc_control_baseline.pkl"))

    # Which parameters to perturb:
    # First single numbers that can be perturbed
    perturbing_params_numbers = [
        'inf_bay_to_bay', 'inf_bay_to_tanoak', 'inf_tanoak_to_bay',
        'nat_mort_bay', 'nat_mort_redwood', 'recov_tanoak', 'recov_bay',
        'resprout_tanoak'
    ]
    # And lists of parameters
    perturbing_params_lists = [
        'inf_tanoak_tanoak', 'nat_mort_tanoak', 'inf_mort_tanoak',
        'trans_tanoak', 'recruit_tanoak'
    ]

    if append:
        logging.info("Loading previous dataset to append new data to")
        # Read in summary data already generated
        with open(os.path.join("data", folder_name, "summary.json"),
                  "r") as infile:
            summary_results = json.load(infile)

        approx_model = ms_approx.MixedStandApprox.load_optimisation_class(
            os.path.join("data", folder_name, "ol_control_baseline.pkl"))
        baseline_ol_control_policy = interp1d(
            approx_model.setup['times'][:-1],
            approx_model.optimisation['control'],
            kind="zero",
            fill_value="extrapolate")

        n_reps = (len(summary_results), len(summary_results) + n_reps)

        ol_alloc_results = np.load(
            os.path.join("data", folder_name, "ol_alloc_results.npy"))
        mpc_alloc_results = np.load(
            os.path.join("data", folder_name, "mpc_alloc_results.npy"))
    else:
        # Otherwise start afresh
        summary_results = []
        ol_alloc_results = np.zeros((0, 9, len(setup['times']) - 1))
        mpc_alloc_results = np.zeros((0, 9, len(setup['times']) - 1))
        n_reps = (0, n_reps)

    error_dist = truncnorm(-1.0 / sigma, np.inf, loc=1.0, scale=sigma)

    for i in range(*n_reps):
        # 2. Perturb these parameters using Normal distribution, sigma 25%
        logging.info("Perturbing parameter set %d of %d with sigma %f", i + 1,
                     n_reps[1], sigma)
        new_params = copy.deepcopy(params)
        for param_key in perturbing_params_numbers:
            new_params[param_key] = new_params[param_key] * error_dist.rvs()
        for param_key in perturbing_params_lists:
            new_params[param_key] = (
                new_params[param_key] *
                error_dist.rvs(size=len(new_params[param_key])))

        # Set space weights and recruitment rates to NaN so can be recaluclated for dyn equilibrium
        new_params['recruit_bay'] = np.nan
        new_params['recruit_redwood'] = np.nan
        new_params['space_tanoak'] = np.full(4, np.nan)

        # 3. Recalculate space weights & recruitment rates to give dynamic equilibrium
        new_params, _ = utils.initialise_params(
            new_params, host_props=parameters.COBB_PROP_FIG4A)

        # 4. Run simulation model with no control policy
        model = ms_sim.MixedStandSimulator(setup, new_params)
        model.run_policy(control_policy=None, n_fixed_steps=None)
        model.save_run(
            os.path.join("data", folder_name, "no_control_{}.pkl".format(i)))

        # 5. Fit approximate model
        _, beta = scale_and_fit.fit_beta(setup, new_params)

        approx_new_params = copy.deepcopy(params)
        approx_new_params['rogue_rate'] *= scale_and_fit_results[
            'roguing_factor']
        approx_new_params['rogue_cost'] /= scale_and_fit_results[
            'roguing_factor']

        # 6. Optimise control (open-loop)
        approx_model = ms_approx.MixedStandApprox(setup, approx_new_params,
                                                  beta)
        *_, exit_text = approx_model.optimise(
            n_stages=20, init_policy=baseline_ol_control_policy)

        if exit_text not in [
                "Optimal Solution Found.", "Solved To Acceptable Level."
        ]:
            logging.warning(
                "Failed optimisation. Trying intialisation from previous solution."
            )
            filename = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), '..',
                'mixed_stand_model', "BOCOP", "problem.def")

            with open(filename, "r") as infile:
                all_lines = infile.readlines()
            all_lines[31] = "# " + all_lines[31]
            all_lines[32] = "# " + all_lines[32]
            all_lines[33] = all_lines[33][2:]
            all_lines[34] = all_lines[34][2:]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            *_, exit_text = approx_model.optimise(
                n_stages=20, init_policy=baseline_ol_control_policy)

            all_lines[31] = all_lines[31][2:]
            all_lines[32] = all_lines[32][2:]
            all_lines[33] = "# " + all_lines[33]
            all_lines[34] = "# " + all_lines[34]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            if exit_text not in [
                    "Optimal Solution Found.", "Solved To Acceptable Level."
            ]:
                logging.error(
                    "Failed optimisation. Falling back to init policy.")

        approx_model.save_optimisation(
            os.path.join("data", folder_name, "ol_control_{}.pkl".format(i)))

        # Run OL control to get objective
        ol_control_policy = interp1d(setup['times'][:-1],
                                     approx_model.optimisation['control'],
                                     kind="zero",
                                     fill_value="extrapolate")
        sim_run = model.run_policy(ol_control_policy)
        ol_obj = model.run['objective']
        sim_state = np.sum(np.reshape(sim_run[0],
                                      (ncells, 15, -1)), axis=0) / ncells
        allocation = (np.array([
            sim_state[1] + sim_state[4], sim_state[7] + sim_state[10],
            sim_state[13],
            np.sum(sim_state[0:6], axis=0),
            np.sum(sim_state[6:12],
                   axis=0), sim_state[12] + sim_state[13], sim_state[14],
            sim_state[0] + sim_state[3], sim_state[6] + sim_state[9]
        ])[:, :-1] * approx_model.optimisation['control'])

        allocation[0:3] *= params['rogue_rate'] * params['rogue_cost']
        allocation[3:7] *= params['thin_rate'] * params['thin_cost']
        allocation[7:] *= params['protect_rate'] * params['protect_cost']
        allocation[0] *= params['rel_small_cost']
        allocation[3] *= params['rel_small_cost']

        expense = utils.control_expenditure(
            approx_model.optimisation['control'], new_params,
            sim_state[:, :-1])
        for j in range(len(setup['times']) - 1):
            if expense[j] > new_params['max_budget']:
                allocation[:, j] *= new_params['max_budget'] / expense[j]

        ol_alloc_results = np.concatenate([ol_alloc_results, [allocation]],
                                          axis=0)

        if run_mpc:
            mpc_args['init_policy'] = ol_control_policy
            # Optimise control (MPC)
            mpc_controller = mpc.Controller(setup,
                                            new_params,
                                            beta,
                                            approx_params=approx_new_params)
            *_, mpc_obj = mpc_controller.optimise(**mpc_args)
            mpc_controller.save_optimisation(
                os.path.join("data", folder_name,
                             "mpc_control_{}.pkl".format(i)))
            sim_run, _ = mpc_controller.run_control()

            sim_state = np.sum(np.reshape(sim_run[0], (ncells, 15, -1)),
                               axis=0) / ncells
            allocation = (np.array([
                sim_state[1] + sim_state[4], sim_state[7] + sim_state[10],
                sim_state[13],
                np.sum(sim_state[0:6], axis=0),
                np.sum(sim_state[6:12],
                       axis=0), sim_state[12] + sim_state[13], sim_state[14],
                sim_state[0] + sim_state[3], sim_state[6] + sim_state[9]
            ])[:, :-1] * mpc_controller.control)

            allocation[0:3] *= params['rogue_rate'] * params['rogue_cost']
            allocation[3:7] *= params['thin_rate'] * params['thin_cost']
            allocation[7:] *= params['protect_rate'] * params['protect_cost']
            allocation[0] *= params['rel_small_cost']
            allocation[3] *= params['rel_small_cost']

            expense = utils.control_expenditure(mpc_controller.control,
                                                new_params, sim_state[:, :-1])
            for j in range(len(setup['times']) - 1):
                if expense[j] > new_params['max_budget']:
                    allocation[:, j] *= new_params['max_budget'] / expense[j]

            mpc_alloc_results = np.concatenate(
                [mpc_alloc_results, [allocation]], axis=0)

        list_keys = [
            'inf_tanoak_tanoak', 'nat_mort_tanoak', 'inf_mort_tanoak',
            'trans_tanoak', 'recruit_tanoak', 'space_tanoak'
        ]
        for key in list_keys:
            new_params[key] = new_params[key].tolist()

        summary_results.append({
            'iteration': i,
            'params': new_params,
            'beta': beta.tolist(),
            'ol_objective': ol_obj,
            'mpc_objective': mpc_obj
        })

        # Write summary results to file
        with open(os.path.join("data", folder_name, "summary.json"),
                  "w") as outfile:
            json.dump(summary_results, outfile, indent=4)

        # Save control allocations to file
        np.save(os.path.join("data", folder_name, "ol_alloc_results.npy"),
                ol_alloc_results)
        np.save(os.path.join("data", folder_name, "mpc_alloc_results.npy"),
                mpc_alloc_results)
def run_optimisations(ensemble_and_fit,
                      params,
                      setup,
                      n_optim_runs,
                      standard_dev,
                      mpc_args,
                      ol_pol=None):
    """Run open-loop and MPC optimisations over parameter distributions"""

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
    approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']

    approx_model = ms_approx.MixedStandApprox(setup, approx_params,
                                              ensemble_and_fit['fit'])
    sim_model = ms_sim.MixedStandSimulator(setup, params)

    if ol_pol is None:
        _, ol_control, exit_text = approx_model.optimise(
            n_stages=20, init_policy=even_policy)
        if exit_text not in [
                "Optimal Solution Found.", "Solved To Acceptable Level."
        ]:
            logging.warning(
                "Failed optimisation. Trying intialisation from previous solution."
            )
            filename = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), "..",
                "mixed_stand_model", "BOCOP", "problem.def")

            with open(filename, "r") as infile:
                all_lines = infile.readlines()
            all_lines[31] = "# " + all_lines[31]
            all_lines[32] = "# " + all_lines[32]
            all_lines[33] = all_lines[33][2:]
            all_lines[34] = all_lines[34][2:]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            _, ol_control, exit_text = approx_model.optimise(
                n_stages=20, init_policy=even_policy)

            all_lines[31] = all_lines[31][2:]
            all_lines[32] = all_lines[32][2:]
            all_lines[33] = "# " + all_lines[33]
            all_lines[34] = "# " + all_lines[34]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            if exit_text not in [
                    "Optimal Solution Found.", "Solved To Acceptable Level."
            ]:
                raise RuntimeError("Open loop optimisation failed!!")
    else:
        ol_control = ol_pol

    # Create parameter error distribution
    if standard_dev != 0.0:
        error_dist = truncnorm(-1.0 / standard_dev,
                               np.inf,
                               loc=1.0,
                               scale=standard_dev)
        error_samples = np.reshape(error_dist.rvs(size=n_optim_runs * 7),
                                   (n_optim_runs, 7))
    else:
        n_optim_runs = 1
        error_samples = np.ones((n_optim_runs, 7))

    # Sample from parameter distribution and run simulations
    baseline_beta = np.zeros(7)
    baseline_beta[:4] = params['inf_tanoak_tanoak']
    baseline_beta[4] = params['inf_bay_to_tanoak']
    baseline_beta[5] = params['inf_tanoak_to_bay']
    baseline_beta[6] = params['inf_bay_to_bay']
    parameter_samples = error_samples * baseline_beta

    logging.info("Generated ensemble parameters for optimisation runs")

    ol_objs = np.zeros(n_optim_runs)
    for i in range(n_optim_runs):
        logging.info("Using parameter set: %s", parameter_samples[i])
        sim_model.params['inf_tanoak_tanoak'] = parameter_samples[i, 0:4]
        sim_model.params['inf_bay_to_tanoak'] = parameter_samples[i, 4]
        sim_model.params['inf_tanoak_to_bay'] = parameter_samples[i, 5]
        sim_model.params['inf_bay_to_bay'] = parameter_samples[i, 6]

        _, obj, _ = sim_model.run_policy(control_policy=ol_control)
        ol_objs[i] = obj

        logging.info("Open-loop run %d of %d done.", i + 1, n_optim_runs)

    mpc_args['init_policy'] = ol_control

    mpc_objs = np.zeros(n_optim_runs)
    mpc_controls = np.zeros((n_optim_runs, 9, len(setup['times']) - 1))
    for i in range(n_optim_runs):
        logging.info("Using parameter set: %s", parameter_samples[i])
        sim_model.params['inf_tanoak_tanoak'] = parameter_samples[i, 0:4]
        sim_model.params['inf_bay_to_tanoak'] = parameter_samples[i, 4]
        sim_model.params['inf_tanoak_to_bay'] = parameter_samples[i, 5]
        sim_model.params['inf_bay_to_bay'] = parameter_samples[i, 6]

        mpc_controller = mpc.Controller(setup,
                                        sim_model.params,
                                        ensemble_and_fit['fit'],
                                        approx_params=approx_params)
        _, _, mpc_control, mpc_obj = mpc_controller.optimise(**mpc_args)
        mpc_objs[i] = mpc_obj
        mpc_controls[i] = mpc_control

        logging.info("MPC run %d of %d done.", i + 1, n_optim_runs)

    ret_dict = {
        'params': parameter_samples,
        'ol_control': np.array([ol_control(t) for t in setup['times'][:-1]]).T,
        'ol_objs': ol_objs,
        'mpc_control': mpc_controls,
        'mpc_objs': mpc_objs
    }

    return ret_dict
Beispiel #10
0
def make_data(folder=None):
    """Scan over budgets to analyse change in OL control"""

    if folder is None:
        folder = os.path.join(os.path.realpath(__file__), '..', '..', 'data',
                              'div_cost_scan')

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    logging.info("Parameters: %s", params)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])

    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
    approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']

    approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)

    div_prop = np.array(
        [0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
    div_costs = div_prop / (setup['times'][-1] * np.log(3))

    for div_cost, prop in zip(div_costs, div_prop):
        logging.info("Diversity cost: %f", div_cost)
        params['div_cost'] = div_cost
        approx_params['div_cost'] = div_cost
        approx_model.params['div_cost'] = div_cost

        _, control, exit_text = approx_model.optimise(n_stages=20,
                                                      init_policy=even_policy)

        if exit_text not in [
                "Optimal Solution Found.", "Solved To Acceptable Level."
        ]:
            logging.warning(
                "Failed optimisation. Trying intialisation from previous solution."
            )
            filename = os.path.join(
                os.path.dirname(os.path.realpath(__file__)), '..',
                'mixed_stand_model', "BOCOP", "problem.def")

            with open(filename, "r") as infile:
                all_lines = infile.readlines()
            all_lines[31] = "# " + all_lines[31]
            all_lines[32] = "# " + all_lines[32]
            all_lines[33] = all_lines[33][2:]
            all_lines[34] = all_lines[34][2:]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            _, control, exit_text = approx_model.optimise(
                n_stages=20, init_policy=even_policy)

            all_lines[31] = all_lines[31][2:]
            all_lines[32] = all_lines[32][2:]
            all_lines[33] = "# " + all_lines[33]
            all_lines[34] = "# " + all_lines[34]
            with ms_approx._try_file_open(filename) as outfile:
                outfile.writelines(all_lines)

            if exit_text not in [
                    "Optimal Solution Found.", "Solved To Acceptable Level."
            ]:
                logging.error("Failed optimisation in OL optimisation.")

        approx_model.save_optimisation(
            os.path.join(folder, "div_cost_" + str(prop) + "_OL.pkl"))

        mpc_controller = mpc.Controller(setup,
                                        params,
                                        beta,
                                        approx_params=approx_params)
        mpc_controller.optimise(horizon=100,
                                time_step=0.5,
                                end_time=100,
                                update_period=20,
                                rolling_horz=False,
                                stage_len=5,
                                init_policy=control,
                                use_init_first=True)

        mpc_controller.save_optimisation(
            os.path.join(folder, "div_cost_" + str(prop) + "_MPC.pkl"))
Beispiel #11
0
def make_plots(data_folder=None, fig_folder=None):
    """Generate plot of diversity cost scan."""

    if data_folder is None:
        data_folder = os.path.join(os.path.realpath(__file__), '..', '..',
                                   'data', 'div_cost_scan')

    if fig_folder is None:
        fig_folder = os.path.join(os.path.realpath(__file__), '..', '..',
                                  'figures', 'div_cost_scan')

    div_props = np.array(
        [0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
    n_costs = len(div_props)

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])
    approx_model = ms_approx.MixedStandApprox(setup, params, beta)

    model = ms_sim.MixedStandSimulator(setup, params)

    ncells = np.product(setup['landscape_dims'])

    # Collect control proportions
    order = np.array([3, 4, 5, 6, 0, 1, 2, 7, 8])
    control_abs_ol = np.zeros((n_costs, 9))
    control_abs_mpc = np.zeros((n_costs, 9))
    objectives_ol = np.zeros(n_costs)
    objectives_mpc = np.zeros(n_costs)
    for i, div_prop in enumerate(div_props):
        div_cost = div_prop / (setup['times'][-1] * np.log(3))

        logging.info("Diversity cost: %f", div_cost)
        approx_model.load_optimisation(
            os.path.join(data_folder, "div_cost_" + str(div_prop) + "_OL.pkl"))

        control = approx_model.optimisation['control']
        control_policy = interp1d(setup['times'][:-1],
                                  control,
                                  kind="zero",
                                  fill_value="extrapolate")
        sim_run, obj, objs = model.run_policy(control_policy)

        sim_state = np.sum(np.reshape(sim_run,
                                      (ncells, 15, -1)), axis=0) / ncells

        allocation = np.array([
            sim_state[1] + sim_state[4], sim_state[7] + sim_state[10],
            sim_state[13],
            np.sum(sim_state[0:6], axis=0),
            np.sum(sim_state[6:12],
                   axis=0), sim_state[12] + sim_state[13], sim_state[14],
            sim_state[0] + sim_state[3], sim_state[6] + sim_state[9]
        ])[:, :-1] * control

        allocation[0:3] *= params['rogue_rate'] * params['rogue_cost']
        allocation[3:7] *= params['thin_rate'] * params['thin_cost']
        allocation[7:] *= params['protect_rate'] * params['protect_cost']
        allocation[0] *= params['rel_small_cost']
        allocation[3] *= params['rel_small_cost']

        expense = utils.control_expenditure(control, params, sim_state[:, :-1])
        for j in range(len(setup['times']) - 1):
            if expense[j] > params['max_budget']:
                allocation[:, j] *= params['max_budget'] / expense[j]

        control_abs_ol[i] = (np.sum(allocation, axis=1))[order] / 200

        objectives_ol[i] = -1 * (obj - objs[-1])

        mpc_controller = mpc.Controller.load_optimisation(
            os.path.join(data_folder,
                         "div_cost_" + str(div_prop) + "_MPC.pkl"))

        sim_run, approx_run = mpc_controller.run_control()
        control = mpc_controller.control

        sim_state = np.sum(np.reshape(sim_run[0],
                                      (ncells, 15, -1)), axis=0) / ncells

        allocation = np.array([
            sim_state[1] + sim_state[4], sim_state[7] + sim_state[10],
            sim_state[13],
            np.sum(sim_state[0:6], axis=0),
            np.sum(sim_state[6:12],
                   axis=0), sim_state[12] + sim_state[13], sim_state[14],
            sim_state[0] + sim_state[3], sim_state[6] + sim_state[9]
        ])[:, :-1] * control

        allocation[0:3] *= params['rogue_rate'] * params['rogue_cost']
        allocation[3:7] *= params['thin_rate'] * params['thin_cost']
        allocation[7:] *= params['protect_rate'] * params['protect_cost']
        allocation[0] *= params['rel_small_cost']
        allocation[3] *= params['rel_small_cost']

        expense = utils.control_expenditure(control, params, sim_state[:, :-1])
        for j in range(len(setup['times']) - 1):
            if expense[j] > params['max_budget']:
                allocation[:, j] *= params['max_budget'] / expense[j]

        control_abs_mpc[i] = (np.sum(allocation, axis=1))[order] / 200

        objectives_mpc[i] = -1 * (sim_run[1] - sim_run[2][-1])

    # Make plot
    labels = [
        "Thin Tan (Small)", "Thin Tan (Large)", "Thin Bay", "Thin Red",
        "Rogue Tan (Small)", "Rogue Tan (Large)", "Rogue Bay",
        "Protect Tan (Small)", "Protect Tan (Large)"
    ]

    colors = visualisation.CONTROL_COLOURS

    fig = plt.figure()
    gs = gridspec.GridSpec(3,
                           2,
                           width_ratios=[2, 1],
                           height_ratios=[0.2, 3, 1],
                           top=0.95,
                           left=0.1,
                           wspace=0.3,
                           bottom=0.3)
    ax = fig.add_subplot(gs[:, 0])
    ax2 = fig.add_subplot(gs[1, 1])

    color = 'tab:red'
    ax2.set_ylabel('Large healthy tanoak')
    ax2.plot(div_props,
             objectives_ol,
             '--',
             color=color,
             label='OL tanoak objective')

    bars = []

    for i in range(9):
        b = ax.bar(div_props - 0.015,
                   control_abs_ol[:, i],
                   bottom=np.sum(control_abs_ol[:, :i], axis=1),
                   color=colors[i],
                   width=0.015,
                   alpha=0.75)
        bars.append(b)

    ax2.plot(div_props,
             objectives_mpc,
             '-',
             color=color,
             label='MPC tanoak objective')

    bars = []

    for i in range(9):
        b = ax.bar(div_props + 0.015,
                   control_abs_mpc[:, i],
                   bottom=np.sum(control_abs_mpc[:, :i], axis=1),
                   color=colors[i],
                   width=0.015,
                   alpha=0.75)
        bars.append(b)

    ax.legend(bars,
              labels,
              bbox_to_anchor=(-0.1, -0.15),
              loc="upper left",
              ncol=3,
              frameon=True)
    ax2.legend(bbox_to_anchor=(0.5, -0.3),
               loc="upper center",
               ncol=1,
               frameon=True,
               fontsize=8)
    ax.set_xlabel("Diversity benefit")
    ax.set_ylabel("Expenditure")
    ax2.set_xlabel("Diversity benefit")

    x_ticks = np.array([0.0, 0.25, 0.5, 0.75, 1.0])
    offset = 0.025
    ax.set_xticks(x_ticks)
    ax2.set_xticks(x_ticks)
    ax.tick_params(axis='x',
                   direction='out',
                   pad=7,
                   length=5,
                   color='darkgray')
    ax2.tick_params(axis='x',
                    direction='out',
                    pad=7,
                    length=5,
                    color='darkgray')

    data_to_axis = ax.transData + ax.transAxes.inverted()
    for x_tick in x_ticks:
        ax.text(data_to_axis.transform((x_tick - offset, 0))[0],
                -0.025,
                'OL',
                ha='center',
                transform=ax.transAxes,
                fontsize=5)
        ax.text(data_to_axis.transform((x_tick + (offset / 2), 0))[0],
                -0.025,
                'MPC',
                ha='left',
                transform=ax.transAxes,
                fontsize=5)

    ax.set_ylim([0, 15])
    fig.text(0.01,
             0.95,
             "(a)",
             transform=fig.transFigure,
             fontsize=11,
             fontweight="semibold")
    fig.text(0.57,
             0.95,
             "(b)",
             transform=fig.transFigure,
             fontsize=11,
             fontweight="semibold")

    fig.savefig(os.path.join(fig_folder, "DivCostScan.pdf"), dpi=300)
Beispiel #12
0
def run_scan(datapath):
    """Scan over control scaling factor."""

    factors = np.arange(0.05, 1.11, 0.01)

    diff_results = np.zeros_like(factors)

    # Run simulations using open-loop control policy
    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A,
        extra_spread=True)

    approx_model = ms_approx.MixedStandApprox.load_optimisation_class(
        os.path.join('data', 'ol_mpc_control', 'ol_control.pkl'))
    ol_control = interp1d(setup['times'][:-1],
                          approx_model.optimisation['control'],
                          kind="zero",
                          fill_value="extrapolate")

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    ncells = np.product(setup['landscape_dims'])

    model = ms_sim.MixedStandSimulator(setup, params)

    sim_run = model.run_policy(ol_control)
    sim_state = np.sum(sim_run[0].reshape((ncells, 15, -1)), axis=0) / ncells
    sim_tans = np.sum(sim_state[[6, 8, 9, 11], -1])

    logging.info("Sim run, healthy tans: %f", sim_tans)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])

    approx_model = ms_approx.MixedStandApprox(setup, params, beta)

    def min_func(factor):
        """Function to minimise, SSE between healthy tanoak using OL control."""

        approx_model.params['rogue_rate'] = factor * params['rogue_rate']

        approx_run = approx_model.run_policy(ol_control)

        approx_tans = np.sum(approx_run[0][[6, 8, 9, 11], -1])

        logging.info("Approx run, Factor %f, tans: %f", factor, approx_tans)

        return np.sum(np.square(approx_tans - sim_tans))

    for i, factor in enumerate(factors):
        diff = min_func(factor)
        diff_results[i] = diff

    csv_file = os.path.join(datapath, 'scan_control.csv')
    with open(csv_file, 'w', newline='') as csvfile:
        spamwriter = csv.writer(csvfile,
                                delimiter=',',
                                quotechar='|',
                                quoting=csv.QUOTE_MINIMAL)
        spamwriter.writerow(['ControlScalingFactor', 'SSE'])
        for i, factor in enumerate(factors):
            spamwriter.writerow([factor, diff_results[i]])
Beispiel #13
0
def scale_control(datapath):
    """Parameterise roguing control in approximate model to match simulations."""

    # First run simulation using open-loop policy
    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A,
        extra_spread=True)

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)

    approx_model = ms_approx.MixedStandApprox.load_optimisation_class(
        os.path.join('data', 'ol_mpc_control', 'ol_control.pkl'))
    ol_control = interp1d(setup['times'][:-1],
                          approx_model.optimisation['control'],
                          kind="zero",
                          fill_value="extrapolate")

    ncells = np.product(setup['landscape_dims'])

    model = ms_sim.MixedStandSimulator(setup, params)

    sim_run = model.run_policy(ol_control)
    sim_state = np.sum(sim_run[0].reshape((ncells, 15, -1)), axis=0) / ncells
    sim_tans = np.sum(sim_state[[6, 8, 9, 11], -1])

    logging.info("Sim run, healthy tans: %f", sim_tans)

    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])

    approx_model = ms_approx.MixedStandApprox(setup, params, beta)

    def min_func(factor):
        """Function to minimise, SSE between healthy tanoak over range of rates."""

        approx_model.params['rogue_rate'] = factor * params['rogue_rate']

        approx_run = approx_model.run_policy(ol_control)

        approx_tans = np.sum(approx_run[0][[6, 8, 9, 11], -1])

        logging.info("Approx run, Factor %f, tans: %f, rate: %f", factor,
                     approx_tans, factor * params['rogue_rate'])

        return abs(approx_tans - sim_tans)

    ret = minimize(min_func, [0.25], bounds=[(0, 2)])

    logging.info(ret)

    # Write scaling results to file
    results = {'roguing_factor': ret.x[0], 'tan_diff': ret.fun}
    with open(os.path.join(datapath, 'scaling_results.json'), "w") as outfile:
        json.dump(results, outfile, indent=4)
Beispiel #14
0
def make_data(n_reps=10, folder=None, append=False):
    """Generate data analysing effect of sampling effort."""

    if folder is None:
        folder = os.path.join(os.path.realpath(__file__), '..', '..', 'data',
                              'obs_uncert')

    setup, params = utils.get_setup_params(
        parameters.CORRECTED_PARAMS,
        scale_inf=True,
        host_props=parameters.COBB_PROP_FIG4A)

    ncells = np.product(setup['landscape_dims'])

    # Use population size of 500 as 500m2 per cell
    pop_size = 500
    sampling_nums = np.array(
        [1, 2, 3, 5, 7, 10, 15, 25, 35, 50, 70, 100, 150, 250, 350, 500])

    with open(os.path.join("data", "scale_and_fit_results.json"),
              "r") as infile:
        scale_and_fit_results = json.load(infile)
    beta_names = [
        'beta_1,1', 'beta_1,2', 'beta_1,3', 'beta_1,4', 'beta_12', 'beta_21',
        'beta_2'
    ]
    beta = np.array([scale_and_fit_results[x] for x in beta_names])

    approx_params = copy.deepcopy(params)
    approx_params['rogue_rate'] *= scale_and_fit_results['roguing_factor']
    approx_params['rogue_cost'] /= scale_and_fit_results['roguing_factor']

    mpc_args = {
        'horizon': 100,
        'time_step': 0.5,
        'end_time': 100,
        'update_period': 20,
        'rolling_horz': False,
        'stage_len': 5,
        'use_init_first': True
    }

    approx_model = ms_approx.MixedStandApprox(setup, approx_params, beta)
    _, baseline_ol_control, _ = approx_model.optimise(n_stages=20,
                                                      init_policy=even_policy)
    mpc_args['init_policy'] = baseline_ol_control

    for n_samples in sampling_nums:
        logging.info("Running with %d/%d stems sampled, %f%%", n_samples,
                     pop_size, 100 * n_samples / pop_size)
        observer = observer_factory(pop_size, n_samples)

        mpc_controls = np.zeros((n_reps, 9, len(setup['times']) - 1))
        mpc_objs = np.zeros(n_reps)

        # For storing observed states:
        mpc_approx_states = np.zeros(
            (n_reps, 4, 15))  # 4 as 4 update steps excluding the start
        mpc_actual_states = np.zeros((n_reps, 4, 15))

        # MPC runs - approximate model initialised correctly, & then observed at update steps
        for i in range(n_reps):

            mpc_controller = mpc.Controller(setup,
                                            params,
                                            beta,
                                            approx_params=approx_params)
            _, _, mpc_control, mpc_obj = mpc_controller.optimise(
                **mpc_args, observer=observer)

            mpc_objs[i] = mpc_obj
            mpc_controls[i] = mpc_control
            mpc_approx_states[i] = mpc_controller.approx_update_states

            sim_run, approx_run = mpc_controller.run_control()
            sim_state = np.sum(np.reshape(sim_run[0], (ncells, 15, -1)),
                               axis=0) / ncells
            mpc_actual_states[i] = sim_state[:, [40, 80, 120, 160]].T

            logging.info("MPC run %d of %d done", i + 1, n_reps)

        # Append to existing data
        if append:
            old_filename = os.path.join(
                folder, "sampled_data_" + str(n_samples) + ".npz")

            with np.load(old_filename) as old_dataset:
                mpc_controls = np.append(old_dataset['mpc_controls'],
                                         mpc_controls,
                                         axis=0)
                mpc_objs = np.append(old_dataset['mpc_objs'], mpc_objs, axis=0)
                mpc_approx_states = np.append(old_dataset['mpc_approx_states'],
                                              mpc_approx_states,
                                              axis=0)

        # Store data
        dataset = {
            'mpc_controls': mpc_controls,
            'mpc_objs': mpc_objs,
            'mpc_approx_states': mpc_approx_states,
            'mpc_actual_states': mpc_actual_states
        }
        np.savez_compressed(
            os.path.join(folder, "sampled_data_" + str(n_samples)), **dataset)