From bde11e3db7dfa2cbdbb04ea8533a5a57b2ef104a Mon Sep 17 00:00:00 2001 From: Timotshak Date: Tue, 28 Nov 2023 16:50:31 +0300 Subject: [PATCH] introduce LFTScheduler and RandomizedLFTScheduler with tests --- sampo/scheduler/base.py | 1 + sampo/scheduler/lft/base.py | 4 +- tests/conftest.py | 50 ++++++++------------ tests/scheduler/genetic/full_scheduling.py | 55 +--------------------- tests/scheduler/lft/__init__.py | 0 tests/scheduler/lft/fixtures.py | 18 +++++++ tests/scheduler/lft/prioritization_test.py | 21 +++++++++ tests/scheduler/lft/scheduling_test.py | 18 +++++++ 8 files changed, 80 insertions(+), 87 deletions(-) create mode 100644 tests/scheduler/lft/__init__.py create mode 100644 tests/scheduler/lft/fixtures.py create mode 100644 tests/scheduler/lft/prioritization_test.py create mode 100644 tests/scheduler/lft/scheduling_test.py diff --git a/sampo/scheduler/base.py b/sampo/scheduler/base.py index bd1b8134..2e7474f0 100644 --- a/sampo/scheduler/base.py +++ b/sampo/scheduler/base.py @@ -24,6 +24,7 @@ class SchedulerType(Enum): Topological = 'topological' HEFTAddEnd = 'heft_add_end' HEFTAddBetween = 'heft_add_between' + LFT = 'LFT' class Scheduler(ABC): diff --git a/sampo/scheduler/lft/base.py b/sampo/scheduler/lft/base.py index def2e3d0..5fe962ca 100644 --- a/sampo/scheduler/lft/base.py +++ b/sampo/scheduler/lft/base.py @@ -30,7 +30,7 @@ class LFTScheduler(Scheduler): """ def __init__(self, - scheduler_type: SchedulerType = SchedulerType.HEFTAddEnd, + scheduler_type: SchedulerType = SchedulerType.LFT, timeline_type: Type = MomentumTimeline, work_estimator: WorkTimeEstimator = DefaultWorkEstimator()): super().__init__(scheduler_type, None, work_estimator) @@ -204,7 +204,7 @@ class RandomizedLFTScheduler(LFTScheduler): """ def __init__(self, - scheduler_type: SchedulerType = SchedulerType.HEFTAddEnd, + scheduler_type: SchedulerType = SchedulerType.LFT, timeline_type: Type = MomentumTimeline, work_estimator: WorkTimeEstimator = DefaultWorkEstimator(), rand: random.Random = random.Random()): diff --git a/tests/conftest.py b/tests/conftest.py index 8c6ef8ec..73b0f31b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,26 +55,14 @@ def setup_simple_synthetic(setup_rand) -> SimpleSynthetic: return SimpleSynthetic(setup_rand) -@fixture(params=[(graph_type, lag) for lag in [ - True, - False - ] - for graph_type in [ - 'manual', - 'small plain synthetic', - 'big plain synthetic' - ]], +@fixture(params=[(graph_type, lag) for lag in [True, False] + for graph_type in ['manual', + 'small plain synthetic', 'big plain synthetic']], # 'small advanced synthetic', 'big advanced synthetic']], ids=[f'Graph: {graph_type}, LAG_OPT={lag_opt}' - for lag_opt in [ - True, - False - ] - for graph_type in [ - 'manual', - 'small plain synthetic', - 'big plain synthetic' - ]]) + for lag_opt in [True, False] + for graph_type in ['manual', + 'small plain synthetic', 'big plain synthetic']]) # 'small advanced synthetic', 'big advanced synthetic']]) def setup_wg(request, setup_sampler, setup_simple_synthetic) -> WorkGraph: SMALL_GRAPH_SIZE = 100 @@ -91,26 +79,26 @@ def setup_wg(request, setup_sampler, setup_simple_synthetic) -> WorkGraph: s = get_start_stage() l1n1 = sr.graph_node('l1n1', [(s, 0, EdgeType.FinishStart)], group='0', work_id='000001') - # l1n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l1n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] l1n2 = sr.graph_node('l1n2', [(s, 0, EdgeType.FinishStart)], group='0', work_id='000002') - # l1n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l1n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] l2n1 = sr.graph_node('l2n1', [(l1n1, 0, EdgeType.FinishStart)], group='1', work_id='000011') - # l2n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l2n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] l2n2 = sr.graph_node('l2n2', [(l1n1, 0, EdgeType.FinishStart), (l1n2, 0, EdgeType.FinishStart)], group='1', work_id='000012') - # l2n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l2n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] l2n3 = sr.graph_node('l2n3', [(l1n2, 1, EdgeType.LagFinishStart)], group='1', work_id='000013') - # l2n3.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l2n3.work_unit.material_reqs = [MaterialReq('mat1', 50)] - l3n1 = sr.graph_node('l3n1', [(l2n1, 0, EdgeType.FinishStart), + l3n1 = sr.graph_node('l2n1', [(l2n1, 0, EdgeType.FinishStart), (l2n2, 0, EdgeType.FinishStart)], group='2', work_id='000021') - # l3n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] - l3n2 = sr.graph_node('l3n2', [(l2n2, 0, EdgeType.FinishStart)], group='2', work_id='000022') - # l3n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] - l3n3 = sr.graph_node('l3n3', [(l2n3, 1, EdgeType.LagFinishStart), + l3n1.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l3n2 = sr.graph_node('l2n2', [(l2n2, 0, EdgeType.FinishStart)], group='2', work_id='000022') + l3n2.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l3n3 = sr.graph_node('l2n3', [(l2n3, 1, EdgeType.LagFinishStart), (l2n2, 0, EdgeType.FinishStart)], group='2', work_id='000023') - # l3n3.work_unit.material_reqs = [MaterialReq('mat1', 50)] + l3n3.work_unit.material_reqs = [MaterialReq('mat1', 50)] f = get_finish_stage([l3n1, l3n2, l3n3]) wg = WorkGraph(s, f) @@ -141,8 +129,8 @@ def setup_wg(request, setup_sampler, setup_simple_synthetic) -> WorkGraph: # TODO Make parametrization with different(specialized) contractors -@fixture(params=[(i, 5 * j) for j in range(0,2) for i in range(1, 2)], - ids=[f'Contractors: count={i}, min_size={5 * j}' for j in range(0,2) for i in range(1, 2)]) +@fixture(params=[(i, 5 * j) for j in range(2) for i in range(1, 2)], + ids=[f'Contractors: count={i}, min_size={5 * j}' for j in range(2) for i in range(1, 2)]) def setup_scheduler_parameters(request, setup_wg, setup_landscape_many_holders) -> tuple[ WorkGraph, list[Contractor], LandscapeConfiguration | Any]: resource_req: Dict[str, int] = {} diff --git a/tests/scheduler/genetic/full_scheduling.py b/tests/scheduler/genetic/full_scheduling.py index b06201fc..96029e4c 100644 --- a/tests/scheduler/genetic/full_scheduling.py +++ b/tests/scheduler/genetic/full_scheduling.py @@ -1,65 +1,12 @@ from sampo.scheduler.genetic.base import GeneticScheduler -from sampo.scheduler.topological.base import TopologicalScheduler -from sampo.scheduler.lft.prioritization import lft_prioritization -from sampo.scheduler.heft.base import HEFTScheduler, HEFTBetweenScheduler -from sampo.utilities.validation import validate_schedule - -import pandas as pd def test_multiprocessing(setup_scheduler_parameters): setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - genetic = GeneticScheduler(number_of_generation=100, + genetic = GeneticScheduler(number_of_generation=10, mutate_order=0.05, mutate_resources=0.005, size_of_population=50) genetic.schedule(setup_wg, setup_contractors, landscape=setup_landscape) - - -def test_topological(setup_scheduler_parameters): - setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - - topological = TopologicalScheduler() - - schedule = topological.schedule(setup_wg, setup_contractors, landscape=setup_landscape) - - print(schedule.execution_time) - - -def test_lft_scheduling(setup_scheduler_parameters): - setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - - scheduler = HEFTScheduler() - schedule = scheduler.schedule(setup_wg, setup_contractors, validate=True, landscape=setup_landscape) - default_time = schedule.execution_time - - scheduler = HEFTScheduler(prioritization_f=lft_prioritization) - schedule = scheduler.schedule(setup_wg, setup_contractors, validate=True, landscape=setup_landscape) - lft_time = schedule.execution_time - - try: - validate_schedule(schedule, setup_wg, setup_contractors) - - except AssertionError as e: - raise AssertionError(f'Scheduler {scheduler} failed validation', e) - - print(default_time, lft_time) - - -def test_lft_scheduling2(setup_scheduler_parameters): - setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters - - scheduler = HEFTBetweenScheduler() - schedule = scheduler.schedule(setup_wg, setup_contractors, - validate=True, - landscape=setup_landscape) - default_time = schedule.execution_time - - try: - validate_schedule(schedule, setup_wg, setup_contractors) - except AssertionError as e: - raise AssertionError(f'Scheduler {scheduler} failed validation', e) - - print(default_time) diff --git a/tests/scheduler/lft/__init__.py b/tests/scheduler/lft/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/scheduler/lft/fixtures.py b/tests/scheduler/lft/fixtures.py new file mode 100644 index 00000000..2ec94588 --- /dev/null +++ b/tests/scheduler/lft/fixtures.py @@ -0,0 +1,18 @@ +from pytest import fixture + +from sampo.scheduler.lft.base import LFTScheduler, RandomizedLFTScheduler +import random + + +@fixture(params=[LFTScheduler, RandomizedLFTScheduler], + ids=[f'Scheduler: {scheduler}' for scheduler in ['LFTScheduler', 'RandomizedLFTScheduler']]) +def setup_schedulers_and_parameters(request, setup_scheduler_parameters) -> tuple: + scheduler = request.param + if isinstance(scheduler, RandomizedLFTScheduler): + scheduler = scheduler(rand=random.Random(2023)) + else: + scheduler = scheduler() + + setup_wg, setup_contractors, setup_landscape = setup_scheduler_parameters + + return setup_wg, setup_contractors, setup_landscape, scheduler diff --git a/tests/scheduler/lft/prioritization_test.py b/tests/scheduler/lft/prioritization_test.py new file mode 100644 index 00000000..1e65a3e8 --- /dev/null +++ b/tests/scheduler/lft/prioritization_test.py @@ -0,0 +1,21 @@ +from sampo.scheduler.lft.prioritization import lft_prioritization +from sampo.schemas.graph import GraphNode +from sampo.schemas.contractor import get_worker_contractor_pool +from tests.scheduler.lft.fixtures import setup_schedulers_and_parameters + + +def test_correct_order(setup_schedulers_and_parameters): + setup_wg, setup_contractors, _, scheduler = setup_schedulers_and_parameters + worker_pool = get_worker_contractor_pool(setup_contractors) + _, node_id2duration = scheduler._contractor_workers_assignment(setup_wg, setup_contractors, worker_pool) + order = lft_prioritization(setup_wg, node_id2duration) + seen: set[GraphNode] = set() + + for node in reversed(order): + if not node.children: + break + assert all([parent in seen for parent in node.parents]) + seen.add(node) + while node.is_inseparable_parent(): + node = node.inseparable_son + seen.add(node) diff --git a/tests/scheduler/lft/scheduling_test.py b/tests/scheduler/lft/scheduling_test.py new file mode 100644 index 00000000..ea80e98a --- /dev/null +++ b/tests/scheduler/lft/scheduling_test.py @@ -0,0 +1,18 @@ +from sampo.utilities.validation import validate_schedule +from tests.scheduler.lft.fixtures import setup_schedulers_and_parameters + + +def test_lft_scheduling(setup_schedulers_and_parameters): + setup_wg, setup_contractors, setup_landscape, scheduler = setup_schedulers_and_parameters + + schedule = scheduler.schedule(setup_wg, setup_contractors, + validate=True, + landscape=setup_landscape) + lft_time = schedule.execution_time + + assert not lft_time.is_inf() + + try: + validate_schedule(schedule, setup_wg, setup_contractors) + except AssertionError as e: + raise AssertionError(f'Scheduler {scheduler} failed validation', e)