Exemple #1
0
    def test__index_linear_reinsertion_nsga_constraints(self):
        """
        1) Compares selection to a manually calculated solution.
        2) Tests the selection of individuals for crossover to verify whether the mean of the number of violations is t1<=t0. If the lowest violations individuals are indeed being selected.
        """
        print("_index_linear_reinsertion_nsga_constraints")

        data = np.loadtxt(
            os.path.join(
                self.path_data,
                "_index_linear_reinsertion_nsga_constraints_data.csv"),
            delimiter=",",
            skiprows=1,
        )  # violations	crowd_dist	fronts
        index = np.loadtxt(
            os.path.join(
                self.path_data,
                "_index_linear_reinsertion_nsga_constraints_index.csv"),
            delimiter=",",
            skiprows=1,
            dtype=np.int32,
        )
        ix_sel = AlgNsga2._index_linear_reinsertion_nsga_constraints(
            data[:, 0], data[:, 1], data[:, 2], 100)
        self.assertTrue(len(set(ix_sel) - set(index)) == 0)

        # Test with second data
        data = np.loadtxt(
            os.path.join(
                self.path_data,
                "_index_linear_reinsertion_nsga_constraints_data_2.csv"),
            delimiter=",",
            skiprows=1,
        )  # violations	crowd_dist	fronts
        index = np.loadtxt(
            os.path.join(
                self.path_data,
                "_index_linear_reinsertion_nsga_constraints_index_2.csv"),
            delimiter=",",
            skiprows=1,
            dtype=np.int32,
        )  # All indexes in the array are acceptable
        ix_sel = AlgNsga2._index_linear_reinsertion_nsga_constraints(
            data[:, 0], data[:, 1], data[:, 2], 100)
        self.assertTrue(len(set(ix_sel) - set(index)) == 0)

        # Test 2 Verify if median is decreasing

        n_tests = 2000  # Number of tests
        n_ind = 200
        for i in range(n_tests):
            violations = np.random.randint(0, n_ind / 20, size=n_ind)
            mean_violations = np.mean(violations)
            crowd_dist = np.random.randint(0, 100, size=n_ind)
            fronts = np.random.randint(0, 3, size=n_ind)
            ix_sel = AlgNsga2._index_linear_reinsertion_nsga_constraints(
                violations, crowd_dist, fronts,
                len(fronts) // 2)
            self.assertLess(np.mean(violations[ix_sel]), mean_violations)
Exemple #2
0
    def test_select_pop_by_index(self):
        """Tests if:
        1)selection of pop by index is indeed reducing population violations data in n_tests.
        2)Verify invalid number of batches
        3)Verify invalid value of active genes
        """
        # Number of genes
        num_genes = int(25)
        # Number of products
        num_products = int(4)
        # Number of Objectives
        num_objectives = 2
        # Start date of manufacturing
        start_date = datetime.date(2016, 12, 1)  # YYYY-MM-DD.
        qc_max_months = 4  # Max number of months
        # Number of Months
        num_months = 36
        num_fronts = 3  # Number of fronts created

        myPlan = Planning(
            num_genes,
            num_products,
            num_objectives,
            start_date,
            qc_max_months,
            num_months,
            num_fronts,
        )

        n_tests = 100  # Number of tests
        for i in range(n_tests):
            pop = load_obj(
                os.path.join(self.path_data, "select_pop_by_index_pop.pkl"))
            violations_copy = np.copy(pop.backlogs[:, 6])
            crowding_dist_copy = np.copy(pop.crowding_dist)
            fronts_copy = np.copy(pop.fronts)

            mean_violations = np.mean(violations_copy)

            ix_sel = AlgNsga2._index_linear_reinsertion_nsga_constraints(
                violations_copy, crowding_dist_copy, fronts_copy,
                len(fronts_copy) //
                2)  # Input Violations,Crowding Distance, Fronts
            self.assertLess(
                np.mean(violations_copy[ix_sel]), mean_violations
            )  # Verifies if selected indexes indeed reduce violations mean
            myPlan.select_pop_by_index(pop, ix_sel)
            self.assertLess(
                np.mean(pop.backlogs[:, 6]), mean_violations
            )  # Verifies if selected individuals indeed reduce violations mean
            for i in range(0, len(pop.products_raw)):
                self.assertFalse(
                    any(pop.batches_raw[i][pop.masks[i]] ==
                        0))  # Verify invalid number of batches (0)
                self.assertFalse(
                    np.sum(pop.masks[i][pop.genes_per_chromo[i]:]) > 0
                )  # Verify invalid value of active genes (If true Invalid bool after number of active genes.)
Exemple #3
0
    def test__fronts(self):
        """Tests _fronts, that classifies solutions(Each row is a solution with different objectives values) into pareto fronts.
        1) Tests using data manually classified to 3 fronts.
        2) Tests random values to verify if fronts are within expected range
        """
        print("_fronts")
        # Test 1
        num_fronts = 3
        objectives_fn = np.loadtxt(
            os.path.join(self.path_data, "_fronts.csv"),
            delimiter=",",
            skiprows=1
        )  # Index(['f0', 'f1', 'f2', 'f3', 'front'], dtype='object')
        front_calc = AlgNsga2._fronts(objectives_fn[:, 0:3], num_fronts)
        self.assertEqual(front_calc.all(), objectives_fn[:, 4].all())
        # Test 2
        # Matrix of ones
        objectives_fn = np.ones(shape=(100,
                                       4))  # Index(['f0', 'f1', 'f2', 'f3'])
        front_calc = AlgNsga2._fronts(objectives_fn, num_fronts)
        self.assertFalse((any(front_calc >= num_fronts))
                         | (any(front_calc < 0)))
        self.assertEqual(np.unique(front_calc)[0], 0)

        # Matrix of zeros
        objectives_fn = np.zeros(shape=(100,
                                        4))  # Index(['f0', 'f1', 'f2', 'f3'])
        front_calc = AlgNsga2._fronts(objectives_fn, num_fronts)
        self.assertFalse((any(front_calc >= num_fronts))
                         | (any(front_calc < 0)))
        self.assertEqual(np.unique(front_calc)[0], 0)

        # Matrix of random
        num_tests = 10
        for i in range(num_tests):
            objectives_fn = np.random.randint(0, 100, size=400).reshape(
                -1, 4)  # Index(['f0', 'f1', 'f2', 'f3'])
            front_calc = AlgNsga2._fronts(objectives_fn, num_fronts)
            self.assertFalse((any(front_calc >= num_fronts))
                             | (any(front_calc < 0)))
Exemple #4
0
 def test__crowding_distance(self):
     """Tests evaluation of crowding distance for a manually calculated crowding distance."""
     print("_crowding_distance")
     big_dummy = 10**5
     objectives_fn = np.loadtxt(
         os.path.join(self.path_data, "_crowding_distance.csv"),
         delimiter=",",
         skiprows=1
     )  # Index(['f0', 'f1', 'f2', 'f3', 'front','dcrowd'], dtype='object')
     crowding_dist = AlgNsga2._crowding_distance(objectives_fn[:, 0:4],
                                                 objectives_fn[:, 4],
                                                 big_dummy)
     delta = 0.0001
     message = "First and second are not almost equal."  # error message in case if test case got failed
     self.assertAlmostEqual(crowding_dist.all(), objectives_fn[:, 5].all(),
                            None, message, delta)
Exemple #5
0
    def main(self, num_exec, num_chromossomes, num_geracoes, n_tour,
             perc_crossover, pmut):
        print("START Exec number:", num_exec)
        t0 = perf_counter()
        # 1) Random parent population is initialized with its attributes
        pop = population.Population(
            self.num_genes,
            num_chromossomes,
            self.num_products,
            self.num_objectives,
            self.start_date,
            self.qc_max_months,
            self.num_months,
        )
        # 1.1) Initializes class object for Offspring Population
        # Number of chromossomes for crossover, guarantees an even number
        n_parents = int(num_chromossomes * perc_crossover)
        if n_parents % 2 == 1:
            n_parents = n_parents + 1
        pop_offspring = population.Population(
            self.num_genes,
            n_parents,
            self.num_products,
            self.num_objectives,
            self.start_date,
            self.qc_max_months,
            self.num_months,
        )
        # 1.2) Creates start and end date from schedule assures only batches with End date<Last day of manufacturing

        # 2) Is calculated along Step 1, Note that USP end dates are calculated, but not stored.
        pop = self.calc_start_end(pop)

        # 3)Calculate inventory levels and objectives
        pop = self.calc_inventory_objectives(pop)

        # 4)Front Classification
        objectives_raw_copy = pop.objectives_raw.copy()
        pop.fronts = AlgNsga2._fronts(objectives_raw_copy, self.num_fronts)

        # 5) Crowding Distance
        objectives_raw_copy = pop.objectives_raw.copy()
        fronts_copy = pop.fronts.copy()
        pop.crowding_dist = AlgNsga2._crowding_distance(
            objectives_raw_copy, fronts_copy, self.big_dummy)
        for i_gen in range(0, num_geracoes):
            # print("Generation ", i_gen)

            # 6)Selection for Crossover Tournament
            backlogs_copy = pop.backlogs[:, 6].copy()
            fronts_copy = pop.fronts.copy()
            crowding_dist_copy = pop.crowding_dist.copy()
            ix_to_crossover = self.tournament_restrictions(
                fronts_copy, crowding_dist_copy, n_parents, n_tour,
                backlogs_copy)

            # 7)Crossover
            # 7.1 Sorts Selected by number of genes
            genes_per_chromo_copy = pop.genes_per_chromo.copy()
            ix_to_crossover = ix_to_crossover[np.argsort(
                genes_per_chromo_copy[ix_to_crossover])]
            # 7.2 Creates a new population for offspring population crossover and calls uniform crossover
            products_raw_copy = pop.products_raw.copy()
            batches_raw_copy = pop.batches_raw.copy()
            masks_copy = pop.masks.copy()
            new_products, new_batches, new_mask = Crossovers._crossover_uniform(
                products_raw_copy[ix_to_crossover],
                batches_raw_copy[ix_to_crossover],
                masks_copy[ix_to_crossover],
                perc_crossover,
            )

            # 8)Mutation
            new_products, new_batches, new_mask = self.mutation_processes(
                new_products, new_batches, new_mask, pmut)

            # 9)Aggregate batches with same product neighbours
            new_products, new_batches, new_mask = self.fix_aggregation_batches(
                new_products, new_batches, new_mask)

            # 10) Merge populations Current and Offspring
            pop_offspring.update_new_population(new_products, new_batches,
                                                new_mask)

            # 11) 2) Is calculated along Step 1, Note that USP end dates are calculated, but not stored.
            pop_offspring = self.calc_start_end(pop_offspring)

            # 12) 3)Calculate inventory levels and objectives
            pop_offspring = self.calc_inventory_objectives(pop_offspring)
            # 13) Merge Current Pop with Offspring
            pop = Planning.merge_pop_with_offspring(pop, pop_offspring)

            # 14) 4)Front Classification
            objectives_raw_copy = pop.objectives_raw.copy()
            pop.fronts = AlgNsga2._fronts(objectives_raw_copy, self.num_fronts)

            # 15) 5) Crowding Distance
            objectives_copy = pop.objectives_raw.copy()
            fronts_copy = pop.fronts.copy()
            pop.crowding_dist = AlgNsga2._crowding_distance(
                objectives_copy, fronts_copy, self.big_dummy)

            # 16) Linear Reinsertion

            # 16.1) Selects indexes to maintain
            # Calculates number of violated constraints
            backlogs_copy = np.copy(pop.backlogs[:, 6])
            crowding_dist_copy = np.copy(pop.crowding_dist)
            fronts_copy = np.copy(pop.fronts)
            ix_reinsert = AlgNsga2._index_linear_reinsertion_nsga_constraints(
                backlogs_copy,
                crowding_dist_copy,
                fronts_copy,
                num_chromossomes,
            )

            # 16.2) Remove non reinserted chromossomes from pop
            ix_reinsert_copy = np.copy(ix_reinsert)
            self.select_pop_by_index(pop, ix_reinsert_copy)
        t1 = perf_counter()
        print("Exec", num_exec, "Time", t1 - t0)
        # gc.collect()
        return pop
Exemple #6
0
def run_parallel(numExec, numGenerations, maxWorkers):
    """Run GA using Multiprocessing.

    Args:
        numExec (int): Number of executions
        numGenerations (int): Number of generations
        maxWorkers (int): Number of workers
    """

    # Parameters
    n_exec_ite = range(0, numExec)

    # Number of genes
    num_genes = int(25)
    # Number of products
    num_products = int(4)
    # Number of Objectives
    num_objectives = 2
    # Start date of manufacturing
    start_date = datetime.date(2016, 12, 1)  # YYYY-MM-DD.
    qc_max_months = 4  # Max number of months
    # Number of Months
    num_months = 36
    num_fronts = 3  # Number of fronts created

    # Inversion val to convert maximization of throughput to minimization, using a value a little bit higher than the article max 630.4
    ref_point = [2500, 2500]
    volume_max = np.prod(ref_point)  # Maximum Volume

    # Variables
    # Variant
    var = "front_nsga,tour_vio,rein_vio,vio_back,calc_montecarlo"

    # Number of Chromossomes
    nc = [100]
    ng = [numGenerations]  # Number of Generations

    # Number of tour
    nt = [2]
    # Crossover Probability
    pcross = [0.5]
    # Parameters for the mutation operator (pmutp,pposb,pnegb,pswap)
    pmut = [(0.04, 0.61, 0.77, 0.47)]

    output_data_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "data/output_raw/"))
    if os.path.exists(output_data_path) == False:
        raise Exception(f"Could not find the path {output_data_path}, please modify the path.")

    # List of variants
    list_vars = list(product(*[nc, ng, nt, pcross, pmut]))

    # Lists store results
    result_execs = []
    result_ids = []
    times = []
    for v_i in list_vars:
        name_var = f"{var},{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}"
        # Creates a dummy pop with one chromossome to concatenate results
        pop_main = population.Population(
            num_genes,
            1,
            num_products,
            num_objectives,
            start_date,
            qc_max_months,
            num_months,
        )
        pop_main.name_variation = name_var
        file_name = f"pop_{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}.pkl"
        export_obj(pop_main, os.path.join(output_data_path, file_name))
        del pop_main  # 1)Is it a best practice delete an object after exporting and then load, export and del again?

        t0 = perf_counter()
        with concurrent.futures.ProcessPoolExecutor(max_workers=maxWorkers) as executor:
            for pop_exec in executor.map(
                planning.Planning(
                    num_genes,
                    num_products,
                    num_objectives,
                    start_date,
                    qc_max_months,
                    num_months,
                    num_fronts,
                ).main,
                n_exec_ite,
                [v_i[0]] * numExec,
                [v_i[1]] * numExec,
                [v_i[2]] * numExec,
                [v_i[3]] * numExec,
                [v_i[4]] * numExec,
            ):
                pop_main = load_obj(os.path.join(output_data_path, file_name))
                print("In merge num_chromossomes", pop_main.num_chromossomes)
                pop_main = planning.Planning.merge_pop_with_offspring(pop_main, pop_exec)
                print("Out merge num_chromossomes", pop_main.num_chromossomes)

                export_obj(pop_main, os.path.join(output_data_path, file_name))
                del pop_main
                del pop_exec
                gc.collect()

        pop_main = load_obj(os.path.join(output_data_path, file_name))
        # Removes the first dummy one chromossome
        print("Final Num Chromossomes", pop_main.fronts.shape)
        planning.Planning.select_pop_by_index(pop_main, np.arange(1, pop_main.num_chromossomes))
        # Front Classification
        pop_main.fronts = AlgNsga2._fronts(pop_main.objectives_raw, num_fronts)
        print("fronts out", pop_main.fronts)
        # Select only front 0 with no violations or front 0
        ix_vio = np.where(pop_main.backlogs[:, 6] == 0)[0]
        ix_par = np.where(pop_main.fronts == 0)[0]
        ix_pareto_novio = np.intersect1d(ix_vio, ix_par)
        if len(ix_pareto_novio) > 0:
            var = var + "metrics_front0_wo_vio"
            print(
                "Found Solutions without violations and in pareto front",
                len(ix_pareto_novio),
                ix_pareto_novio,
            )
        else:
            print(
                "No solution without violations and in front 0, passing all in front 0.",
                ix_pareto_novio,
            )
            var = var + "metrics_front0_w_vio"
            ix_pareto_novio = ix_par
        print("Fronts In select by index", pop_main.fronts)
        print("Backlog In select by index", pop_main.backlogs[:, 6])
        planning.Planning.select_pop_by_index(pop_main, ix_pareto_novio)
        print("Fronts out select by index", pop_main.fronts)
        print("Backlog out select by index", pop_main.backlogs[:, 6])
        print("Objectives before metrics_inversion_violations", pop_main.objectives_raw)

        # Extract Metrics

        r_exec, r_ind = pop_main.metrics_inversion_violations(
            ref_point,
            volume_max,
            num_fronts,
            0,
            name_var,
            pop_main.backlogs[:, 6],
        )

        result_execs.append(r_exec)
        result_ids.append(r_ind[0])  # X
        result_ids.append(r_ind[1])  # Y
        print("Objectives after metrics_inversion_violations", pop_main.objectives_raw)
        print("Backlog Out after metrics_inversion_violations", pop_main.backlogs[:, 6])

        file_name = f"pop_{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}.pkl"
        export_obj(pop_main, os.path.join(output_data_path, file_name))

        tf = perf_counter()
        delta_t = tf - t0
        print("Total time ", delta_t, "Per execution", delta_t / numExec)
        times.append([v_i, delta_t, delta_t / numExec])
    name_var = "v_0"
    # name_var=f"exec{numExec}_chr{nc}_ger{ng}_tour{nt}_cross{pcross}_mut{pmut}"
    # print(f"{tempo} tempo/exec{tempo/numExec}")
    # Export times
    export_list_to_csv(
        times, os.path.join(output_data_path, name_var + "_results.csv"), methodexport="a"
    )

    # Export Pickle
    export_obj(result_execs, os.path.join(output_data_path, name_var + "_exec.pkl"))
    export_obj(result_ids, os.path.join(output_data_path, name_var + "_id.pkl"))

    print("Finish")
