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

Commit

Permalink
Merge pull request #70 from garciparedes/master
Browse files Browse the repository at this point in the history
Issue 69
  • Loading branch information
garciparedes authored Jan 10, 2020
2 parents 34618fd + 9af704b commit 190fc23
Show file tree
Hide file tree
Showing 56 changed files with 1,295 additions and 687 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PR Details

## Related Issue
## Related Issues

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
Expand Down
17 changes: 17 additions & 0 deletions .github/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
1 change: 1 addition & 0 deletions examples/cordeau/example_insertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(self, *args, **kwargs):
class MyAlgorithm(jit.InsertionAlgorithm):
def __init__(self, *args, **kwargs):
super().__init__(
conjecturer_cls=jit.IntensiveConjecturer,
*args, **kwargs,
)

Expand Down
2 changes: 1 addition & 1 deletion examples/cordeau/example_milp_three_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(self, *args, **kwargs):
super().__init__(
storer_cls_set={
MyStorer,
# jit.GraphPlotStorer,
jit.GraphPlotStorer,
},
*args, **kwargs,
)
Expand Down
2 changes: 1 addition & 1 deletion examples/hashcode/example_insertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class MyAlgorithm(jit.InsertionAlgorithm):
def __init__(self, *args, **kwargs):
super().__init__(
neighborhood_max_size=None,
criterion_cls=jit.HashCodePlannedTripCriterion,
criterion_cls=jit.HashCodeRouteCriterion,
*args, **kwargs,
)

Expand Down
16 changes: 11 additions & 5 deletions jinete/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
TaxiSharingObjective,
HashCodeObjective,

PlannedTripCriterion,
ShortestTimePlannedTripCriterion,
LongestTimePlannedTripCriterion,
LongestUtilTimePlannedTripCriterion,
HashCodePlannedTripCriterion,
RouteCriterion,
ShortestTimeRouteCriterion,
LongestTimeRouteCriterion,
LongestUtilTimeRouteCriterion,
HashCodeRouteCriterion,

MAX_INT,
MIN_INT,
Expand Down Expand Up @@ -80,11 +80,17 @@
BestStatelessCrosser,
OrderedCrosser,
RandomizedCrosser,
Breeder,
FlipBreeder,
Conjecturer,
SamplingConjecturer,
IntensiveConjecturer,
)

from .exceptions import (
JineteException,
StopPlannedTripIterationException,
NonFeasiblePlannedTripException,
NonFeasibleRouteException,
PreviousStopNotInRouteException,
)
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, 12)
VERSION = (0, 0, 13)

__version__ = '.'.join(map(str, VERSION))
5 changes: 5 additions & 0 deletions jinete/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@
BestStatelessCrosser,
OrderedCrosser,
RandomizedCrosser,
Breeder,
FlipBreeder,
Conjecturer,
SamplingConjecturer,
IntensiveConjecturer,
)
3 changes: 2 additions & 1 deletion jinete/algorithms/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def optimize(self) -> Result:
planning=planning,
computation_time=computation_time,
)
logger.info(f'Optimized with {self.__class__.__name__}!')
logger.info(f'Optimized with {self.__class__.__name__} obtaining {"" if result.feasible else "non "}'
f'feasible results!')
return result

@abstractmethod
Expand Down
8 changes: 2 additions & 6 deletions jinete/algorithms/heuristics/insertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,10 @@ def _optimize(self) -> Planning:

while not crosser.completed:
try:
planned_trip = next(crosser)
route = next(crosser)
except StopPlannedTripIterationException:
break
route = planned_trip.route
route.append_planned_trip(planned_trip)
crosser.mark_planned_trip_as_done(planned_trip)
crosser.set_route(route)

for route in crosser.routes:
route.finish()
planning = Planning(crosser.routes)
return planning
13 changes: 9 additions & 4 deletions jinete/algorithms/heuristics/local_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@

class LocalSearchAlgorithm(Algorithm):

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

@property
def initial_planning(self) -> Planning:
Expand All @@ -45,11 +46,15 @@ def initial_routes(self) -> Set[Route]:
def _optimize(self) -> Planning:
best = self.initial

again = True
while again:
no_improvement_count = 0
while no_improvement_count < self.no_improvement_threshold:
no_improvement_count += 1

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

if best == current:
no_improvement_count = 0

assert best is not None

Expand Down
19 changes: 11 additions & 8 deletions jinete/algorithms/metaheuristics/grasp.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@

class GraspAlgorithm(Algorithm):

