Skip to content

Commit

Permalink
Bug fixing and modifying genetic algorithm (#48)
Browse files Browse the repository at this point in the history
Modify genetic algorithm's logic and operators

Selection did not work correctly so the population degenerated - there was mistakes in operators order and arguments - now it is fixed.
Crossovers didn't work - now mistakes are fixed.
Mutation order operator changed to more stable to make validity chromosomes.
Population initialization changed to more various.
Selection changed to basic ranging, it is simplier and more efficient.
Resource borders changing operators are placed in condition blocks to disable them when it is not needed.

The results of the genetic algorithm have improved.
  • Loading branch information
Timotshak authored Aug 25, 2023
1 parent 925b802 commit 619e437
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 469 deletions.
2 changes: 1 addition & 1 deletion experiments/algorithms_2_multi_agency.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def log(message: str, logfile: IO):
schedulers = [HEFTScheduler(),
HEFTBetweenScheduler(),
TopologicalScheduler()]
# GeneticScheduler(50, 50, 0.5, 0.5, 20)]
# GeneticScheduler(50, 0.5, 0.5, 20)]

contractors = [p_rand.contractor(10) for _ in range(len(schedulers))]

Expand Down
10 changes: 5 additions & 5 deletions experiments/genetic_2_multi_agency.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def obstruction_getter(i: int):


if __name__ == '__main__':
schedulers = [GeneticScheduler(50, 50, 0.5, 0.5, 100),]
# GeneticScheduler(50, 50, 0.5, 0.5, 100),
# GeneticScheduler(50, 50, 0.5, 0.5, 100),
# GeneticScheduler(50, 50, 0.5, 0.5, 100),
# GeneticScheduler(50, 50, 0.5, 0.5, 100)]
schedulers = [GeneticScheduler(50, 0.5, 0.5, 100),]
# GeneticScheduler(50, 0.5, 0.5, 100),
# GeneticScheduler(50, 0.5, 0.5, 100),
# GeneticScheduler(50, 0.5, 0.5, 100),
# GeneticScheduler(50, 0.5, 0.5, 100)]

contractors = [p_rand.contractor(10) for _ in range(len(schedulers))]

Expand Down
20 changes: 10 additions & 10 deletions experiments/modular_examples/multi_agency_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@ def run_iteration(args):
if mode == 0:
schedulers = [HEFTScheduler(), HEFTBetweenScheduler(), TopologicalScheduler()]
else:
schedulers = [GeneticScheduler(50, 50, 0.5, 0.5, 100),
GeneticScheduler(50, 100, 0.25, 0.5, 100),
GeneticScheduler(50, 100, 0.5, 0.75, 100),
GeneticScheduler(50, 100, 0.75, 0.75, 100),
GeneticScheduler(50, 50, 0.9, 0.9, 100),
GeneticScheduler(50, 100, 0.5, 0.5, 500),
GeneticScheduler(50, 200, 0.25, 0.5, 500),
GeneticScheduler(50, 50, 0.5, 0.75, 500),
GeneticScheduler(50, 100, 0.75, 0.75, 500),
GeneticScheduler(50, 50, 0.5, 0.9, 500)]
schedulers = [GeneticScheduler(50, 0.5, 0.5, 100),
GeneticScheduler(50, 0.25, 0.5, 100),
GeneticScheduler(50, 0.5, 0.75, 100),
GeneticScheduler(50, 0.75, 0.75, 100),
GeneticScheduler(50, 0.9, 0.9, 100),
GeneticScheduler(50, 0.5, 0.5, 500),
GeneticScheduler(50, 0.25, 0.5, 500),
GeneticScheduler(50, 0.5, 0.75, 500),
GeneticScheduler(50, 0.75, 0.75, 500),
GeneticScheduler(50, 0.5, 0.9, 500)]
variate_block_size(logfile, schedulers)


Expand Down
41 changes: 12 additions & 29 deletions sampo/scheduler/genetic/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import math
import random
from typing import Optional, Callable

Expand Down Expand Up @@ -33,7 +32,6 @@ class GeneticScheduler(Scheduler):

def __init__(self,
number_of_generation: Optional[int] = 50,
size_selection: Optional[int or None] = None,
mutate_order: Optional[float or None] = None,
mutate_resources: Optional[float or None] = None,
size_of_population: Optional[float or None] = None,
Expand All @@ -49,64 +47,50 @@ def __init__(self,
resource_optimizer=resource_optimizer,
work_estimator=work_estimator)
self.number_of_generation = number_of_generation
self.size_selection = size_selection
self.mutate_order = mutate_order
self.mutate_resources = mutate_resources
self.size_of_population = size_of_population
self.rand = rand or random.Random(seed)
self.fitness_constructor = fitness_constructor
self.work_estimator = work_estimator
self._n_cpu = n_cpu
self._weights = None
self._weights = weights

self._time_border = None
self._deadline = None

def __str__(self) -> str:
return f'GeneticScheduler[' \
f'generations={self.number_of_generation},' \
f'size_selection={self.size_selection},' \
f'population_size={self.size_of_population},' \
f'mutate_order={self.mutate_order},' \
f'mutate_resources={self.mutate_resources}' \
f']'

def get_params(self, works_count: int) -> tuple[int, float, float, int]:
def get_params(self, works_count: int) -> tuple[float, float, int]:
"""
Return base parameters for model to make new population
:param works_count:
:return:
"""
size_selection = self.size_selection
if size_selection is None:
if works_count < 300:
size_selection = 20
else:
size_selection = works_count // 15

mutate_order = self.mutate_order
if mutate_order is None:
if works_count < 300:
mutate_order = 0.05
else:
mutate_order = 2 / math.sqrt(works_count)
mutate_order = 0.05

mutate_resources = self.mutate_resources
if mutate_resources is None:
if works_count < 300:
mutate_resources = 0.1
else:
mutate_resources = 6 / math.sqrt(works_count)
mutate_resources = 0.005

size_of_population = self.size_of_population
if size_of_population is None:
if works_count < 300:
size_of_population = 20
elif 1500 > works_count >= 300:
size_of_population = 50
elif 1500 > works_count >= 300:
size_of_population = 100
else:
size_of_population = works_count // 50
return size_selection, mutate_order, mutate_resources, size_of_population
size_of_population = works_count // 25
return mutate_order, mutate_resources, size_of_population

def set_use_multiprocessing(self, n_cpu: int):
"""
Expand All @@ -130,8 +114,8 @@ def set_deadline(self, deadline: Time):
def set_weights(self, weights: list[int]):
self._weights = weights

@classmethod
def generate_first_population(self, wg: WorkGraph, contractors: list[Contractor],
@staticmethod
def generate_first_population(wg: WorkGraph, contractors: list[Contractor],
landscape: LandscapeConfiguration = LandscapeConfiguration(),
spec: ScheduleSpec = ScheduleSpec(),
work_estimator: WorkTimeEstimator = None,
Expand Down Expand Up @@ -213,15 +197,14 @@ def schedule_with_cache(self,
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)
mutate_order, mutate_resources, size_of_population = self.get_params(wg.vertex_count)
worker_pool = get_worker_contractor_pool(contractors)

scheduled_works, schedule_start_time, timeline, order_nodes = build_schedule(wg,
contractors,
worker_pool,
size_of_population,
self.number_of_generation,
size_selection,
mutate_order,
mutate_resources,
init_schedules,
Expand Down
Loading

0 comments on commit 619e437

Please sign in to comment.