diff --git a/.ci/secret.tar.enc b/.ci/secret.tar.enc deleted file mode 100644 index 1e9847e7..00000000 Binary files a/.ci/secret.tar.enc and /dev/null differ diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 0d7f5b15..00000000 --- a/.codecov.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Automatically generated by nengo-bones, do not edit this file directly - -codecov: - ci: - - "!ci.appveyor.com" - notify: - require_ci_to_pass: no - -coverage: - status: - project: - default: - enabled: yes - target: auto - patch: - default: - enabled: yes - target: 100% - changes: no diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..fae501ef --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,89 @@ +name: CI testing +on: + pull_request: {} + push: + branches: + - main + - release-candidate-* + tags: + - v* + workflow_dispatch: + inputs: + debug_enabled: + description: Run the build with SSH debugging enabled + required: false + default: false + +jobs: + static: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: nengo/nengo-bones/actions/setup@main + with: + python-version: "3.9" + - uses: nengo/nengo-bones/actions/generate-and-check@main + - uses: nengo/nengo-bones/actions/run-script@main + with: + name: static + test: + needs: + - static + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + matrix: + include: + - script: remote-test + tf-version: tensorflow + python-version: "3.9" + coverage-name: remote-test + - script: test + tf-version: tensorflow + python-version: "3.9" + coverage-name: test-latest + - script: test + tf-version: tensorflow==2.1.0 + python-version: "3.6" + coverage-name: test-old + - script: test + tf-version: tensorflow~=2.6.0 + python-version: "3.9" + coverage-name: test-2.6 + - script: remote-docs + tf-version: tensorflow + python-version: "3.9" + - script: remote-examples + tf-version: tensorflow + python-version: "3.9" + fail-fast: false + env: + TF_VERSION: ${{ matrix.tf-version }} + SSH_KEY: ${{ secrets.SSH_KEY }} + SSH_CONFIG: ${{ secrets.SSH_CONFIG }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + steps: + - uses: nengo/nengo-bones/actions/setup@main + with: + python-version: ${{ matrix.python-version }} + - uses: nengo/nengo-bones/actions/generate-and-check@main + - name: Write secrets to file + run: | + mkdir -p ~/.ssh + echo '${{ secrets.AZURE_PEM }}' > ~/.ssh/azure.pem + - uses: nengo/nengo-bones/actions/run-script@main + with: + name: ${{ matrix.script }} + - uses: actions/upload-artifact@v3 + if: ${{ always() && matrix.coverage-name }} + with: + name: coverage-${{ matrix.coverage-name }} + path: .coverage + coverage: + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: + - test + if: ${{ always() }} + steps: + - uses: nengo/nengo-bones/actions/coverage-report@main diff --git a/.gitignore b/.gitignore index 99cd6944..e3bd3317 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ keras_lmu.egg-info *.ipynb_checkpoints __pycache__ /.idea +/bones-scripts /docs/_build +/secret /tmp -/.ci diff --git a/.nengobones.yml b/.nengobones.yml index 1ea19546..69302ddf 100644 --- a/.nengobones.yml +++ b/.nengobones.yml @@ -62,36 +62,6 @@ docs_conf_py: sphinx_options: autodoc_inherit_docstrings: False -travis_yml: - python: 3.8 - global_vars: - TF_VERSION: tensorflow - jobs: - - script: static - - script: remote-test - - script: test - env: - TF_VERSION: tensorflow==2.1.0 - python: 3.6 - - script: test - env: - TF_VERSION: tensorflow~=2.6.0 - - script: remote-docs - - script: remote-examples - pypi_user: __token__ - slack_notifications: "wZ7l/X7cVeetmwfup7vCeN74pqFGMC5eaJfy/aqRwVGCbY3aHQKoqJaBBrVef\ - c+DsJwPPM9HIOGs7jkPY+Y1pFbklAhWCDCvmc+f3fL4/yPWK1u7r8IIHhM3O0YvYrEHfFfZn+V1nAomx1\ - /wttFOr07LLffaNOn+sFzXgKP1Ebv5MrlCNGY0q3T2A38pwXus60GXaGalI/I/a/vjRdkEmq38eIMqLl0\ - v94oCy67Gqb/5mlB8eT6SffLGoFCctNpC9G9jyh/eRQp+B+YwksjZz+SOQTIy/mOlk8fU1ZL4f9wPguXh\ - eKznckuvX+J0o+o5kspyrQveWW2lo1VjmnFfdifCVjTj4AIcmc60Zb2dMzzBcfcA+zeVlhWkWbB65LuuX\ - kfxxsN97EwP5KVH+Lw7c2ycUyHV8XvKHNzVRLIUuVeOTKLHgGpkVgz/WasT5yBOEbnAyZxxAHQpMbXQr9\ - POwaq92Kwb7du0sLcsP4agdd2L0YACb4JdwQeeD1c8h6RrXua2ejBJ41e+9ZkZcZGad9P6YVmxvI+hddd\ - jdzzvBWq6+B/Ubft9AqQXmn4pg0fKkZLjrzsSYGRhXzINxhVBjZD8b5BQIb/Aeix3IFhLmDdSQMhjdETA\ - gL3jSd9D5t1G5QNp4AdbitqcH+yNoXzZCXye76hrwYr6yrHi3JN9yrM=" - deploy_dists: - - sdist - - bdist_wheel - ci_scripts: - template: static - template: docs @@ -104,7 +74,6 @@ ci_scripts: remote_script: test output_name: remote-test host: azure - travis_var_key: 2895d60e3414 azure_name: nengo-dl azure_group: nengo-ci coverage: true @@ -118,7 +87,6 @@ ci_scripts: remote_script: docs output_name: remote-docs host: azure-docs - travis_var_key: 2895d60e3414 azure_name: nengo-dl-docs azure_group: nengo-ci remote_setup: @@ -128,15 +96,11 @@ ci_scripts: remote_script: examples output_name: remote-examples host: azure-examples - travis_var_key: 2895d60e3414 azure_name: nengo-dl-examples azure_group: nengo-ci remote_setup: - conda install -y -c conda-forge cudatoolkit=11.3 cudnn=8.2 - export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:\$CONDA_PREFIX/lib - - template: deploy - -codecov_yml: {} pre_commit_config_yaml: {} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7ec9000b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,120 +0,0 @@ -# Automatically generated by nengo-bones, do not edit this file directly - -language: python -python: 3.8 -notifications: - email: - on_success: change - on_failure: change - slack: - if: branch = master - on_pull_requests: false - on_success: change - on_failure: always - rooms: - - secure: "wZ7l/X7cVeetmwfup7vCeN74pqFGMC5eaJfy/aqRwVGCbY3aHQKoqJaBBrVefc+DsJwPPM9HIOGs7jkPY+Y1pFbklAhWCDCvmc+f3fL4/yPWK1u7r8IIHhM3O0YvYrEHfFfZn+V1nAomx1/wttFOr07LLffaNOn+sFzXgKP1Ebv5MrlCNGY0q3T2A38pwXus60GXaGalI/I/a/vjRdkEmq38eIMqLl0v94oCy67Gqb/5mlB8eT6SffLGoFCctNpC9G9jyh/eRQp+B+YwksjZz+SOQTIy/mOlk8fU1ZL4f9wPguXheKznckuvX+J0o+o5kspyrQveWW2lo1VjmnFfdifCVjTj4AIcmc60Zb2dMzzBcfcA+zeVlhWkWbB65LuuXkfxxsN97EwP5KVH+Lw7c2ycUyHV8XvKHNzVRLIUuVeOTKLHgGpkVgz/WasT5yBOEbnAyZxxAHQpMbXQr9POwaq92Kwb7du0sLcsP4agdd2L0YACb4JdwQeeD1c8h6RrXua2ejBJ41e+9ZkZcZGad9P6YVmxvI+hdddjdzzvBWq6+B/Ubft9AqQXmn4pg0fKkZLjrzsSYGRhXzINxhVBjZD8b5BQIb/Aeix3IFhLmDdSQMhjdETAgL3jSd9D5t1G5QNp4AdbitqcH+yNoXzZCXye76hrwYr6yrHi3JN9yrM=" -cache: pip - -dist: xenial - -env: - global: - - SCRIPT="test" - - TEST_ARGS="" - - BRANCH_NAME="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}" - - TF_VERSION="tensorflow" - -jobs: - include: - - - env: - SCRIPT="static" - - - env: - SCRIPT="remote-test" - - - env: - TF_VERSION="tensorflow==2.1.0" - SCRIPT="test" - python: 3.6 - - - env: - TF_VERSION="tensorflow~=2.6.0" - SCRIPT="test" - - - env: - SCRIPT="remote-docs" - - - env: - SCRIPT="remote-examples" - - stage: deploy - if: branch =~ ^release-candidate-* OR tag =~ ^v[0-9]* - env: SCRIPT="deploy" - cache: false - deploy: - - provider: pypi - server: https://test.pypi.org/legacy/ - user: __token__ - password: $PYPI_TEST_TOKEN - distributions: "sdist bdist_wheel " - on: - all_branches: true - tags: false - condition: $TRAVIS_BRANCH =~ ^release-candidate-* - - provider: pypi - user: __token__ - password: $PYPI_TOKEN - distributions: "sdist bdist_wheel " - on: - all_branches: true - tags: true - condition: $TRAVIS_TAG =~ ^v[0-9]* - -before_install: - # export travis_terminate for use in scripts, from here: - # https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/bash/travis_terminate.bash - - export -f travis_terminate - _travis_terminate_agent - _travis_terminate_freebsd - _travis_terminate_linux - _travis_terminate_osx - _travis_terminate_unix - _travis_terminate_windows - # upgrade pip - - pip install pip setuptools --upgrade - # fixes setuptools bug, see https://github.com/pypa/setuptools/issues/3293 - - pip install "importlib-metadata>=0.21" - # install/run nengo-bones - - pip install git+https://github.com/nengo/nengo-bones#egg=nengo-bones - - bones-generate --output-dir .ci ci-scripts - - bones-check --verbose - # display environment info - - pip freeze - -install: - - .ci/$SCRIPT.sh install - - pip freeze - -before_script: - - .ci/$SCRIPT.sh before_script - -script: - - .ci/$SCRIPT.sh script - -before_cache: - - .ci/$SCRIPT.sh before_cache - -after_success: - - .ci/$SCRIPT.sh after_success - -after_failure: - - .ci/$SCRIPT.sh after_failure - -before_deploy: - - .ci/$SCRIPT.sh before_deploy - -after_deploy: - - .ci/$SCRIPT.sh after_deploy - -after_script: - - .ci/$SCRIPT.sh after_script diff --git a/CHANGES.rst b/CHANGES.rst index 085ebab6..6dc6ae3b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -22,6 +22,15 @@ Release history 0.4.3 (unreleased) ================== +*Compatible with TensorFlow 2.1 - 2.10* + +**Added** + +- Layers are registered with the Keras serialization system (no longer need to + be passed as ``custom_objects``). (`#49`_) + +.. _#49: https://github.com/nengo/keras-lmu/pull/49 + 0.4.2 (May 17, 2022) ==================== diff --git a/MANIFEST.in b/MANIFEST.in index 79887354..3925070d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -24,6 +24,7 @@ prune .github prune .tox prune .eggs prune .ci +prune bones-scripts # Exclude auto-generated files recursive-exclude docs *.py diff --git a/docs/conf.py b/docs/conf.py index b3586bbb..14bd9448 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -105,7 +105,36 @@ html_theme_options = { "nengo_logo": "general-full-light.svg", "nengo_logo_color": "#a8acaf", - "tagmanager_id": "GTM-KWCR2HN", + "analytics": """ + + + + + + + + """, } html_redirects = [ ("getting_started.html", "getting-started.html"), diff --git a/keras_lmu/layers.py b/keras_lmu/layers.py index bafce9f4..e5efd983 100644 --- a/keras_lmu/layers.py +++ b/keras_lmu/layers.py @@ -1,6 +1,4 @@ -""" -Core classes for the KerasLMU package. -""" +"""Core classes for the KerasLMU package.""" import numpy as np import tensorflow as tf @@ -20,6 +18,7 @@ from keras.engine.base_layer import BaseRandomLayer +@tf.keras.utils.register_keras_serializable("keras-lmu") class LMUCell(DropoutRNNCellMixin, BaseRandomLayer): """ Implementation of LMU cell (to be used within Keras RNN wrapper). @@ -179,8 +178,8 @@ def theta(self): """ Value of the ``theta`` parameter. - If ``trainable_theta=True`` this returns the trained value, not the initial - value passed in to the constructor. + If ``trainable_theta=True`` this returns the trained value, not the + initial value passed in to the constructor. """ if self.built: return 1 / tf.keras.backend.get_value(self.theta_inv) @@ -444,6 +443,7 @@ def from_config(cls, config): return super().from_config(config) +@tf.keras.utils.register_keras_serializable("keras-lmu") class LMU(tf.keras.layers.Layer): """ A layer of trainable low-dimensional delay systems. @@ -581,8 +581,8 @@ def theta(self): """ Value of the ``theta`` parameter. - If ``trainable_theta=True`` this returns the trained value, not the initial - value passed in to the constructor. + If ``trainable_theta=True`` this returns the trained value, not the + initial value passed in to the constructor. """ if self.built: @@ -706,6 +706,7 @@ def from_config(cls, config): return super().from_config(config) +@tf.keras.utils.register_keras_serializable("keras-lmu") class LMUFeedforward(tf.keras.layers.Layer): """ Layer class for the feedforward variant of the LMU. diff --git a/keras_lmu/tests/test_benchmarks.py b/keras_lmu/tests/test_benchmarks.py index d58de82e..dffb1832 100644 --- a/keras_lmu/tests/test_benchmarks.py +++ b/keras_lmu/tests/test_benchmarks.py @@ -38,7 +38,7 @@ def on_predict_batch_end(self, batch, logs=None): @pytest.mark.skipif(not tf_gpu_installed, reason="Very slow on CPU") @pytest.mark.parametrize( "mode, min_time, max_time", - [("rnn", 0.1, 0.2), ("fft", 0.1, 0.2), ("raw", 0.05, 0.15)], + [("rnn", 0.1, 0.2), ("fft", 0.05, 0.15), ("raw", 0.05, 0.15)], ) def test_performance(mode, min_time, max_time): # performance is based on Azure NC6 VM diff --git a/keras_lmu/tests/test_layers.py b/keras_lmu/tests/test_layers.py index 1fe4a96a..7590a21e 100644 --- a/keras_lmu/tests/test_layers.py +++ b/keras_lmu/tests/test_layers.py @@ -82,7 +82,9 @@ def test_layer_vs_cell(rng, has_input_kernel, feedforward, discretizer): kernel_initializer="glorot_uniform" if has_input_kernel else None, memory_to_memory=not feedforward, ) - hidden_cell = lambda: tf.keras.layers.SimpleRNNCell(units=64) + + def hidden_cell(): + return tf.keras.layers.SimpleRNNCell(units=64) inp = rng.uniform(-1, 1, size=(2, n_steps, input_d)) @@ -202,14 +204,7 @@ def test_save_load_serialization(mode, tmp_path, trainable_theta, discretizer): model.save(str(tmp_path)) - model_load = tf.keras.models.load_model( - str(tmp_path), - custom_objects={ - "LMUCell": layers.LMUCell, - "LMU": layers.LMU, - "LMUFeedforward": layers.LMUFeedforward, - }, - ) + model_load = tf.keras.models.load_model(str(tmp_path)) assert np.allclose( model.predict(np.ones((32, 10, 32))), model_load.predict(np.ones((32, 10, 32))) @@ -634,9 +629,7 @@ def test_theta_update(discretizer, trainable_theta, tmp_path): # save model and make sure you get same outputs, that is, correct theta was stored model.save(str(tmp_path)) - model_load = tf.keras.models.load_model( - str(tmp_path), custom_objects={"LMUCell": layers.LMUCell} - ) + model_load = tf.keras.models.load_model(str(tmp_path)) assert np.allclose( model.predict(np.ones((32, 10, 20))), diff --git a/keras_lmu/version.py b/keras_lmu/version.py index b4f5f83e..260760de 100644 --- a/keras_lmu/version.py +++ b/keras_lmu/version.py @@ -1,13 +1,14 @@ # Automatically generated by nengo-bones, do not edit this file directly -# pylint: disable=consider-using-f-string +# pylint: disable=consider-using-f-string,bad-string-format-type -"""KerasLMU version information. +""" +KerasLMU version information. -We use semantic versioning (see http://semver.org/) -and conform to PEP440 (see https://www.python.org/dev/peps/pep-0440/). -'.dev0' will be added to the version unless the code base represents -a release version. Release versions are git tagged with the version. +We use semantic versioning (see http://semver.org/) and conform to PEP440 (see +https://www.python.org/dev/peps/pep-0440/). '.dev0' will be added to the version +unless the code base represents a release version. Release versions are git +tagged with the version. """ version_info = (0, 4, 3) diff --git a/pyproject.toml b/pyproject.toml index 84fbf08a..b5c59e83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,8 @@ target-version = ['py36'] [tool.isort] profile = "black" src_paths = ["keras_lmu"] + +[tool.docformatter] +wrap-summaries = 88 +wrap-descriptions = 81 +pre-summary-newline = true diff --git a/setup.cfg b/setup.cfg index a9395c39..ba859c21 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,7 @@ all_files = 1 [coverage:run] source = ./ +relative_files = True [coverage:report] # Regexes for lines to exclude from consideration @@ -59,7 +60,6 @@ disable = arguments-differ, assignment-from-no-return, attribute-defined-outside-init, - bad-continuation, blacklisted-name, comparison-with-callable, duplicate-code, @@ -73,7 +73,6 @@ disable = no-else-return, no-member, no-name-in-module, - no-self-use, not-an-iterable, not-context-manager, protected-access,