def test_repeated_evaluation(self):
        SEQUENTIAL_CONFIG = {
            "n_steps": 3,
            "cell_lines": ['DV90', 'HS695T'],
            "objective": TestObjective(),
            "max_dosage": 8000,
            "domain": UnitSimplex(7),
            "scale": "linear"
        }

        repeated_evaluator = Evaluator(SEQUENTIAL_CONFIG,
                                       self.n_envs,
                                       store=True,
                                       repeated=True)
        x = np.array([0.5, 0.5, 0, 0, 0, 0, 0])
        treats = [
            prepare_dict(x,
                         max_dosage=TEST_CONFIG["max_dosage"],
                         scale=TEST_CONFIG["scale"])
            for _ in range(self.n_steps)
        ]

        # use evaluator
        _, prolifs = repeated_evaluator.evaluate([x])  # write test to check y
        p = 1
        for i in range(self.n_steps):
            simulator = Simulator()
            simulator.initialize("HS695T")
            p *= simulator.apply_treatment(treats[i])
        print("p: ", p)
        print("prolis: ", prolifs)
        self.assertAlmostEqual(prolifs[0][1], p)
        repeated_evaluator.terminate()
class TestEvaluator(unittest.TestCase):
    def setUp(self):
        self.n_envs = 2
        self.n_steps = 3
        self.evaluator = Evaluator(TEST_CONFIG, self.n_envs, store=True)
        self.xs = [np.random.uniform(0, 1, 7) for i in range(EVALS)]
        self.xs = [x / sum(x + EPS) for x in self.xs]

    def test_evaluate(self):
        ys, prolifs = self.evaluator.evaluate(self.xs)
        ys, prolifs = self.evaluator.evaluate(self.xs)

        # compare results with direct serial execution
        for i, x in enumerate(self.xs):
            self.assertTrue(np.abs(ys[i] - np.average(prolifs[i])) < EPS)
            avg = 0
            for j, line in enumerate(TEST_CONFIG["cell_lines"]):
                treat = prepare_dict(x, max_dosage=TEST_CONFIG["max_dosage"])
                simulator = Simulator()
                simulator.initialize(line)
                r = simulator.apply_treatment(treat)
                self.assertTrue(np.abs(prolifs[i][j] - r) < EPS)
                avg += r
            avg /= len(TEST_CONFIG["cell_lines"])
            self.assertTrue(np.abs(avg - ys[i]) < EPS)

    def test_buffer(self):
        # test if things get stored in buffer correctly
        _, _ = self.evaluator.evaluate(self.xs)
        buffer_dict = self.evaluator.get_res_dict()
        self.assertEqual(
            len(buffer_dict[TEST_CONFIG["cell_lines"][0]]
                ["relative_proliferation"]), EVALS)

        # compare buffer content with direct serial execution
        for i, x in enumerate(self.xs):
            for line in TEST_CONFIG["cell_lines"]:
                treat = prepare_dict(x, max_dosage=TEST_CONFIG["max_dosage"])
                simulator = Simulator()
                simulator.initialize(line)
                prolif = simulator.apply_treatment(treat)
                self.assertTrue(
                    np.abs(prolif -
                           buffer_dict[line]["relative_proliferation"][i]) <=
                    EPS)

    def test_repeated_evaluation(self):
        SEQUENTIAL_CONFIG = {
            "n_steps": 3,
            "cell_lines": ['DV90', 'HS695T'],
            "objective": TestObjective(),
            "max_dosage": 8000,
            "domain": UnitSimplex(7),
            "scale": "linear"
        }

        repeated_evaluator = Evaluator(SEQUENTIAL_CONFIG,
                                       self.n_envs,
                                       store=True,
                                       repeated=True)
        x = np.array([0.5, 0.5, 0, 0, 0, 0, 0])
        treats = [
            prepare_dict(x,
                         max_dosage=TEST_CONFIG["max_dosage"],
                         scale=TEST_CONFIG["scale"])
            for _ in range(self.n_steps)
        ]

        # use evaluator
        _, prolifs = repeated_evaluator.evaluate([x])  # write test to check y
        p = 1
        for i in range(self.n_steps):
            simulator = Simulator()
            simulator.initialize("HS695T")
            p *= simulator.apply_treatment(treats[i])
        print("p: ", p)
        print("prolis: ", prolifs)
        self.assertAlmostEqual(prolifs[0][1], p)
        repeated_evaluator.terminate()

    def test_evaluate_without_pd(self):
        SEQUENTIAL_CONFIG = {
            "n_steps": 2,
            "cell_lines": ['DV90', 'HS695T'],
            "objective": TestObjective(),
            "max_dosage": 8000,
            "domain": UnitSimplex(7),
            "scale": "linear"
        }

        repeated_evaluator = Evaluator(SEQUENTIAL_CONFIG,
                                       self.n_envs,
                                       store=True,
                                       repeated=True,
                                       allow_pd=False)
        x = np.array([0.0, 0.5, 0.5, 0, 0, 0, 0])
        treats = [
            prepare_dict(x,
                         max_dosage=TEST_CONFIG["max_dosage"],
                         scale=TEST_CONFIG["scale"])
            for _ in range(self.n_steps)
        ]

        # use evaluator
        _, prolifs = repeated_evaluator.evaluate([x[1:]
                                                  ])  # write test to check y
        p = 1
        for i in range(SEQUENTIAL_CONFIG["n_steps"]):
            simulator = Simulator()
            simulator.initialize("HS695T")
            p *= simulator.apply_treatment(treats[i])
        print("p: ", p)
        print("prolis: ", prolifs)
        self.assertAlmostEqual(prolifs[0][1], p)
        repeated_evaluator.terminate()

    def tearDown(self):
        # performs internal check if all environments terminate
        self.evaluator.terminate()
