-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Base class for stopping criteria * Implemented MaxIterations and passed tests * Imeplemented MaxRuntime and passed tests * Refactor MaxIterations and MaxRuntime * Add start_time and total_runtime fields to Statistics
- Loading branch information
Showing
11 changed files
with
363 additions
and
50 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from numpy.random import RandomState | ||
|
||
from alns.State import State | ||
from alns.stopping_criteria.StoppingCriterion import StoppingCriterion | ||
|
||
|
||
class MaxIterations(StoppingCriterion): | ||
def __init__(self, max_iterations: int): | ||
""" | ||
Criterion that stops after a maximum number of iterations. | ||
""" | ||
if max_iterations < 0: | ||
raise ValueError("Max iterations must be non-negative.") | ||
|
||
self._max_iterations = max_iterations | ||
self._current_iteration = 0 | ||
|
||
@property | ||
def max_iterations(self) -> int: | ||
return self._max_iterations | ||
|
||
def __call__(self, rnd: RandomState, best: State, current: State) -> bool: | ||
self._current_iteration += 1 | ||
|
||
return self._current_iteration > self.max_iterations |
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,29 @@ | ||
import time | ||
|
||
from typing import Optional | ||
from numpy.random import RandomState | ||
|
||
from alns.State import State | ||
from alns.stopping_criteria.StoppingCriterion import StoppingCriterion | ||
|
||
|
||
class MaxRuntime(StoppingCriterion): | ||
def __init__(self, max_runtime: float): | ||
""" | ||
Criterion that stops after a specified maximum runtime. | ||
""" | ||
if max_runtime < 0: | ||
raise ValueError("Max runtime must be non-negative.") | ||
|
||
self._max_runtime = max_runtime | ||
self._start_runtime: Optional[float] = None | ||
|
||
@property | ||
def max_runtime(self) -> float: | ||
return self._max_runtime | ||
|
||
def __call__(self, rnd: RandomState, best: State, current: State) -> bool: | ||
if self._start_runtime is None: | ||
self._start_runtime = time.perf_counter() | ||
|
||
return time.perf_counter () - self._start_runtime > self.max_runtime |
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,31 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
from numpy.random import RandomState | ||
|
||
from alns.State import State | ||
|
||
|
||
class StoppingCriterion(ABC): | ||
""" | ||
Base class from which to implement a stopping criterion. | ||
""" | ||
|
||
@abstractmethod | ||
def __call__(self, rnd: RandomState, best: State, current: State) -> bool: | ||
""" | ||
Determines whether to stop based on the implemented stopping criterion. | ||
Parameters | ||
---------- | ||
rnd | ||
May be used to draw random numbers from. | ||
best | ||
The best solution state observed so far. | ||
current | ||
The current solution state. | ||
Returns | ||
------- | ||
Whether to stop the iteration (True), or not (False). | ||
""" | ||
return NotImplemented |
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,3 @@ | ||
from .MaxIterations import MaxIterations | ||
from .MaxRuntime import MaxRuntime | ||
from .StoppingCriterion import StoppingCriterion |
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,52 @@ | ||
import pytest | ||
|
||
from numpy.random import RandomState | ||
from numpy.testing import assert_, assert_raises | ||
|
||
from alns.stopping_criteria import MaxIterations | ||
from alns.tests.states import Zero | ||
|
||
|
||
@pytest.mark.parametrize("max_iterations", [-1, -42, -10000]) | ||
def test_raise_negative_parameters(max_iterations: int): | ||
""" | ||
Maximum iterations cannot be negative. | ||
""" | ||
with assert_raises(ValueError): | ||
MaxIterations(max_iterations) | ||
|
||
|
||
@pytest.mark.parametrize("max_iterations", [1, 42, 10000]) | ||
def test_does_not_raise(max_iterations: int): | ||
""" | ||
Valid parameters should not raise. | ||
""" | ||
MaxIterations(max_iterations) | ||
|
||
|
||
@pytest.mark.parametrize("max_iterations", [1, 42, 10000]) | ||
def test_max_iterations(max_iterations): | ||
""" | ||
Test if the max iterations parameter is correctly set. | ||
""" | ||
stop = MaxIterations(max_iterations) | ||
assert stop.max_iterations == max_iterations | ||
|
||
|
||
def test_before_max_iterations(): | ||
stop = MaxIterations(100) | ||
rnd = RandomState(0) | ||
|
||
for _ in range(100): | ||
assert_(not stop(rnd, Zero(), Zero())) | ||
|
||
|
||
def test_after_max_iterations(): | ||
stop = MaxIterations(100) | ||
rnd = RandomState() | ||
|
||
for _ in range(100): | ||
stop(rnd, Zero(), Zero()) | ||
|
||
for _ in range(100): | ||
assert_(stop(rnd, Zero(), Zero())) |
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 time | ||
import pytest | ||
|
||
from numpy.random import RandomState | ||
from numpy.testing import assert_, assert_almost_equal, assert_raises | ||
|
||
from alns.stopping_criteria import MaxRuntime | ||
from alns.tests.states import Zero | ||
|
||
|
||
def sleep(duration, get_now=time.perf_counter): | ||
""" | ||
Custom sleep function. Built-in time.sleep function is not precise | ||
and has different performances depending on the OS, see | ||
https://stackoverflow.com/questions/1133857/how-accurate-is-pythons-time-sleep | ||
""" | ||
now = get_now() | ||
end = now + duration | ||
while now < end: | ||
now = get_now() | ||
|
||
|
||
@pytest.mark.parametrize("max_runtime", [-0.001, -1, -10.1]) | ||
def test_raise_negative_parameters(max_runtime: float): | ||
""" | ||
Maximum runtime may not be negative. | ||
""" | ||
with assert_raises(ValueError): | ||
MaxRuntime(max_runtime) | ||
|
||
|
||
@pytest.mark.parametrize("max_runtime", [0.001, 1, 10.1]) | ||
def test_valid_parameters(max_runtime: float): | ||
""" | ||
Does not raise for non-negative parameters. | ||
""" | ||
MaxRuntime(max_runtime) | ||
|
||
|
||
@pytest.mark.parametrize("max_runtime", [0.01, 0.1, 1]) | ||
def test_max_runtime(max_runtime): | ||
""" | ||
Test if the max time parameter is correctly set. | ||
""" | ||
stop = MaxRuntime(max_runtime) | ||
assert_(stop.max_runtime, max_runtime) | ||
|
||
|
||
@pytest.mark.parametrize("max_runtime", [0.01, 0.05, 0.10]) | ||
def test_before_max_runtime(max_runtime): | ||
stop = MaxRuntime(max_runtime) | ||
rnd = RandomState() | ||
for _ in range(100): | ||
assert_(not stop(rnd, Zero(), Zero())) | ||
|
||
|
||
@pytest.mark.parametrize("max_runtime", [0.01, 0.05, 0.10]) | ||
def test_after_max_runtime(max_runtime): | ||
stop = MaxRuntime(max_runtime) | ||
rnd = RandomState() | ||
stop(rnd, Zero(), Zero()) # Trigger the first time measurement | ||
sleep(max_runtime) | ||
|
||
for _ in range(100): | ||
assert_(stop(rnd, Zero(), Zero())) |
Oops, something went wrong.