diff --git a/.github/workflows/alns.yaml b/.github/workflows/alns.yaml index 79ec9f8..09b59ff 100644 --- a/.github/workflows/alns.yaml +++ b/.github/workflows/alns.yaml @@ -10,10 +10,14 @@ on: jobs: build: + name: > + Build and test ALNS using Python ${{ matrix.python-version }} and "${{ matrix.extras }}" runs-on: ubuntu-latest strategy: + fail-fast: true matrix: - python-version: [ '3.9', '3.10', '3.11', '3.12' ] + python-version: [ '3.9', '3.12' ] + extras: [ '--all-extras', '' ] steps: - uses: actions/checkout@v4 - name: Set up Python @@ -29,10 +33,10 @@ jobs: id: cache-python with: path: ~/.cache/pypoetry - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }} + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ matrix.extras }} - name: Install Python dependencies if: steps.cache-python.outputs.cache-hit != 'true' - run: poetry install + run: poetry install ${{ matrix.extras }} - name: Cache pre-commit uses: actions/cache@v4 id: cache-pre-commit diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 0cfcd29..32e6a1c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,7 +10,7 @@ build: post_install: # VIRTUAL_ENV needs to be set manually for now; see # https://github.com/readthedocs/readthedocs.org/issues/11150 for details. - - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs,examples + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs,examples --all-extras sphinx: configuration: docs/source/conf.py diff --git a/alns/select/tests/test_mab_selector.py b/alns/select/tests/test_mab_selector.py index e326512..1aae373 100644 --- a/alns/select/tests/test_mab_selector.py +++ b/alns/select/tests/test_mab_selector.py @@ -1,17 +1,20 @@ from typing import List import numpy.random as rnd -from mabwiser.mab import LearningPolicy, NeighborhoodPolicy +import pytest from numpy.testing import assert_, assert_equal, assert_raises -from pytest import mark from alns.Outcome import Outcome from alns.select import MABSelector -from alns.select.MABSelector import arm2ops, ops2arm +from alns.select.MABSelector import MABWISER_AVAILABLE, arm2ops, ops2arm from alns.tests.states import Zero, ZeroWithOneContext, ZeroWithZeroContext +if MABWISER_AVAILABLE: + from mabwiser.mab import LearningPolicy, NeighborhoodPolicy -@mark.parametrize( + +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") +@pytest.mark.parametrize( "destroy_idx, repair_idx", [ (0, 0), @@ -28,6 +31,7 @@ def test_arm_conversion(destroy_idx, repair_idx): assert_equal(actual, expected) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") def test_does_not_raise_on_valid_mab(): policy = LearningPolicy.EpsilonGreedy(0.15) select = MABSelector([5, 0, 3, 0], 2, 1, policy) @@ -41,33 +45,25 @@ def test_does_not_raise_on_valid_mab(): MABSelector([2, 1, 0, 0], 2, 1, policy, seed=1234567) -@mark.parametrize( - "scores, learning_policy, num_destroy, num_repair", +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") +@pytest.mark.parametrize( + "scores, num_destroy, num_repair", [ - ( - [5, 3, 2, -1], - LearningPolicy.EpsilonGreedy(0.15), - 1, - 1, - ), # negative score - ( - [5, 3, 2], - LearningPolicy.EpsilonGreedy(0.15), - 1, - 1, - ), # len(score) < 4 + ([5, 3, 2, -1], 1, 1), # negative score + ([5, 3, 2], 1, 1), # len(score) < 4 ], ) def test_raises_invalid_arguments( scores: List[float], - learning_policy: LearningPolicy, num_destroy: int, num_repair: int, ): + policy = LearningPolicy.EpsilonGreedy(0.15) with assert_raises(ValueError): - MABSelector(scores, num_destroy, num_repair, learning_policy) + MABSelector(scores, num_destroy, num_repair, policy) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") def test_call_with_only_one_operator_pair(): # Only one operator pair, so the algorithm should select (0, 0). select = MABSelector( @@ -80,6 +76,7 @@ def test_call_with_only_one_operator_pair(): assert_equal(selected, (0, 0)) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") def test_mab_epsilon_greedy(): rng = rnd.default_rng() @@ -98,7 +95,8 @@ def test_mab_epsilon_greedy(): assert_equal(selected, (1, 0)) -@mark.parametrize("alpha", [0.25, 0.5]) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") +@pytest.mark.parametrize("alpha", [0.25, 0.5]) def test_mab_ucb1(alpha): rng = rnd.default_rng() select = MABSelector([2, 1, 1, 0], 2, 1, LearningPolicy.UCB1(alpha)) @@ -112,6 +110,7 @@ def test_mab_ucb1(alpha): assert_equal(mab_select, (0, 0)) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") def test_contextual_mab_requires_context(): select = MABSelector( [2, 1, 1, 0], @@ -124,6 +123,7 @@ def test_contextual_mab_requires_context(): select.update(Zero(), 0, 0, outcome=Outcome.BEST) +@pytest.mark.skipif(not MABWISER_AVAILABLE, reason="MABWiser not available") def text_contextual_mab_uses_context(): rng = rnd.default_rng() select = MABSelector( diff --git a/docs/source/setup/contributing.rst b/docs/source/setup/contributing.rst index d1af316..b144973 100644 --- a/docs/source/setup/contributing.rst +++ b/docs/source/setup/contributing.rst @@ -22,7 +22,7 @@ Now, change into the ALNS directory, and set-up the virtual environment using `` cd ALNS pip install --upgrade poetry - poetry install --with examples + poetry install --with examples --all-extras This might take a few minutes, but only needs to be done once. Now make sure everything runs smoothly, by executing the test suite: diff --git a/docs/source/setup/installation.rst b/docs/source/setup/installation.rst index 8541b09..05c72e1 100644 --- a/docs/source/setup/installation.rst +++ b/docs/source/setup/installation.rst @@ -44,7 +44,7 @@ This goes like so: .. code-block:: cd ALNS - poetry install --with examples + poetry install --with examples --all-extras This might take a few minutes to resolve, but only needs to be done once. After setting everything up, simply open the jupyter notebooks: diff --git a/pyproject.toml b/pyproject.toml index 3e8bd27..a26ceff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,6 @@ pre-commit = "^2.20.0" pytest = ">=6.0.0" pytest-cov = ">=2.6.1" codecov = "*" -mabwiser = ">=2.7.1" # This optional docs group is needed to build the documentation. It is not # required by the package itself.