diff --git a/sampo/scheduler/genetic/base.py b/sampo/scheduler/genetic/base.py index 3e2a2346..6487e73b 100644 --- a/sampo/scheduler/genetic/base.py +++ b/sampo/scheduler/genetic/base.py @@ -133,10 +133,10 @@ def set_weights(self, weights: list[int]): @classmethod def generate_first_population(self, wg: WorkGraph, contractors: list[Contractor], landscape: LandscapeConfiguration = LandscapeConfiguration(), - work_estimator: WorkTimeEstimator = None, spec: ScheduleSpec = ScheduleSpec(), + work_estimator: WorkTimeEstimator = None, deadline: Time = None, - weights = None): + weights=None): """ Algorithm, that generate first population @@ -164,8 +164,8 @@ def init_k_schedule(scheduler_class, k): def init_schedule(scheduler_class): try: return scheduler_class(work_estimator=work_estimator).schedule(wg, contractors, - landscape=landscape), \ - list(reversed(prioritization(wg, work_estimator))), spec + landscape=landscape), \ + list(reversed(prioritization(wg, work_estimator))), spec except NoSufficientContractorError: return None, None, None @@ -174,21 +174,20 @@ def init_schedule(scheduler_class): try: (schedule, _, _, _), modified_spec = AverageBinarySearchResourceOptimizingScheduler( scheduler_class(work_estimator=work_estimator) - ).schedule_with_cache(wg, contractors, deadline, spec, landscape=landscape)[0] + ).schedule_with_cache(wg, contractors, deadline, spec, landscape=landscape) return schedule, list(reversed(prioritization(wg, work_estimator))), modified_spec except NoSufficientContractorError: return None, None, None return { - "heft_end": (*init_schedule(HEFTScheduler), weights[0]), - "heft_between": (*init_schedule(HEFTBetweenScheduler), weights[1]), - "12.5%": (*init_k_schedule(HEFTScheduler, 8), weights[2]), - "25%": (*init_k_schedule(HEFTScheduler, 4), weights[3]), - "75%": (*init_k_schedule(HEFTScheduler, 4 / 3), weights[4]), - "87.5%": (*init_k_schedule(HEFTScheduler, 8 / 7), weights[5]) + "heft_end": (*init_schedule(HEFTScheduler), weights[0]), + "heft_between": (*init_schedule(HEFTBetweenScheduler), weights[1]), + "12.5%": (*init_k_schedule(HEFTScheduler, 8), weights[2]), + "25%": (*init_k_schedule(HEFTScheduler, 4), weights[3]), + "75%": (*init_k_schedule(HEFTScheduler, 4 / 3), weights[4]), + "87.5%": (*init_k_schedule(HEFTScheduler, 8 / 7), weights[5]) } - def schedule_with_cache(self, wg: WorkGraph, contractors: list[Contractor], @@ -211,8 +210,8 @@ def schedule_with_cache(self, :param timeline: :return: """ - init_schedules = GeneticScheduler.generate_first_population(wg, contractors, landscape, self.work_estimator, - self._deadline, self._weights) + init_schedules = GeneticScheduler.generate_first_population(wg, contractors, landscape, spec, + self.work_estimator, self._deadline, self._weights) size_selection, mutate_order, mutate_resources, size_of_population = self.get_params(wg.vertex_count) worker_pool = get_worker_contractor_pool(contractors) diff --git a/sampo/scheduler/genetic/operators.py b/sampo/scheduler/genetic/operators.py index 2322cc8b..219d0627 100644 --- a/sampo/scheduler/genetic/operators.py +++ b/sampo/scheduler/genetic/operators.py @@ -161,7 +161,7 @@ def init_toolbox(wg: WorkGraph, # create from generate_chromosome function one individual toolbox.register('individual', tools.initRepeat, Individual, toolbox.generate_chromosome, n=1) # create population from individuals - toolbox.register('population', generate_population, wg=wg, contractors=contractors, + toolbox.register('population', generate_population, wg=wg, contractors=contractors, spec=spec, work_id2index=work_id2index, worker_name2index=worker_name2index, contractor2index=contractor2index, contractor_borders=contractor_borders, init_chromosomes=init_chromosomes, rand=rand, work_estimator=work_estimator, landscape=landscape) @@ -218,11 +218,12 @@ def ind_getter(): def generate_population(size_population: int, wg: WorkGraph, contractors: list[Contractor], + spec: ScheduleSpec, work_id2index: dict[str, int], worker_name2index: dict[str, int], contractor2index: dict[str, int], contractor_borders: np.ndarray, - init_chromosomes: dict[str, tuple[ChromosomeType, float]], + init_chromosomes: dict[str, tuple[ChromosomeType, float, ScheduleSpec]], rand: random.Random, work_estimator: WorkTimeEstimator = None, landscape: LandscapeConfiguration = LandscapeConfiguration()) -> list[ChromosomeType]: @@ -233,15 +234,15 @@ def generate_population(size_population: int, def randomized_init() -> ChromosomeType: schedule = RandomizedTopologicalScheduler(work_estimator, int(rand.random() * 1000000)) \ - .schedule(wg, contractors, landscape=landscape) + .schedule(wg, contractors, spec, landscape=landscape) return convert_schedule_to_chromosome(wg, work_id2index, worker_name2index, - contractor2index, contractor_borders, schedule) + contractor2index, contractor_borders, schedule, spec) # chromosome types' weights # these numbers are the probability weights: prob = norm(weights), sum(prob) = 1 weights = [2, 2, 1, 1, 1, 1, 2] - for i, (_, importance) in enumerate(init_chromosomes.values()): + for i, (_, importance, _) in enumerate(init_chromosomes.values()): weights[i] = int(weights[i] * importance) weights_multiplier = math.ceil(size_population / sum(weights)) @@ -260,6 +261,7 @@ def randomized_init() -> ChromosomeType: return [wrap(chromosome) for chromosome in chromosomes] + def generate_chromosome(wg: WorkGraph, contractors: list[Contractor], work_id2index: dict[str, int], diff --git a/sampo/scheduler/genetic/schedule_builder.py b/sampo/scheduler/genetic/schedule_builder.py index de563f17..744c6ff2 100644 --- a/sampo/scheduler/genetic/schedule_builder.py +++ b/sampo/scheduler/genetic/schedule_builder.py @@ -13,7 +13,7 @@ from sampo.scheduler.genetic.converter import convert_schedule_to_chromosome from sampo.scheduler.genetic.operators import init_toolbox, ChromosomeType, Individual, copy_chromosome, \ - FitnessFunction, TimeFitness, is_chromosome_correct, wrap + FitnessFunction, TimeFitness, is_chromosome_correct from sampo.scheduler.native_wrapper import NativeWrapper from sampo.scheduler.timeline.base import Timeline from sampo.schemas.contractor import Contractor, WorkerContractorPool @@ -33,7 +33,7 @@ def create_toolbox(wg: WorkGraph, selection_size: int, mutate_order: float, mutate_resources: float, - init_schedules: dict[str, tuple[Schedule, list[GraphNode] | None, int]], + init_schedules: dict[str, tuple[Schedule, list[GraphNode] | None, ScheduleSpec, int]], rand: random.Random, spec: ScheduleSpec = ScheduleSpec(), work_estimator: WorkTimeEstimator = None, @@ -92,9 +92,10 @@ def create_toolbox(wg: WorkGraph, init_chromosomes: dict[str, tuple[ChromosomeType, int]] = \ {name: (convert_schedule_to_chromosome(wg, work_id2index, worker_name2index, - contractor2index, contractor_borders, schedule, order), importance) - if schedule is not None else None - for name, (schedule, order, importance) in init_schedules.items()} + contractor2index, contractor_borders, schedule, chromosome_spec, order), + importance, chromosome_spec) + if schedule is not None else None + for name, (schedule, order, chromosome_spec, importance) in init_schedules.items()} for name, chromosome in init_chromosomes.items(): if chromosome is not None: @@ -127,6 +128,7 @@ def create_toolbox(wg: WorkGraph, Time(0), work_estimator), resources_border + def build_schedule(wg: WorkGraph, contractors: list[Contractor], worker_pool: WorkerContractorPool, @@ -135,12 +137,12 @@ def build_schedule(wg: WorkGraph, selection_size: int, mutate_order: float, mutate_resources: float, - init_schedules: dict[str, tuple[Schedule, list[GraphNode] | None, int, ScheduleSpec]], + init_schedules: dict[str, tuple[Schedule, list[GraphNode] | None, ScheduleSpec, int]], rand: random.Random, spec: ScheduleSpec, landscape: LandscapeConfiguration = LandscapeConfiguration(), fitness_constructor: Callable[[Callable[[list[ChromosomeType]], list[int]]], - FitnessFunction] = TimeFitness, + FitnessFunction] = TimeFitness, work_estimator: WorkTimeEstimator = DefaultWorkEstimator(), show_fitness_graph: bool = False, n_cpu: int = 1, diff --git a/tests/conftest.py b/tests/conftest.py index 52f13533..3e09660e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,10 +10,6 @@ from sampo.scheduler.base import SchedulerType from sampo.scheduler.generate import generate_schedule from sampo.scheduler.genetic.base import GeneticScheduler -from sampo.scheduler.heft.base import HEFTBetweenScheduler -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.scheduler.resource.average_req import AverageReqResourceOptimizer -from sampo.scheduler.resource.full_scan import FullScanResourceOptimizer from sampo.schemas.contractor import Contractor from sampo.schemas.exceptions import NoSufficientContractorError from sampo.schemas.graph import WorkGraph, EdgeType @@ -196,7 +192,8 @@ def setup_default_schedules(setup_scheduler_parameters): setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters return setup_scheduler_parameters, GeneticScheduler.generate_first_population(setup_wg, setup_contractors, - setup_landscape, work_estimator) + setup_landscape, + work_estimator=work_estimator) @fixture(scope='session', diff --git a/tests/utils/validation_test.py b/tests/utils/validation_test.py index b90f6b0d..4b72f442 100644 --- a/tests/utils/validation_test.py +++ b/tests/utils/validation_test.py @@ -28,7 +28,7 @@ def is_resources_break(self) -> bool: def test_check_order_validity_right(setup_default_schedules): (setup_wg, _, _), setup_default_schedules = setup_default_schedules - for scheduler, (schedule, _, _) in setup_default_schedules.items(): + for scheduler, (schedule, _, _, _) in setup_default_schedules.items(): try: _check_all_tasks_scheduled(schedule, setup_wg) _check_parent_dependencies(schedule, setup_wg) @@ -39,7 +39,7 @@ def test_check_order_validity_right(setup_default_schedules): def test_check_order_validity_wrong(setup_default_schedules): (setup_wg, _, _), setup_default_schedules = setup_default_schedules - for (schedule, _, _) in setup_default_schedules.values(): + for (schedule, _, _, _) in setup_default_schedules.values(): for break_type in BreakType: if break_type.is_order_break(): broken = break_schedule(break_type, schedule, setup_wg) @@ -56,7 +56,7 @@ def test_check_order_validity_wrong(setup_default_schedules): def test_check_resources_validity_right(setup_default_schedules): (setup_wg, setup_contractors, _), setup_default_schedules = setup_default_schedules - for scheduler, (schedule, _, _) in setup_default_schedules.items(): + for scheduler, (schedule, _, _, _) in setup_default_schedules.items(): try: _check_all_workers_correspond_to_worker_reqs(schedule) _check_all_allocated_workers_do_not_exceed_capacity_of_contractors(schedule, setup_contractors) @@ -68,7 +68,7 @@ def test_check_resources_validity_wrong(setup_default_schedules): (setup_wg, setup_contractors, _), setup_default_schedules = setup_default_schedules setup_worker_pool = get_worker_contractor_pool(setup_contractors) - for (schedule, _, _) in setup_default_schedules.values(): + for (schedule, _, _, _) in setup_default_schedules.values(): for break_type in BreakType: if break_type.is_resources_break(): broken = break_schedule(break_type, schedule, setup_wg, setup_worker_pool)