def __init__(self, first_solution_episodes: int = 3, local_search_episodes: int = 10,
def __init__(self, first_solution_episodes: int = 1, no_improvement_threshold: int = 10,
seed: int = 56, *args, **kwargs):
super().__init__(*args, **kwargs)

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

self.args = args
Expand All @@ -54,16 +54,19 @@ def build_local_search_algorithm(self, *args, **kwargs) -> Algorithm:

return LocalSearchAlgorithm(*args, **kwargs)

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

def _optimize(self) -> Planning:
iterative = self.build_first_solution_algorithm()
best = iterative.optimize()

i = 0
while self.again(i):
no_improvement_count = 0
while no_improvement_count < self.no_improvement_threshold:
no_improvement_count += 1

current = self.build_local_search_algorithm(initial=best).optimize()
current = self.build_first_solution_algorithm(routes=current.routes).optimize()
best = self.objective.best(best, current)
i += 1

if best == current:
no_improvement_count = 0

return best.planning
2 changes: 1 addition & 1 deletion jinete/algorithms/metaheuristics/iterative.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

class IterativeAlgorithm(Algorithm):

def __init__(self, episodes: int = 100, algorithm_cls: Type[Algorithm] = None, seed: int = 56, *args, **kwargs):
def __init__(self, episodes: int = 10, algorithm_cls: Type[Algorithm] = None, seed: int = 56, *args, **kwargs):
super().__init__(*args, **kwargs)
if algorithm_cls is None:
algorithm_cls = InsertionAlgorithm
Expand Down
5 changes: 5 additions & 0 deletions jinete/algorithms/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@
Breeder,
FlipBreeder,
)
from .conjecturer import (
Conjecturer,
SamplingConjecturer,
IntensiveConjecturer,
)
41 changes: 35 additions & 6 deletions jinete/algorithms/utils/breeders/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
ABC,
abstractmethod,
)
from typing import (
TYPE_CHECKING,
)
from copy import deepcopy

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


class Breeder(ABC):
Expand All @@ -29,6 +37,27 @@ def objective(self) -> Objective:
def planning(self) -> Planning:
return self.result.planning

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

def improve(self) -> Result:

if __debug__:
for route in self.routes:
assert all(s1 == s2.previous for s1, s2 in zip(route.stops[:-1], route.stops[1:]))
assert all(s1.departure_time <= s2.arrival_time for s1, s2 in zip(route.stops[:-1], route.stops[1:]))

result = self._improve()

if __debug__:
for route in result.routes:
assert all(s1 == s2.previous for s1, s2 in zip(route.stops[:-1], route.stops[1:]))
if not all(s1.departure_time <= s2.arrival_time for s1, s2 in zip(route.stops[:-1], route.stops[1:])):
assert False

return result

@abstractmethod
def _improve(self) -> Result:
pass
33 changes: 27 additions & 6 deletions jinete/algorithms/utils/breeders/flips.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,52 @@
Breeder,
)
from ....models import (
Result
Result,
Stop,
Route,
)

logger = logging.getLogger(__name__)


class FlipBreeder(Breeder):

def improve(self) -> Result:
for route in self.planning.routes:
def _improve(self) -> Result:
logger.info(f'Starting to improve "Result" with "{self.__class__.__name__}"...')
for idx, route in enumerate(self.routes):
cost = self.objective.optimization_function(route)

for i in range(1, len(route.stops) - 1):
j = i + 1
k = i + 2
first = route.stops[i]
second = route.stops[j]
third = route.stops[k] if k < len(route.stops) else None

if not set(first.pickups).isdisjoint(second.deliveries):
continue
first.flip(second)
self.flip(route, first, second, third)

if not route.feasible or cost < self.objective.optimization_function(route):
second.flip(first)
if not route.feasible or cost == self.objective.best(cost, route):
self.flip(route, second, first, third)
continue

cost = self.objective.optimization_function(route)
logger.info(f'Flipped "{i}"-th and "{j}"-th stops from "{route}".')
return self.result

def flip(self, route: Route, previous: Stop, other: Stop, following: Stop = None) -> None:
assert following is None or following.previous == other
assert other.previous == previous

self_index = route.stops.index(other)
other_index = route.stops.index(previous)
route.stops[self_index], route.stops[other_index] = route.stops[other_index], route.stops[self_index]

if following is not None:
following.previous = previous
other.previous = previous.previous
previous.previous = other

for stop in route.stops[self_index:]:
stop.flush()
Loading

0 comments on commit 190fc23

Please sign in to comment.