Skip to content
This repository has been archived by the owner on May 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #43 from garciparedes/master
Browse files Browse the repository at this point in the history
Issue 37
  • Loading branch information
garciparedes authored Sep 1, 2019
2 parents 67cb9ec + 4c82b98 commit 1c925d8
Show file tree
Hide file tree
Showing 80 changed files with 2,509 additions and 437 deletions.
8 changes: 8 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
coverage:
status:
project:
default:
threshold: 1
patch:
default:
threshold: 1
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ install:
- 'pip install pipenv'
- 'pipenv sync'
script:
- 'pipenv run coverage run --source=jinete --module unittest discover tests'
- 'pipenv run flake8'
- 'pipenv run mypy'
- 'pipenv run coverage run -m unittest discover tests'
after_success:
- 'pipenv run codecov'

branches:
only:
- master
229 changes: 229 additions & 0 deletions Pipfile.lock

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion examples/main_cordeau.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,20 @@ def __init__(self, *args, **kwargs):
*args, **kwargs,
)

class MyStorerSet(jit.StorerSet):
def __init__(self, *args, **kwargs):
super().__init__(
storer_cls_set={
MyStorer,
jit.GraphPlotStorer,
},
*args, **kwargs,
)

dispatcher = jit.StaticDispatcher(
MyLoader,
MyAlgorithm,
MyStorer,
MyStorerSet,
)

result = dispatcher.run()
Expand Down
12 changes: 10 additions & 2 deletions jinete/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
Route,
Planning,
PlannedTrip,

Stop,

Result,

OptimizationDirection,
Expand Down Expand Up @@ -59,13 +62,17 @@
Storer,
FileStorer,
PromptStorer,
GraphPlotStorer,
StorerSet,
)

from .algorithms import (
Algorithm,
NaiveAlgorithm,
InsertionAlgorithm,
LocalSearchAlgorithm,
GraspAlgorithm,
IterativeAlgorithm,
Crosser,
StatelessCrosser,
BestStatelessCrosser,
Expand All @@ -75,6 +82,7 @@

from .exceptions import (
JineteException,
NonFeasiblePlannedTripFoundException,
PlannedTripNotFeasibleException,
StopPlannedTripIterationException,
NonFeasiblePlannedTripException,
NonFeasibleRouteException,
)
2 changes: 1 addition & 1 deletion jinete/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VERSION = (0, 0, 10)
VERSION = (0, 0, 11)

__version__ = '.'.join(map(str, VERSION))
2 changes: 2 additions & 0 deletions jinete/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
)
from .heuristics import (
InsertionAlgorithm,
LocalSearchAlgorithm,
)
from .metaheuristics import (
GraspAlgorithm,
IterativeAlgorithm,
)
from .utils import (
Crosser,
Expand Down
3 changes: 3 additions & 0 deletions jinete/algorithms/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def objective(self) -> Objective:
return self.job.objective

def optimize(self) -> Result:
logger.info(f'Optimizing with {self.__class__.__name__}...')

start_time = time()
planning = self._optimize()
end_time = time()
Expand All @@ -42,6 +44,7 @@ def optimize(self) -> Result:
planning=planning,
computation_time=computation_time,
)
logger.info(f'Optimized with {self.__class__.__name__}!')
return result

@abstractmethod
Expand Down
3 changes: 3 additions & 0 deletions jinete/algorithms/heuristics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from .insertion import (
InsertionAlgorithm,
)
from .local_search import (
LocalSearchAlgorithm,
)
14 changes: 6 additions & 8 deletions jinete/algorithms/heuristics/insertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
import logging
from typing import TYPE_CHECKING

