-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extended all schedulers with the ability of participate in population-based hybridisation schemes. Implemented cyclic hybridisation scheme, made experiments.
- Loading branch information
1 parent
dc365c7
commit 78debc1
Showing
13 changed files
with
566 additions
and
61 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import json | ||
|
||
import pathos | ||
from tqdm import tqdm | ||
|
||
import sampo.scheduler | ||
from sampo.backend.multiproc import MultiprocessingComputationalBackend | ||
|
||
from sampo.hybrid.population_tabu import TabuPopulationScheduler | ||
|
||
from sampo.hybrid.cycle import CycleHybridScheduler | ||
from sampo.api.genetic_api import ScheduleGenerationScheme | ||
from sampo.scheduler import HEFTScheduler, HEFTBetweenScheduler, TopologicalScheduler, GeneticScheduler | ||
from sampo.hybrid.population import HeuristicPopulationScheduler, GeneticPopulationScheduler | ||
|
||
from sampo.generator.environment import get_contractor_by_wg | ||
from sampo.generator import SimpleSynthetic | ||
|
||
from sampo.base import SAMPO | ||
from sampo.schemas import WorkGraph | ||
|
||
def run_experiment(args): | ||
graph_size, iteration = args | ||
|
||
heuristics = HeuristicPopulationScheduler([HEFTScheduler(), HEFTBetweenScheduler(), TopologicalScheduler()]) | ||
# genetic1 = TabuPopulationScheduler() | ||
genetic1 = GeneticPopulationScheduler(GeneticScheduler(mutate_order=0.2, | ||
mutate_resources=0.2, | ||
sgs_type=ScheduleGenerationScheme.Parallel)) | ||
genetic2 = GeneticPopulationScheduler(GeneticScheduler(mutate_order=0.001, | ||
mutate_resources=0.001, | ||
sgs_type=ScheduleGenerationScheme.Parallel)) | ||
|
||
hybrid_combine = CycleHybridScheduler(heuristics, [genetic1, genetic2], max_plateau_size=1) | ||
hybrid_genetic1 = CycleHybridScheduler(heuristics, [genetic1], max_plateau_size=1) | ||
hybrid_genetic2 = CycleHybridScheduler(heuristics, [genetic2], max_plateau_size=1) | ||
|
||
wg = WorkGraph.load('wgs', f'{graph_size}_{iteration}') | ||
contractors = [get_contractor_by_wg(wg)] | ||
|
||
# SAMPO.backend = MultiprocessingComputationalBackend(n_cpus=10) | ||
SAMPO.backend.cache_scheduler_info(wg, contractors) | ||
SAMPO.backend.cache_genetic_info() | ||
|
||
schedule_hybrid_combine = hybrid_combine.schedule(wg, contractors) | ||
schedule_genetic1 = hybrid_genetic1.schedule(wg, contractors) | ||
schedule_genetic2 = hybrid_genetic2.schedule(wg, contractors) | ||
|
||
# print(f'Hybrid combine: {schedule_hybrid_combine.execution_time}') | ||
# print(f'Scheduler 1 cycled: {schedule_genetic1.execution_time}') | ||
# print(f'Scheduler 2 cycled: {schedule_genetic2.execution_time}') | ||
return schedule_hybrid_combine.execution_time, schedule_genetic1.execution_time, schedule_genetic2.execution_time | ||
|
||
if __name__ == '__main__': | ||
arguments = [(graph_size, iteration) for graph_size in [100, 200, 300, 400, 500] for iteration in range(5)] | ||
results = {graph_size: [] for graph_size in [100, 200, 300, 400, 500]} | ||
|
||
with pathos.multiprocessing.Pool(processes=11) as p: | ||
r = p.map(run_experiment, arguments) | ||
|
||
for (graph_size, _), (combined_time, time1, time2) in zip(arguments, r): | ||
results[graph_size].append((combined_time / time1, combined_time / time2)) | ||
|
||
with open('hybrid_results.json', 'w') as f: | ||
json.dump(results, f) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import json | ||
from itertools import chain | ||
from random import Random | ||
|
||
import matplotlib.pyplot as plt | ||
|
||
with open('hybrid_results.json', 'r') as f: | ||
results = json.load(f) | ||
|
||
results = {int(graph_size): [100 * (1 - res) for res in chain(*result)] | ||
for graph_size, result in results.items()} | ||
graph_sizes = results.keys() | ||
|
||
rand = Random() | ||
|
||
plt.title('Прирост качества планов\nот применения гибридизации', fontsize=16) | ||
plt.xlabel('Размер графа') | ||
plt.ylabel('% прироста качества относительно базового алгоритма') | ||
plt.boxplot(results.values(), labels=graph_sizes) | ||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from sampo.generator import SimpleSynthetic | ||
|
||
from tqdm import tqdm | ||
|
||
ss = SimpleSynthetic(rand=231) | ||
|
||
for size in range(100, 500 + 1, 100): | ||
for i in tqdm(range(100)): | ||
wg = ss.work_graph(bottom_border=size - 5, | ||
top_border=size) | ||
while not (size - 5 <= wg.vertex_count <= size): | ||
wg = ss.work_graph(bottom_border=size - 20, | ||
top_border=size) | ||
wg.dump('wgs', f'{size}_{i}') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import numpy as np | ||
|
||
from sampo.api.genetic_api import FitnessFunction, ChromosomeType, ScheduleGenerationScheme | ||
from sampo.base import SAMPO | ||
from sampo.hybrid.population import PopulationScheduler | ||
from sampo.scheduler.genetic import TimeFitness | ||
from sampo.scheduler.genetic.schedule_builder import create_toolbox | ||
from sampo.schemas import WorkGraph, Contractor, Time, LandscapeConfiguration, Schedule | ||
from sampo.schemas.schedule_spec import ScheduleSpec | ||
|
||
|
||
class CycleHybridScheduler: | ||
def __init__(self, | ||
starting_scheduler: PopulationScheduler, | ||
cycle_schedulers: list[PopulationScheduler], | ||
fitness: FitnessFunction = TimeFitness(), | ||
max_plateau_size: int = 2): | ||
self._starting_scheduler = starting_scheduler | ||
self._cycle_schedulers = cycle_schedulers | ||
self._fitness = fitness | ||
self._max_plateau_size = max_plateau_size | ||
|
||
def _get_population_fitness(self, pop: list[ChromosomeType]): | ||
# return best chromosome's fitness | ||
return min(SAMPO.backend.compute_chromosomes(self._fitness, pop)) | ||
|
||
def _get_best_individual(self, pop: list[ChromosomeType]) -> ChromosomeType: | ||
fitness = SAMPO.backend.compute_chromosomes(self._fitness, pop) | ||
return pop[np.argmin(fitness)] | ||
|
||
def run(self, | ||
wg: WorkGraph, | ||
contractors: list[Contractor], | ||
spec: ScheduleSpec = ScheduleSpec(), | ||
assigned_parent_time: Time = Time(0), | ||
landscape: LandscapeConfiguration = LandscapeConfiguration()) -> ChromosomeType: | ||
pop = self._starting_scheduler.schedule([], wg, contractors, spec, assigned_parent_time, landscape) | ||
|
||
cur_fitness = Time.inf().value | ||
plateau_steps = 0 | ||
|
||
while True: | ||
pop_fitness = self._get_population_fitness(pop) | ||
if pop_fitness == cur_fitness: | ||
plateau_steps += 1 | ||
if plateau_steps == self._max_plateau_size: | ||
break | ||
else: | ||
plateau_steps = 0 | ||
cur_fitness = pop_fitness | ||
|
||
for scheduler in self._cycle_schedulers: | ||
pop = scheduler.schedule(pop, wg, contractors, spec, assigned_parent_time, landscape) | ||
|
||
return self._get_best_individual(pop) | ||
|
||
def schedule(self, | ||
wg: WorkGraph, | ||
contractors: list[Contractor], | ||
spec: ScheduleSpec = ScheduleSpec(), | ||
assigned_parent_time: Time = Time(0), | ||
sgs_type: ScheduleGenerationScheme = ScheduleGenerationScheme.Parallel, | ||
landscape: LandscapeConfiguration = LandscapeConfiguration()) -> Schedule: | ||
best_ind = self.run(wg, contractors, spec, assigned_parent_time, landscape) | ||
|
||
toolbox = create_toolbox(wg=wg, contractors=contractors, landscape=landscape, | ||
assigned_parent_time=assigned_parent_time, spec=spec, | ||
sgs_type=sgs_type) | ||
node2swork = toolbox.chromosome_to_schedule(best_ind)[0] | ||
|
||
return Schedule.from_scheduled_works(node2swork.values(), wg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
from sampo.api.genetic_api import ChromosomeType, Individual | ||
from sampo.scheduler import GeneticScheduler, Scheduler | ||
from sampo.scheduler.genetic.schedule_builder import create_toolbox | ||
from sampo.schemas import WorkGraph, Contractor, Time, LandscapeConfiguration | ||
from sampo.schemas.schedule_spec import ScheduleSpec | ||
|
||
|
||
class PopulationScheduler(ABC): | ||
|
||
@abstractmethod | ||
def schedule(self, | ||
initial_population: list[ChromosomeType], | ||
wg: WorkGraph, | ||
contractors: list[Contractor], | ||
spec: ScheduleSpec = ScheduleSpec(), | ||
assigned_parent_time: Time = Time(0), | ||
landscape: LandscapeConfiguration = LandscapeConfiguration()) -> list[ChromosomeType]: | ||
... | ||
|
||
|
||
class GeneticPopulationScheduler(PopulationScheduler): | ||
def __init__(self, genetic: GeneticScheduler): | ||
self._genetic = genetic | ||
|
||
def schedule(self, | ||
initial_population: list[ChromosomeType], | ||
wg: WorkGraph, | ||
contractors: list[Contractor], | ||
spec: ScheduleSpec = ScheduleSpec(), | ||
assigned_parent_time: Time = Time(0), | ||
landscape: LandscapeConfiguration = LandscapeConfiguration()) -> list[ChromosomeType]: | ||
return self._genetic.upgrade_pop(wg, contractors, initial_population, spec, | ||
assigned_parent_time, landscape=landscape) | ||
|
||
|
||
class HeuristicPopulationScheduler(PopulationScheduler): | ||
def __init__(self, schedulers: list[Scheduler]): | ||
self._schedulers = schedulers | ||
|
||
def schedule(self, | ||
initial_population: list[ChromosomeType], | ||
wg: WorkGraph, | ||
contractors: list[Contractor], | ||
spec: ScheduleSpec = ScheduleSpec(), | ||
assigned_parent_time: Time = Time(0), | ||
landscape: LandscapeConfiguration = LandscapeConfiguration()) -> list[ChromosomeType]: | ||
toolbox = create_toolbox(wg=wg, contractors=contractors, | ||
spec=spec, assigned_parent_time=assigned_parent_time, | ||
landscape=landscape) | ||
return [toolbox.Individual(toolbox.schedule_to_chromosome(schedule=schedule)) | ||
for scheduler in self._schedulers | ||
for schedule in scheduler.schedule(wg, contractors, spec, landscape=landscape)] |
Oops, something went wrong.