Exemple #7
0
def process_incomplete_populations(numGenerations):
    """Used to process runs where executions were not totally completed, generates a pareto front using all populations and finishes the results exportation as in the original run.py execution.

    Args:
        numGenerations (int): Number of generations
    """

    num_fronts = 3  # Number of fronts created

    # Inversion val to convert maximization of throughput to minimization, using a value a little bit higher than the article max 630.4
    ref_point = [2500, 2500]
    volume_max = np.prod(ref_point)  # Maximum Volume

    # Variables
    # Variant
    var = "front_nsga,tour_vio,rein_vio,vio_back,calc_montecarlo"

    # Number of Chromossomes
    nc = [100]
    ng = [numGenerations]  # Number of Generations

    # Number of tour
    nt = [2]
    # Crossover Probability
    # pcross = [0.11]
    pcross = [0.5]
    # Parameters for the mutation operator (pmutp,pposb,pnegb,pswap)
    pmut = [(0.04, 0.61, 0.77, 0.47)]

    output_data_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "data/output_raw/"))
    if os.path.exists(output_data_path) == False:
        raise Exception(f"Could not find the path {output_data_path}, please modify the path.")

    # List of variants
    list_vars = list(product(*[nc, ng, nt, pcross, pmut]))

    # Lists store results
    result_execs = []
    result_ids = []
    times = []
    for v_i in list_vars:
        name_var = f"{var},{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}"
        file_name = f"pop_{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}.pkl"

        # Open Population
        pop_main = load_obj(os.path.join(output_data_path, file_name))
        # Removes the first dummy one chromossome
        print("Num Chromossomes", pop_main.fronts.shape)
        planning.Planning.select_pop_by_index(pop_main, np.arange(1, pop_main.num_chromossomes))
        # Front Classification
        pop_main.fronts = AlgNsga2._fronts(pop_main.objectives_raw, num_fronts)
        print("fronts out", pop_main.fronts)
        # Select only front 0 with no violations or front 0
        ix_vio = np.where(pop_main.backlogs[:, 6] == 0)[0]
        ix_par = np.where(pop_main.fronts == 0)[0]
        ix_pareto_novio = np.intersect1d(ix_vio, ix_par)
        if len(ix_pareto_novio) > 0:
            var = var + "metrics_front0_wo_vio"
            print(
                "Found Solutions without violations and in pareto front",
                len(ix_pareto_novio),
                ix_pareto_novio,
            )
        else:
            print(
                "No solution without violations and in front 0, passing all in front 0.",
                ix_pareto_novio,
            )
            var = var + "metrics_front0_w_vio"
            ix_pareto_novio = ix_par
        print("Fronts In select by index", pop_main.fronts)
        print("Backlog In select by index", pop_main.backlogs[:, 6])
        planning.Planning.select_pop_by_index(pop_main, ix_pareto_novio)
        print("Fronts out select by index", pop_main.fronts)
        print("Backlog out select by index", pop_main.backlogs[:, 6])
        print("Objectives before metrics_inversion_violations", pop_main.objectives_raw)

        # Extract Metrics

        r_exec, r_ind = pop_main.metrics_inversion_violations(
            ref_point,
            volume_max,
            num_fronts,
            0,
            name_var,
            pop_main.backlogs[:, 6],
        )

        result_execs.append(r_exec)
        result_ids.append(r_ind[0])  # X
        result_ids.append(r_ind[1])  # Y
        print("Objectives after metrics_inversion_violations", pop_main.objectives_raw)
        print("Backlog Out after metrics_inversion_violations", pop_main.backlogs[:, 6])

        file_name = f"pop_{v_i[0]},{v_i[1]},{v_i[2]},{v_i[3]},{v_i[4]}.pkl"
        export_obj(pop_main, os.path.join(output_data_path, file_name))

        times.append([v_i, 0, 0])
    name_var = "v_0"
    # name_var=f"exec{numExec}_chr{nc}_ger{ng}_tour{nt}_cross{pcross}_mut{pmut}"
    # print(f"{tempo} tempo/exec{tempo/numExec}")
    # Export times
    export_list_to_csv(
        times, os.path.join(output_data_path, name_var + "_results.csv"), methodexport="a"
    )

    # Export Pickle
    export_obj(result_execs, os.path.join(output_data_path, name_var + "_exec.pkl"))
    export_obj(result_ids, os.path.join(output_data_path, name_var + "_id.pkl"))

    print("Finish")