from jinete.models import (
from ...models import (
Planning,
)
from jinete.algorithms.abc import (
from ...exceptions import (
StopPlannedTripIterationException,
)
from ..abc import (
Algorithm,
)
from ..utils import (
OrderedCrosser,
)
from jinete.exceptions import (
NonFeasiblePlannedTripFoundException,
)

if TYPE_CHECKING:
from typing import (
Expand All @@ -41,13 +41,12 @@ def build_crosser(self) -> Crosser:
return self.crosser_cls(*self.args, **self.kwargs)

def _optimize(self) -> Planning:
logger.info('Optimizing...')
crosser = self.build_crosser()

while not crosser.completed:
try:
planned_trip = next(crosser)
except NonFeasiblePlannedTripFoundException:
except StopPlannedTripIterationException:
break
route = planned_trip.route
route.append_planned_trip(planned_trip)
Expand All @@ -56,5 +55,4 @@ def _optimize(self) -> Planning:
for route in crosser.routes:
route.finish()
planning = Planning(crosser.routes)
logger.info('Optimized!')
return planning
60 changes: 60 additions & 0 deletions jinete/algorithms/heuristics/local_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations

import logging
from copy import deepcopy
from typing import TYPE_CHECKING

from ...exceptions import (
NonFeasiblePlannedTripException,
)
from ...models import (
Planning,
)
from ..abc import (
Algorithm,
)
from ..utils.breeders import (
FlipBreeder
)

if TYPE_CHECKING:
from typing import (
Set,
)
from ...models import (
Result,
Route,
)

logger = logging.getLogger(__name__)


class LocalSearchAlgorithm(Algorithm):

def __init__(self, initial: Result, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial = initial
self.args = args
self.kwargs = kwargs
self.breeder_cls = FlipBreeder

@property
def initial_planning(self) -> Planning:
return self.initial.planning

@property
def initial_routes(self) -> Set[Route]:
return self.initial_planning.routes

def _optimize(self) -> Planning:
best = self.initial

again = True
while again:
current = self.breeder_cls(best).improve()
best = self.objective.best(best, current)
again = best == current

assert best is not None

return best.planning
3 changes: 3 additions & 0 deletions jinete/algorithms/metaheuristics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from .grasp import (
GraspAlgorithm,
)
from .iterative import (
IterativeAlgorithm,
)
52 changes: 38 additions & 14 deletions jinete/algorithms/metaheuristics/grasp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,64 @@
Algorithm,
)
from ..heuristics import (
InsertionAlgorithm,
LocalSearchAlgorithm,
)
from .iterative import (
IterativeAlgorithm,
)

if TYPE_CHECKING:
from typing import (
Type,
Optional,
)
from ...models import (
Result,
)

logger = logging.getLogger(__name__)


class GraspAlgorithm(Algorithm):

def __init__(self, episodes: int = 100, algorithm_cls: Type[Algorithm] = None, seed: int = 56, *args, **kwargs):
def __init__(self, first_solution_episodes: int = 3, local_search_episodes: int = 10,
seed: int = 56, *args, **kwargs):
super().__init__(*args, **kwargs)
if algorithm_cls is None:
algorithm_cls = InsertionAlgorithm
self.episodes = episodes
self.algorithm_cls = algorithm_cls

self.first_solution_episodes = first_solution_episodes
self.local_search_episodes = local_search_episodes
self.random = Random(seed)

self.args = args
self.kwargs = kwargs

def build_algorithm(self, *args, **kwargs) -> Algorithm:
return self.algorithm_cls(*self.args, *args, **self.kwargs, **kwargs)
def build_first_solution_algorithm(self, *args, **kwargs) -> Algorithm:
args = (*self.args, *args)
kwargs.update(self.kwargs)

kwargs['episodes'] = self.first_solution_episodes
kwargs['seed'] = self.random.randint(0, MAX_INT)

return IterativeAlgorithm(*args, **kwargs)

def build_local_search_algorithm(self, *args, **kwargs) -> Algorithm:
args = (*self.args, *args)
kwargs.update(self.kwargs)

kwargs['seed'] = self.random.randint(0, MAX_INT),

return LocalSearchAlgorithm(*args, **kwargs)

def again(self, episode_count: int, *args, **kwargs):
return episode_count < self.first_solution_episodes

def _optimize(self) -> Planning:
logger.info('Optimizing...')
iterative = self.build_first_solution_algorithm()
best = iterative.optimize()

best = None
for i in range(self.episodes):
seed = self.random.randint(0, MAX_INT)
current = self.build_algorithm(seed=seed).optimize()
i = 0
while self.again(i):
current = self.build_local_search_algorithm(initial=best).optimize()
best = self.objective.best(best, current)
logger.info('Optimized!')
i += 1
return best.planning
57 changes: 57 additions & 0 deletions jinete/algorithms/metaheuristics/iterative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from __future__ import annotations

import logging
from random import Random
from typing import TYPE_CHECKING

from ...models import (
Planning,
MAX_INT,
)
from ..abc import (
Algorithm,
)
from ..heuristics import (
InsertionAlgorithm,
)

if TYPE_CHECKING:
from typing import (
Type,
Optional,
)
from ...models import (
Result,
)

logger = logging.getLogger(__name__)


class IterativeAlgorithm(Algorithm):

def __init__(self, episodes: int = 100, algorithm_cls: Type[Algorithm] = None, seed: int = 56, *args, **kwargs):
super().__init__(*args, **kwargs)
if algorithm_cls is None:
algorithm_cls = InsertionAlgorithm
self.episodes = episodes
self.algorithm_cls = algorithm_cls
self.random = Random(seed)

self.args = args
self.kwargs = kwargs

def build_algorithm(self, *args, **kwargs) -> Algorithm:
args = (*self.args, *args)
kwargs.update(self.kwargs)

return self.algorithm_cls(*args, **kwargs)

def _optimize(self) -> Planning:
best: Optional[Result] = None
for i in range(self.episodes):
seed = self.random.randint(0, MAX_INT)
current = self.build_algorithm(seed=seed).optimize()
best = self.objective.best(best, current)

assert best is not None
return best.planning
Loading

0 comments on commit 1c925d8

Please sign in to comment.