Skip to content

Commit

Permalink
Allow passing kwargs to iterate (#56)
Browse files Browse the repository at this point in the history
* Allow passing kwargs to iterate

These kwargs are then passed to the operators. This is useful for larger heuristics, as this allows one to avoid explicit dependencies on global state (like a global data singleton)

* Bump version
  • Loading branch information
N-Wouda authored Mar 19, 2022
1 parent c2876f9 commit cb6260d
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 11 deletions.
1 change: 0 additions & 1 deletion .github/workflows/alns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:
- name: Run tests
run: |
poetry run pytest
poetry run mypy alns
- name: Run static analysis
run: |
poetry run mypy alns
Expand Down
17 changes: 12 additions & 5 deletions alns/ALNS.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
_ACCEPT = 2
_REJECT = 3

# TODO this should become a Protocol to allow for kwargs. See also this issue:
# https://stackoverflow.com/q/61569324/4316405.
_OperatorType = Callable[[State, rnd.RandomState], State]


Expand Down Expand Up @@ -108,7 +110,8 @@ def iterate(self,
initial_solution: State,
weight_scheme: WeightScheme,
crit: AcceptanceCriterion,
iterations: int = 10_000) -> Result:
iterations: int = 10_000,
**kwargs) -> Result:
"""
Runs the adaptive large neighbourhood search heuristic [1], using the
previously set destroy and repair operators. The first solution is set
Expand All @@ -127,6 +130,9 @@ def iterate(self,
the ``alns.criteria`` module for an overview.
iterations
The number of iterations. Default 10_000.
**kwargs
Optional keyword arguments. These are passed to the operators,
including callbacks.
Raises
------
Expand Down Expand Up @@ -165,8 +171,8 @@ class of vehicle routing problems with backhauls. *European Journal of
d_name, d_operator = self.destroy_operators[d_idx]
r_name, r_operator = self.repair_operators[r_idx]

destroyed = d_operator(curr, self._rnd_state)
cand = r_operator(destroyed, self._rnd_state)
destroyed = d_operator(curr, self._rnd_state, **kwargs)
cand = r_operator(destroyed, self._rnd_state, **kwargs)

best, curr, s_idx = self._consider_candidate(crit, best, curr, cand)

Expand Down Expand Up @@ -197,7 +203,8 @@ def _consider_candidate(
crit: AcceptanceCriterion,
best: State,
curr: State,
cand: State
cand: State,
**kwargs
) -> Tuple[State, State, int]:
"""
Considers the candidate solution by comparing it against the best and
Expand All @@ -218,7 +225,7 @@ def _consider_candidate(

if cand.objective() < best.objective(): # candidate is new best
if self._on_best:
cand = self._on_best(cand, self._rnd_state)
cand = self._on_best(cand, self._rnd_state, **kwargs)

return cand, cand, _BEST

Expand Down
19 changes: 17 additions & 2 deletions alns/tests/test_alns.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_add_destroy_operator():
alns = ALNS()

for count in [1, 2]:
alns.add_destroy_operator(lambda state, rnd: None, name=str(count))
alns.add_destroy_operator(lambda state, rnd: state, name=str(count))
assert_equal(len(alns.destroy_operators), count)


Expand Down Expand Up @@ -103,7 +103,7 @@ def test_add_repair_operator():
alns = ALNS()

for count in [1, 2]:
alns.add_repair_operator(lambda state, rnd: None, name=str(count))
alns.add_repair_operator(lambda state, rnd: state, name=str(count))
assert_equal(len(alns.repair_operators), count)


Expand Down Expand Up @@ -177,6 +177,21 @@ def test_raises_negative_iterations():
assert_(result.best_state is initial_solution)


def test_iterate_kwargs_are_correctly_passed_to_operators():

def test_operator(state, rnd, item):
assert_(item is orig_item)
return state

alns = get_alns_instance([lambda state, rnd, item: state], [test_operator])

init_sol = One()
weights = SimpleWeights([1, 1, 1, 1], 1, 1, .5)
orig_item = object()

alns.iterate(init_sol, weights, HillClimbing(), 10, item=orig_item)


# EXAMPLES ---------------------------------------------------------------------


Expand Down
4 changes: 2 additions & 2 deletions examples/travelling_salesman_problem.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
"source": [
"# The travelling salesman problem\n",
"\n",
"The travelling salesman problem (TSP) is a classic problem in operations research. It asks how to construct the minimum distance tour between a number of cities, such that each city is visited once and the tour concludes at the starting city (that is, it forms a cycle). It is perhaps the best-known problem in the class of [NP-hard](https://en.wikipedia.org/wiki/NP-hardness) problems.\n",
"The travelling salesman problem (TSP) is a classic problem in operations research. It asks how to construct the minimum distance tour between a number of nodes, such that each node is visited once and the tour concludes at the starting city (that is, it forms a cycle). It is perhaps the best-known problem in the class of [NP-hard](https://en.wikipedia.org/wiki/NP-hardness) problems.\n",
"\n",
"### Data\n",
"There are a considerable number of test data sets available for the TSP, varying in size from a hundred or so locations to many hundreds of thousands. For the sake of exposition, we shall use one of the smaller data sets: the data from the XQF131 VLSI instance, made available [here](http://www.math.uwaterloo.ca/tsp/vlsi/index.html#XQF131). It consists of 'only' 131 cities, with an optimal tour length of 564."
"There are a considerable number of test data sets available for the TSP, varying in size from a hundred or so locations to many hundreds of thousands. For the sake of exposition, we shall use one of the smaller data sets: the data from the XQF131 VLSI instance, made available [here](http://www.math.uwaterloo.ca/tsp/vlsi/index.html#XQF131). It consists of 'only' 131 nodes, with an optimal tour length of 564."
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "alns"
version = "2.0.1"
version = "2.0.2"
description = "A flexible implementation of the adaptive large neighbourhood search (ALNS) algorithm."
authors = ["Niels Wouda <[email protected]>"]
license = "MIT"
Expand Down

0 comments on commit cb6260d

Please sign in to comment.