class TestSequential(unittest.TestCase):
    """
    A test script to verify multiple function related to sequential experiments.
    """

    def setUp(self):
        self.lambd = 10 ** (-4.5)
        self.dim = DIM
        self.n_steps = N_STEPS
        self.evaluator = Evaluator(TEST_CONFIG, 10, store=False)
        self.domain = SequentialSimplex(self.dim, self.n_steps)
        self.sigma = np.eye(self.dim * self.n_steps) / self.dim
        self.objective = MultiWorstLinear(LAMBD)
        

    def test_sequential_simplex(self):
        seq_simplex = SequentialSimplex(self.dim, self.n_steps)
        vertex = np.zeros(self.dim * self.n_steps)
        for i in range(self.n_steps):
            vertex[i * self.dim] = 1
        self.assertTrue(seq_simplex.contains(np.zeros(self.dim * self.n_steps)))
        self.assertTrue(seq_simplex.contains(vertex))
        self.assertFalse(seq_simplex.contains(np.ones(self.dim * self.n_steps)))
        self.assertTrue(seq_simplex.contains(seq_simplex.center()))
        self.assertTrue(seq_simplex.contains(seq_simplex.uniform()))
        self.assertTrue(seq_simplex.contains(seq_simplex.normal(seq_simplex.center(), self.sigma)))

    def test_sequential_cube(self):
        seq_cube = SequentialCube(self.dim, self.n_steps)
        self.assertTrue(seq_cube.contains(np.zeros(self.dim * self.n_steps)))
        self.assertTrue(seq_cube.contains(np.ones(self.dim * self.n_steps)))
        self.assertTrue(seq_cube.contains(seq_cube.center()))
        self.assertTrue(seq_cube.contains(seq_cube.uniform()))
        self.assertTrue(seq_cube.contains(seq_cube.normal(seq_cube.center(), self.sigma)))

    def test_sequential_single_drug(self):
        # get single step result
        single_step_treatment = np.zeros(self.dim)
        single_step_treatment[0] = 1
        single_treat = prepare_dict(single_step_treatment, max_dosage=8000)
        simulator = Simulator()
        simulator.initialize("SKMEL24")
        sp1 = simulator.apply_treatment(single_treat)
        sp2 = simulator.apply_treatment(single_treat)
        sp3 = simulator.apply_treatment(single_treat)
        self.assertAlmostEqual(sp2, sp1 * sp1)
        self.assertAlmostEqual(sp3, sp1 ** 3)
        self.assertAlmostEqual(sp3, sp1 * sp2)

        # get multi-step result
        multi_step_treatment = np.zeros(self.dim * self.n_steps)
        for i in range(self.n_steps):
            multi_step_treatment[i * self.dim] = 1
        ys, prolifs = self.evaluator.evaluate([multi_step_treatment])

        self.assertAlmostEqual(sp1 ** 3, prolifs[0][1])
        self.assertAlmostEqual(sp1 ** 3 + LAMBD * 3 * 8000, ys[0])

    def test_sequential_evaluation(self):
        seq_simplex = SequentialSimplex(self.dim, self.n_steps)
        x = seq_simplex.uniform()
        treats = [prepare_dict(x[i * self.dim:(i + 1) * self.dim], max_dosage=TEST_CONFIG["max_dosage"], scale=TEST_CONFIG["scale"]) for i in range(self.n_steps)]

        # use evaluator
        ys, prolifs = self.evaluator.evaluate([x])
        # compare with SKMEL24 evaluation of single steps
        p = 1
        for i in range(self.n_steps):
            simulator = Simulator()
            simulator.initialize("SKMEL24")
            p *= simulator.apply_treatment(treats[i])
        self.assertAlmostEqual(prolifs[0][1], p)

    def test_sequential_cma_es(self):
        mu, obj, prolif = cma_es(self.evaluator, self.domain, MAX_ITER, verbose=True, seed=23)
        # generate total dosage
        total_dosage = sum([8000 * x for x in mu])
        self.assertTrue(np.abs(obj - (np.max(prolif) + LAMBD * total_dosage)) < EPS)
        self.assertTrue(self.domain.contains(mu))

        # compare objective with sequential computation
        mu = mu.flatten()
        treats = [prepare_dict(mu[i * self.dim:(i + 1) * self.dim], max_dosage=TEST_CONFIG["max_dosage"], scale=TEST_CONFIG["scale"]) for i in range(self.n_steps)]
        cum_treat = empty_treatment()
        prolifs = []
        for line in TEST_CONFIG["cell_lines"]:
            simulator = Simulator()
            simulator.initialize(line)
            for t in treats:
                p = simulator.apply_treatment(t)
            prolifs.append(p)
        for t in treats:
            for k in t:
                cum_treat[k] += t[k]
        o = TEST_CONFIG["objective"].eval(prolifs, cum_treat)
        self.assertTrue(np.abs(obj - o) <= EPS)

    def test_best_sequential_search_result(self):
        treatments, concentration, rel_prolif, objective = best_sequential_search_result("intestine", "./artifacts/sequential/", "2step_worst_simplex_-4_5", 2, lambd=self.lambd, obj="worst", max_dosage=8000, verification=True)
        total_concentration = 0
        for t in treatments:
            for k in t:
                total_concentration += t[k]
        self.assertAlmostEqual(total_concentration, concentration)
        ref_obj = np.max(rel_prolif) + self.lambd * concentration 
        self.assertAlmostEqual(objective, ref_obj)

    def best_interpolated_multi_search_result(self):
        treatments, concentration, rel_prolif, objective = best_sequential_search_result("intestine", "./artifacts/multi/", "worst_simplex_-4_5", 2, lambd=self.lambd, obj="worst", max_dosage=8000, verification=True)
        total_concentration = 0
        for t in treatments:
            for k in t:
                total_concentration += t[k]
        self.assertAlmostEqual(total_concentration, concentration)
        ref_obj = np.max(rel_prolif) + self.lambd * concentration 
        self.assertAlmostEqual(objective, ref_obj)

    def tearDown(self):
        # performs internal check if all environments terminate
        self.evaluator.terminate()