diff --git a/.github/utilities/run_examples.sh b/.github/utilities/run_examples.sh index 12532d87..eb923381 100755 --- a/.github/utilities/run_examples.sh +++ b/.github/utilities/run_examples.sh @@ -7,7 +7,6 @@ CMD="jupyter nbconvert --to notebook --inplace --execute --ExecutePreprocessor.t excluded=( "tsml_eval/publications/y2023/distance_based_clustering/package_distance_timing.ipynb" - "examples/regression_experiments.ipynb" ) if [ "$1" = true ]; then excluded+=() diff --git a/.github/workflows/issue_comment_edited.yml b/.github/workflows/issue_comment_edited.yml index 4cd61d3d..ad43d332 100644 --- a/.github/workflows/issue_comment_edited.yml +++ b/.github/workflows/issue_comment_edited.yml @@ -11,7 +11,7 @@ concurrency: jobs: pr-welcome-edited: if: ${{ github.event.issue.pull_request }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/create-github-app-token@v1 diff --git a/.github/workflows/issue_comment_posted.yml b/.github/workflows/issue_comment_posted.yml index 7403b961..799d0c13 100644 --- a/.github/workflows/issue_comment_posted.yml +++ b/.github/workflows/issue_comment_posted.yml @@ -6,7 +6,7 @@ on: jobs: self-assign: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/periodic_github_maintenace.yml b/.github/workflows/periodic_github_maintenace.yml index 4eac3146..999bdf6a 100644 --- a/.github/workflows/periodic_github_maintenace.yml +++ b/.github/workflows/periodic_github_maintenace.yml @@ -6,13 +6,17 @@ on: - cron: "0 1 1,15 * *" workflow_dispatch: -permissions: - issues: write - contents: write +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true jobs: stale_branches: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 + + permissions: + issues: write + contents: write steps: - uses: actions/create-github-app-token@v1 @@ -32,3 +36,58 @@ jobs: stale-branch-label: "stale branch" compare-branches: "info" pr-check: true + + pre-commit-auto-update: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0 + + - if: always() + uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.PR_APP_ID }} + private-key: ${{ secrets.PR_APP_KEY }} + + - if: always() + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ steps.app-token.outputs.token }} + commit-message: "Automated `pre-commit` hook update" + branch: pre-commit-hooks-update + title: "[MNT] Automated `pre-commit` hook update" + body: "Automated weekly update to `.pre-commit-config.yaml` hook versions." + labels: maintenance, full pre-commit + + github-security-scorecard: + runs-on: ubuntu-24.04 + + permissions: + security-events: write + id-token: write + + steps: + - uses: actions/checkout@v4 + + - uses: ossf/scorecard-action@v2.4.0 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - uses: actions/upload-artifact@v4 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.github/workflows/periodic_tests.yml b/.github/workflows/periodic_tests.yml index 9fb6c6b1..1098fbb6 100644 --- a/.github/workflows/periodic_tests.yml +++ b/.github/workflows/periodic_tests.yml @@ -12,7 +12,7 @@ concurrency: jobs: check-manifest: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -26,7 +26,7 @@ jobs: extra_args: check-manifest --hook-stage manual pre-commit: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -65,7 +65,7 @@ jobs: run: mypy tsml_eval/ run-notebook-examples: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -99,13 +99,74 @@ jobs: path: ${{ github.workspace }}/.numba_cache key: numba-run-notebook-examples-${{ runner.os }}-3.10-${{ env.CURRENT_DATE }} + test-no-soft-deps: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Use numba cache to set env variables but not restore cache + uses: ./.github/actions/numba_cache + with: + cache_name: "test-no-soft-deps" + runner_os: ${{ runner.os }} + python_version: "3.10" + restore_cache: "false" + + - name: Install + uses: nick-fields/retry@v3 + with: + timeout_minutes: 30 + max_attempts: 3 + command: python -m pip install .[dev] + + - name: Show dependencies + run: python -m pip list + + - name: Run tests + run: python -m pytest -n logical + + - name: Save new cache + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/.numba_cache + # Save cache with the current date (ENV set in numba_cache action) + key: numba-test-no-soft-deps-${{ runner.os }}-3.10-${{ env.CURRENT_DATE }} + + test-core-imports: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install + uses: nick-fields/retry@v3 + with: + timeout_minutes: 30 + max_attempts: 3 + command: python -m pip install . + + - name: Show dependencies + run: python -m pip list + + - name: Run import test + run: python tsml_eval/testing/tests/test_core_imports.py + pytest: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, macos-14, windows-2022 ] + os: [ ubuntu-24.04, macos-14, windows-2022 ] python-version: [ "3.9", "3.10", "3.11", "3.12" ] steps: @@ -143,7 +204,7 @@ jobs: key: numba-pytest-${{ runner.os }}-${{ matrix.python-version}}-${{ env.CURRENT_DATE }} codecov: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr_examples.yml b/.github/workflows/pr_examples.yml index 768bc554..62a1f3b1 100644 --- a/.github/workflows/pr_examples.yml +++ b/.github/workflows/pr_examples.yml @@ -19,7 +19,7 @@ concurrency: jobs: run-notebook-examples: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr_opened.yml b/.github/workflows/pr_opened.yml index 23413ea9..34e32bf0 100644 --- a/.github/workflows/pr_opened.yml +++ b/.github/workflows/pr_opened.yml @@ -12,7 +12,7 @@ permissions: jobs: # based on the scikit-learn 1.3.1 PR labelers labeler: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr_precommit.yml b/.github/workflows/pr_precommit.yml index 2e1e98c8..b330e01b 100644 --- a/.github/workflows/pr_precommit.yml +++ b/.github/workflows/pr_precommit.yml @@ -14,7 +14,7 @@ concurrency: jobs: pre-commit: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/create-github-app-token@v1 diff --git a/.github/workflows/pr_pytest.yml b/.github/workflows/pr_pytest.yml index e0d0188e..ac7b847b 100644 --- a/.github/workflows/pr_pytest.yml +++ b/.github/workflows/pr_pytest.yml @@ -17,13 +17,66 @@ concurrency: cancel-in-progress: true jobs: + test-no-soft-deps: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Restore numba cache + uses: ./.github/actions/numba_cache + with: + cache_name: "test-no-soft-deps" + runner_os: ${{ runner.os }} + python_version: "3.10" + + - name: Install + uses: nick-fields/retry@v3 + with: + timeout_minutes: 30 + max_attempts: 3 + command: python -m pip install .[dev] + + - name: Show dependencies + run: python -m pip list + + - name: Run tests + run: python -m pytest -n logical + + test-core-imports: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install + uses: nick-fields/retry@v3 + with: + timeout_minutes: 30 + max_attempts: 3 + command: python -m pip install . + + - name: Show dependencies + run: python -m pip list + + - name: Run import test + run: python tsml_eval/testing/tests/test_core_imports.py + pytest: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, windows-2022 ] + os: [ ubuntu-24.04, windows-2022 ] python-version: [ "3.9", "3.10", "3.11", "3.12" ] # skip python versions unless the PR has the 'full pytest actions' label pr-testing: @@ -60,7 +113,7 @@ jobs: run: python -m pytest -n logical codecov: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr_typecheck.yml b/.github/workflows/pr_typecheck.yml index c38f5e22..2dc40af4 100644 --- a/.github/workflows/pr_typecheck.yml +++ b/.github/workflows/pr_typecheck.yml @@ -9,6 +9,7 @@ on: - main paths: - "tsml_eval/**" + - "pyproject.toml" concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} diff --git a/.github/workflows/precommit_autoupdate.yml b/.github/workflows/precommit_autoupdate.yml deleted file mode 100644 index b5c26cc8..00000000 --- a/.github/workflows/precommit_autoupdate.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Update pre-commit Hooks - -on: - schedule: - # every Monday at 12:30 AM UTC - - cron: "30 0 * * 1" - workflow_dispatch: - -jobs: - pre-commit-auto-update: - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - uses: browniebroke/pre-commit-autoupdate-action@v1.0.0 - - - if: always() - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ vars.PR_APP_ID }} - private-key: ${{ secrets.PR_APP_KEY }} - - - if: always() - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ steps.app-token.outputs.token }} - commit-message: "Automated `pre-commit` hook update" - branch: pre-commit-hooks-update - title: "[MNT] Automated `pre-commit` hook update" - body: "Automated weekly update to `.pre-commit-config.yaml` hook versions." - labels: maintenance, full pre-commit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88f2c8d2..2d3b3ea4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: jobs: check-manifest: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -23,7 +23,7 @@ jobs: build-project: needs: check-manifest - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -51,7 +51,7 @@ jobs: strategy: matrix: - os: [ ubuntu-20.04, macos-14, windows-2022 ] + os: [ ubuntu-24.04, macos-14, windows-2022 ] python-version: [ "3.9", "3.10", "3.11", "3.12" ] steps: @@ -93,7 +93,7 @@ jobs: upload-wheels: needs: test-wheels - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - uses: actions/download-artifact@v4 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml deleted file mode 100644 index 0f992251..00000000 --- a/.github/workflows/scorecard.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Scorecard supply-chain security - -on: - branch_protection_rule: - schedule: - - cron: '30 1 * * 6' - push: - branches: - - main - -permissions: read-all - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: ossf/scorecard-action@v2.4.0 - with: - results_file: results.sarif - results_format: sarif - publish_results: true - - - uses: actions/upload-artifact@v4 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif diff --git a/conftest.py b/conftest.py index 8ae73d9b..03962c27 100644 --- a/conftest.py +++ b/conftest.py @@ -1,7 +1,5 @@ """Main configuration file for pytest.""" -__author__ = ["MatthewMiddlehurst"] - import shutil from tsml_eval.experiments import experiments diff --git a/examples/classification_experiments.ipynb b/examples/classification_experiments.ipynb index 9bf45959..550607f0 100644 --- a/examples/classification_experiments.ipynb +++ b/examples/classification_experiments.ipynb @@ -35,8 +35,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:27.561711Z", - "start_time": "2024-04-25T11:13:25.110267Z" + "end_time": "2024-12-05T22:45:19.997167Z", + "start_time": "2024-12-05T22:45:18.534057Z" } }, "outputs": [], @@ -51,8 +51,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:27.567720Z", - "start_time": "2024-04-25T11:13:27.562709Z" + "end_time": "2024-12-05T22:45:20.103552Z", + "start_time": "2024-12-05T22:45:20.100613Z" } }, "outputs": [], @@ -83,8 +83,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:28.625866Z", - "start_time": "2024-04-25T11:13:27.568693Z" + "end_time": "2024-12-05T22:45:21.063870Z", + "start_time": "2024-12-05T22:45:20.108557Z" } }, "outputs": [], @@ -116,8 +116,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:28.663766Z", - "start_time": "2024-04-25T11:13:28.626864Z" + "end_time": "2024-12-05T22:45:21.076788Z", + "start_time": "2024-12-05T22:45:21.070905Z" } }, "outputs": [ @@ -164,8 +164,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:28.737595Z", - "start_time": "2024-04-25T11:13:28.664765Z" + "end_time": "2024-12-05T22:45:21.367747Z", + "start_time": "2024-12-05T22:45:21.085295Z" } }, "outputs": [ @@ -198,8 +198,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:28.902138Z", - "start_time": "2024-04-25T11:13:28.738565Z" + "end_time": "2024-12-05T22:45:21.473783Z", + "start_time": "2024-12-05T22:45:21.381117Z" } }, "outputs": [ @@ -240,8 +240,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:28.913109Z", - "start_time": "2024-04-25T11:13:28.903135Z" + "end_time": "2024-12-05T22:45:21.482812Z", + "start_time": "2024-12-05T22:45:21.476787Z" } }, "outputs": [ @@ -337,8 +337,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:13:29.044784Z", - "start_time": "2024-04-25T11:13:28.914106Z" + "end_time": "2024-12-05T22:45:21.554943Z", + "start_time": "2024-12-05T22:45:21.513153Z" } }, "outputs": [ @@ -347,7 +347,7 @@ "text/plain": [ "
" ], - "image/png": "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t3aHw6GKFSu69YmKisq3jLy2kJCQQtULAFZx5MgRjRkzRps3b5bDkf9jNyMjQ7m5uQUePw8dOiTp+vHPz89PwcHB+frcePwsaBl5bfeqb775Rn369NGvv/4qh8OhRo0aqWXLlsrJydEPP/ygBQsWaMGCBWrcuLHrhNHRo0fVvHlzSVLFihUVGxurkJAQHT16VGvWrNGaNWuUlJSkJUuWuJ0gsLIiB7aBAwfq+eefd5tmjNHXX3+t119/XRs3blSzZs20ffv2fB/A8K6ffvpJr732mlavXq2AgABvlwMA963c3Fw999xzeuedd1S3bl1vl3PPWbFihbp166bc3Fy9+OKLmjhxYr4//I8fP673339fixYtck2z2Wx69NFHNWrUKLVv314+Pj6uto0bN6pLly5avny5EhMT75lr/Ir1pgObzabHHntM3377rerUqaOTJ09q4MCBxbkKFINdu3bp1KlTevjhh+VwOORwOLRx40ZNnz5dDodDubm5+eapWrVqvruRTp48qapVq7ra86bdqs+NF9BKUk5Ojs6cOePWp6Bl3LgOALhXXLhwQd99950SEhJcx9t3331Xe/fulcPh0Lp161SpUiX5+Pjc9viZlZWlzMzMW/a5n46fp0+fVp8+fZSbm6thw4bpk08+yRfWJKlWrVqaNWuW2926v/vd77R27Vp16tTJLaxJUnx8vMaMGSNJmj9/vke3oTh55C7R4OBgffDBB5KkdevWadeuXa62vGvfbibve+wNGzbcdPqOHTvUpUsXhYaGqnz58oqPj9fmzZtdfVetWqW2bdsqJCREgYGBat++vXbv3p1vXWlpabLZbIqMjJTT6dT06dPVoEEDlS1bVtWqVdPgwYN15swZSdK1a9c0YcIE1atXT2XKlFF4eLhee+01Xbp0yW2Z/fv3l81m08SJE2+6jYsWLZLNZlOTJk1u2seT2rZtq9TUVKWkpLhesbGx6t27t1JSUvL9cktS06ZNtXbtWrdpq1evVtOmTSVJUVFRqlq1qluf8+fPKzk52dWnadOmyszMdPt9WLdunZxOp+Li4lx9Nm3apOzsbLf1REdH83UogHtOUFBQvuPt4MGDFR0drZSUFMXFxcnPz0+NGjVyO346nU6tXbvWdfxs1KiRfH193focPnxYx48fdzvGpqamuv1hvHr1agUFBemBBx4ooS0uPjNmzFBm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/z33MNZbqV+/vh599FEdPHhQX331lYKCgtSvXz9vlwUAgOWFhoZq/vz5stvt+vDDDzVw4MACr2c7ceKEEhIS1L17d7fps2fP1quvvnpfhDXpLs6wzZkzx/U0gmvXrikjI0O7d+92PRmgdevWmjt3riIiItzm++Mf/6jFixdr5cqVqlu3rho3bqxff/1VO3fuVPPmzdWsWTNt27at6FtkMcOGDdO6deskXX8KQt6NDAAA4Na6du2q5cuXq1+/fvrkk080b948xcbGKiIiwvXw971798oYo0ceecQ1X0pKil5++WUZY1S7dm0tXrxYixcvLnAdn332WQltzd0pcmDbunWrtm7dKkkqV66cKlSooPr16ys2NlY9evRQ48aNC5wvKipK27Zt01tvvaX169dr+fLlioyM1Jtvvql/+7d/U/v27YtakiW1bdtWPj4+cjqdxfJ1KAAApUnnzp117NgxzZkzRytXrlRqaqp2794th8OhGjVqqHfv3urZs6fbsF6ZmZmub+wOHTqkQ4cO3XT590pgs5n76TtIC5ozZ45eeukldejQQf/93//t7XIAFCA7O1t+fn4evQuuJNYB4P7FGA8edOnSJddD4EeMGOHlagAAwL2qZO/RLSUmT56s/fv3a8uWLTp69Kg6depU4JMUAAAACoPA5gErVqzQxo0bValSJT3//POaNm2at0sCAAD3MK5hA1DqcQ0bAKvjGrZSauLEiWrcuLHKly+vKlWqqHv37jp8+HCh509KSpLNZss37o0xRmPHjlW1atVUpkwZtWvXTkeOHHHrc+bMGfXu3VtBQUEKDg7WgAEDdPHiRbc++/btU8uWLRUQEKCaNWtq0qRJRd5WACguRTl2zp49Wy1btlRISIhCQkLUrl07ffvtt259OHbidghspdTGjRs1ZMgQ7dixQ6tXr1Z2drY6dOigS5cu3XbetLQ0jRw5Mt+I0pI0adIkTZ8+XbNmzVJycrLKlSunjh076urVq64+vXv31vfff6/Vq1dr+fLl2rRpkwYNGuRqP3/+vDp06KCIiAjt2rVLkydP1vjx4/XXv/61eDYeAIqoKMfODRs2qFevXlq/fr22b9+umjVrqkOHDjpx4oSrD8dO3JYBjDGnTp0ykszGjRtv2S8nJ8c0a9bMzJkzx/Tv399069bN1eZ0Ok3VqlXN5MmTXdMyMzONv7+/+fzz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IyNDu3fv1pkzZyRJrVu31ty5cxUREVGk5U+ZMkUVK1bU+PHjNWLECI0fP9718PeLFy9q3759rs/60aNHF8cmlbhSEdgGDBigqKgorV27VsnJyTpw4IBOnjwph8Oh8PBw9erVS/369bvpbbt3qnPnzjp27JjmzJmjlStXKjU1Vbt375bD4VCNGjXUu3dv9ezZU4899lixrA8AACvbunWrtm7dKkkqV66cKlSooPr16ys2NlY9evRQ48aN73odf/zjH9W7d299/PHHWrNmjfbs2aNz586pXLlyql27trp166b+/fsrJibmrtflDbZ/DhoHAAAAiyoV17ABAADcywhsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4v4P0hF4BJcwTmEAAAAASUVORK5CYII=" + "image/png": "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t3aHw6GKFSu69YmKisq3jLy2kJCQQtULAFZx5MgRjRkzRps3b5bDkf9jNyMjQ7m5uQUePw8dOiTp+vHPz89PwcHB+frcePwsaBl5bfeqb775Rn369NGvv/4qh8OhRo0aqWXLlsrJydEPP/ygBQsWaMGCBWrcuLHrhNHRo0fVvHlzSVLFihUVGxurkJAQHT16VGvWrNGaNWuUlJSkJUuWuJ0gsLIiB7aBAwfq+eefd5tmjNHXX3+t119/XRs3blSzZs20ffv2fB/A8K6ffvpJr732mlavXq2AgABvlwMA963c3Fw999xzeuedd1S3bl1vl3PPWbFihbp166bc3Fy9+OKLmjhxYr4//I8fP673339fixYtck2z2Wx69NFHNWrUKLVv314+Pj6uto0bN6pLly5avny5EhMT75lr/Ir1pgObzabHHntM3377rerUqaOTJ09q4MCBxbkKFINdu3bp1KlTevjhh+VwOORwOLRx40ZNnz5dDodDubm5+eapWrVqvruRTp48qapVq7ra86bdqs+NF9BKUk5Ojs6cOePWp6Bl3LgOALhXXLhwQd99950SEhJcx9t3331Xe/fulcPh0Lp161SpUiX5+Pjc9viZlZWlzMzMW/a5n46fp0+fVp8+fZSbm6thw4bpk08+yRfWJKlWrVqaNWuW2926v/vd77R27Vp16tTJLaxJUnx8vMaMGSNJmj9/vke3oTh55C7R4OBgffDBB5KkdevWadeuXa62vGvfbibve+wNGzbcdPqOHTvUpUsXhYaGqnz58oqPj9fmzZtdfVetWqW2bdsqJCREgYGBat++vXbv3p1vXWlpabLZbIqMjJTT6dT06dPVoEEDlS1bVtWqVdPgwYN15swZSdK1a9c0YcIE1atXT2XKlFF4eLhee+01Xbp0yW2Z/fv3l81m08SJE2+6jYsWLZLNZlOTJk1u2seT2rZtq9TUVKWkpLhesbGx6t27t1JSUvL9cktS06ZNtXbtWrdpq1evVtOmTSVJUVFRqlq1qluf8+fPKzk52dWnadOmyszMdPt9WLdunZxOp+Li4lx9Nm3apOzsbLf1REdH83UogHtOUFBQvuPt4MGDFR0drZSUFMXFxcnPz0+NGjVyO346nU6tXbvWdfxs1KiRfH193focPnxYx48fdzvGpqamuv1hvHr1agUFBemBBx4ooS0uPjNmzFBm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/z33MNZbqV+/vh599FEdPHhQX331lYKCgtSvXz9vlwUAgOWFhoZq/vz5stvt+vDDDzVw4MACr2c7ceKEEhIS1L17d7fps2fP1quvvnpfhDXpLs6wzZkzx/U0gmvXrikjI0O7d+92PRmgdevWmjt3riIiItzm++Mf/6jFixdr5cqVqlu3rho3bqxff/1VO3fuVPPmzdWsWTNt27at6FtkMcOGDdO6deskXX8KQt6NDAAA4Na6du2q5cuXq1+/fvrkk080b948xcbGKiIiwvXw971798oYo0ceecQ1X0pKil5++WUZY1S7dm0tXrxYixcvLnAdn332WQltzd0pcmDbunWrtm7dKkkqV66cKlSooPr16ys2NlY9evRQ48aNC5wvKipK27Zt01tvvaX169dr+fLlioyM1Jtvvql/+7d/U/v27YtakiW1bdtWPj4+cjqdxfJ1KAAApUnnzp117NgxzZkzRytXrlRqaqp2794th8OhGjVqqHfv3urZs6fbsF6ZmZmub+wOHTqkQ4cO3XT590pgs5n76TtIC5ozZ45eeukldejQQf/93//t7XIAFCA7O1t+fn4evQuuJNYB4P7FGA8edOnSJddD4EeMGOHlagAAwL2qZO/RLSUmT56s/fv3a8uWLTp69Kg6depU4JMUAAAACoPA5gErVqzQxo0bValSJT3//POaNm2at0sCAAD3MK5hA1DqcQ0bAKvjGrZSauLEiWrcuLHKly+vKlWqqHv37jp8+HCh509KSpLNZss37o0xRmPHjlW1atVUpkwZtWvXTkeOHHHrc+bMGfXu3VtBQUEKDg7WgAEDdPHiRbc++/btU8uWLRUQEKCaNWtq0qRJRd5WACguRTl2zp49Wy1btlRISIhCQkLUrl07ffvtt259OHbidghspdTGjRs1ZMgQ7dixQ6tXr1Z2drY6dOigS5cu3XbetLQ0jRw5Mt+I0pI0adIkTZ8+XbNmzVJycrLKlSunjh076urVq64+vXv31vfff6/Vq1dr+fLl2rRpkwYNGuRqP3/+vDp06KCIiAjt2rVLkydP1vjx4/XXv/61eDYeAIqoKMfODRs2qFevXlq/fr22b9+umjVrqkOHDjpx4oSrD8dO3JYBjDGnTp0ykszGjRtv2S8nJ8c0a9bMzJkzx/Tv399069bN1eZ0Ok3VqlXN5MmTXdMyMzONv7+/+fzz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IyNDu3fv1pkzZyRJrVu31ty5cxUREVGk5U+ZMkUVK1bU+PHjNWLECI0fP9718PeLFy9q3759rs/60aNHF8cmlbhSEdgGDBigqKgorV27VsnJyTpw4IBOnjwph8Oh8PBw9erVS/369bvpbbt3qnPnzjp27JjmzJmjlStXKjU1Vbt375bD4VCNGjXUu3dv9ezZU4899lixrA8AACvbunWrtm7dKkkqV66cKlSooPr16ys2NlY9evRQ48aN73odf/zjH9W7d299/PHHWrNmjfbs2aNz586pXLlyql27trp166b+/fsrJibmrtflDbZ/DhoHAAAAiyoV17ABAADcywhsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4ghsAAAAFkdgAwAAsDgCGwAAgMUR2AAAACyOwAYAAGBxBDYAAACLI7ABAABYHIENAADA4v4P0hF4BJcwTmEAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" diff --git a/examples/clustering_experiments.ipynb b/examples/clustering_experiments.ipynb index a6205916..d75ad7b4 100644 --- a/examples/clustering_experiments.ipynb +++ b/examples/clustering_experiments.ipynb @@ -18,8 +18,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2024-04-25T11:12:24.583146Z", - "start_time": "2024-04-25T11:12:22.271318Z" + "end_time": "2024-12-05T22:45:23.706541Z", + "start_time": "2024-12-05T22:45:22.433686Z" } }, "source": [ @@ -51,8 +51,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:24.589130Z", - "start_time": "2024-04-25T11:12:24.584144Z" + "end_time": "2024-12-05T22:45:23.712661Z", + "start_time": "2024-12-05T22:45:23.709646Z" } }, "outputs": [], @@ -81,8 +81,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.618098Z", - "start_time": "2024-04-25T11:12:24.590129Z" + "end_time": "2024-12-05T22:45:24.726972Z", + "start_time": "2024-12-05T22:45:23.796765Z" } }, "outputs": [], @@ -113,8 +113,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.636028Z", - "start_time": "2024-04-25T11:12:25.620076Z" + "end_time": "2024-12-05T22:45:24.739641Z", + "start_time": "2024-12-05T22:45:24.733123Z" } }, "outputs": [ @@ -122,11 +122,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.\n", - " 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n", - "0.5\n", - "0.48717948717948717\n", - "0.0\n" + "[0. 0. 1. 1. 0. 0. 1. 1. 1. 0. 1. 1. 1. 1. 1. 0. 1. 0. 1. 1. 0. 1. 0. 0.\n", + " 1. 1. 0. 0. 0. 0. 1. 1. 0. 0. 0. 1. 0. 1. 1. 1.]\n", + "0.65\n", + "0.5333333333333333\n", + "0.0462008291815135\n" ] } ], @@ -163,8 +163,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.700819Z", - "start_time": "2024-04-25T11:12:25.637032Z" + "end_time": "2024-12-05T22:45:24.778422Z", + "start_time": "2024-12-05T22:45:24.744978Z" } }, "outputs": [ @@ -200,8 +200,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.878754Z", - "start_time": "2024-04-25T11:12:25.701817Z" + "end_time": "2024-12-05T22:45:24.840393Z", + "start_time": "2024-12-05T22:45:24.783015Z" } }, "outputs": [ @@ -242,8 +242,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.889693Z", - "start_time": "2024-04-25T11:12:25.879721Z" + "end_time": "2024-12-05T22:45:24.852592Z", + "start_time": "2024-12-05T22:45:24.844889Z" } }, "outputs": [ @@ -339,8 +339,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-04-25T11:12:25.975471Z", - "start_time": "2024-04-25T11:12:25.890691Z" + "end_time": "2024-12-05T22:45:24.910913Z", + "start_time": "2024-12-05T22:45:24.873439Z" } }, "outputs": [ @@ -349,7 +349,7 @@ "text/plain": [ "
" ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv0AAAD6CAYAAAAyXPiFAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA6xElEQVR4nO3deXhMd/8//udMJvsuCVnIQpWqRILINwuiErHWvjVVVKvKTVXbm6qlqh/clFu1ulH7fd9a7mpLUVuEEClBYgmXlqBqC2KJkGVevz/85tzGTIhIMsnxfFyX675z3u9z5nXOTM88z5lz3kcjIgIiIiIiIlItraULICIiIiKiisXQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1Urdy6dQu1a9eGRqPBvn37LF0OVYD169ejdevW8PLygq2tLerWrYsxY8bg+vXrli6NKsiqVavQtWtX1K5dG46OjggNDcWiRYsgIpYujSrQ77//jmHDhiE0NBQ6nQ6NGze2dElEqqazdAFEj2Pq1KkoKiqydBlUga5evYqIiAiMGjUKHh4eOHz4MD788EMcPnwYmzZtsnR5VAHmzJmDwMBAzJ49G15eXti8eTNef/11nD17FpMnT7Z0eVRBjhw5gl9++QURERHQ6/XQ6/WWLolI1TTCUylUTRw7dgzNmzfH7NmzMWzYMOzduxfNmze3dFlUCRYsWIChQ4fi3Llz8PX1tXQ5VM5ycnLg6elpNG3o0KH47rvvcO3aNWi1/FFajfR6vfLeDho0CPv27cPhw4ctXBWRenFPStXGyJEjMWzYMDRo0MDSpVAl8/DwAAAUFBRYuBKqCA8GfgAICwvDjRs3kJeXZ4GK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" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv0AAAD6CAYAAAAyXPiFAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA6xElEQVR4nO3deXhMd/8//udMJvsuCVnIQpWqRILINwuiErHWvjVVVKvKTVXbm6qlqh/clFu1ulH7fd9a7mpLUVuEEClBYgmXlqBqC2KJkGVevz/85tzGTIhIMsnxfFyX675z3u9z5nXOTM88z5lz3kcjIgIiIiIiIlItraULICIiIiKiisXQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1ERERERCrH0E9EREREpHIM/UREREREKsfQT0RERESkcgz9REREREQqx9BPRERERKRyDP1Urdy6dQu1a9eGRqPBvn37LF0OVYD169ejdevW8PLygq2tLerWrYsxY8bg+vXrli6NKsiqVavQtWtX1K5dG46OjggNDcWiRYsgIpYujSrQ77//jmHDhiE0NBQ6nQ6NGze2dElEqqazdAFEj2Pq1KkoKiqydBlUga5evYqIiAiMGjUKHh4eOHz4MD788EMcPnwYmzZtsnR5VAHmzJmDwMBAzJ49G15eXti8eTNef/11nD17FpMnT7Z0eVRBjhw5gl9++QURERHQ6/XQ6/WWLolI1TTCUylUTRw7dgzNmzfH7NmzMWzYMOzduxfNmze3dFlUCRYsWIChQ4fi3Llz8PX1tXQ5VM5ycnLg6elpNG3o0KH47rvvcO3aNWi1/FFajfR6vfLeDho0CPv27cPhw4ctXBWRenFPStXGyJEjMWzYMDRo0MDSpVAl8/DwAAAUFBRYuBKqCA8GfgAICwvDjRs3kJeXZ4GK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" }, "metadata": {}, "output_type": "display_data" diff --git a/examples/regression_experiments.ipynb b/examples/regression_experiments.ipynb index 6c2c9f08..f5f694c8 100644 --- a/examples/regression_experiments.ipynb +++ b/examples/regression_experiments.ipynb @@ -16,7 +16,11 @@ { "cell_type": "code", "metadata": { - "collapsed": true + "collapsed": true, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:13.988322Z", + "start_time": "2024-12-05T22:45:13.985299Z" + } }, "source": [ "import numpy as np\n", @@ -36,7 +40,7 @@ ")" ], "outputs": [], - "execution_count": null + "execution_count": 9 }, { "cell_type": "code", @@ -45,10 +49,14 @@ "X_test, y_test = load_minimal_gas_prices(split=\"test\")" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:14.007539Z", + "start_time": "2024-12-05T22:45:14.004467Z" + } }, "outputs": [], - "execution_count": null + "execution_count": 10 }, { "cell_type": "code", @@ -73,10 +81,14 @@ ")" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:14.965698Z", + "start_time": "2024-12-05T22:45:14.009636Z" + } }, "outputs": [], - "execution_count": null + "execution_count": 11 }, { "cell_type": "markdown", @@ -102,10 +114,29 @@ "print(rr.r2_score)" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:14.978971Z", + "start_time": "2024-12-05T22:45:14.974820Z" + } }, - "outputs": [], - "execution_count": null + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179\n", + " -0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179\n", + " -0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179 -0.32689179\n", + " -0.32689179 -0.32689179]\n", + "0.008893058227437198\n", + "0.09430301282269404\n", + "0.36624719862013666\n", + "-0.07184048625633688\n" + ] + } + ], + "execution_count": 12 }, { "cell_type": "markdown", @@ -140,25 +171,73 @@ "results" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:15.056258Z", + "start_time": "2024-12-05T22:45:14.988659Z" + } }, - "outputs": [], - "execution_count": null + "outputs": [ + { + "data": { + "text/plain": [ + "{'CardanoSentiment': 0.3002608403259928,\n", + " 'Covid3Month': 0.04471992368682529,\n", + " 'FloodModeling1': 0.018863328807814914,\n", + " 'FloodModeling2': 0.018547996598852055,\n", + " 'NaturalGasPricesSentiment': 0.09023204999410936}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 13 }, { "cell_type": "code", "source": [ - "benchmarks = [\"InceptionE\", \"FreshPRINCE\", \"DrCIF\"]\n", + "benchmarks = [\"InceptionT\", \"FreshPRINCE\", \"DrCIF\"]\n", "res = get_estimator_results(\n", " datasets=datasets, estimators=benchmarks, task=\"regression\", measure=\"rmse\"\n", ")\n", "res" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:15.207445Z", + "start_time": "2024-12-05T22:45:15.133130Z" + } }, - "outputs": [], - "execution_count": null + "outputs": [ + { + "data": { + "text/plain": [ + "{'InceptionT': {'CardanoSentiment': 0.3790249345482153,\n", + " 'Covid3Month': 0.0547486330011963,\n", + " 'FloodModeling1': 0.0066867519847921,\n", + " 'FloodModeling2': 0.0043589524619258,\n", + " 'NaturalGasPricesSentiment': 0.2406518450482171},\n", + " 'FreshPRINCE': {'CardanoSentiment': 0.2894787944853637,\n", + " 'Covid3Month': 0.0401913023459625,\n", + " 'FloodModeling1': 0.0049994162451307,\n", + " 'FloodModeling2': 0.0068567616393676,\n", + " 'NaturalGasPricesSentiment': 0.054954074837973},\n", + " 'DrCIF': {'CardanoSentiment': 0.3133879847892337,\n", + " 'Covid3Month': 0.0430093286336655,\n", + " 'FloodModeling1': 0.0060619965978605,\n", + " 'FloodModeling2': 0.0061042553512311,\n", + " 'NaturalGasPricesSentiment': 0.0534594028311273}}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 14 }, { "cell_type": "code", @@ -168,10 +247,95 @@ "table" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:15.220963Z", + "start_time": "2024-12-05T22:45:15.215379Z" + } }, - "outputs": [], - "execution_count": null + "outputs": [ + { + "data": { + "text/plain": [ + " InceptionT FreshPRINCE DrCIF Dummy\n", + "CardanoSentiment 0.379025 0.289479 0.313388 0.300261\n", + "Covid3Month 0.054749 0.040191 0.043009 0.044720\n", + "FloodModeling1 0.006687 0.004999 0.006062 0.018863\n", + "FloodModeling2 0.004359 0.006857 0.006104 0.018548\n", + "NaturalGasPricesSentiment 0.240652 0.054954 0.053459 0.090232" + ], + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
InceptionTFreshPRINCEDrCIFDummy
CardanoSentiment0.3790250.2894790.3133880.300261
Covid3Month0.0547490.0401910.0430090.044720
FloodModeling10.0066870.0049990.0060620.018863
FloodModeling20.0043590.0068570.0061040.018548
NaturalGasPricesSentiment0.2406520.0549540.0534590.090232
\n", + "
" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 15 }, { "cell_type": "code", @@ -182,10 +346,25 @@ "plt.show()" ], "metadata": { - "collapsed": false + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-12-05T22:45:15.260311Z", + "start_time": "2024-12-05T22:45:15.234269Z" + } }, - "outputs": [], - "execution_count": null + "outputs": [ + { + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAssAAAD6CAYAAABXsQr6AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAwFUlEQVR4nO3dd3xUVf7/8fekQzolIRBIACkWAkgQI2AooSkKogKRRaSKFF0Bv7CCgG2JNBVdREUQd1VEXL4ggtKSAKE3KSKyUmSp0iGUJMz5/sEv82NIbghpA+H1fDzmodx77p3PnZmcec+dM+fajDFGAAAAALJwc3UBAAAAwK2KsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICyjUJ0/f17h4eGy2WzasGGDq8tBIViwYIFiY2NVtmxZeXt7q0qVKho0aJDOnDnj6tJQSL799lu1a9dO4eHh8vX1VZ06dTRt2jQZY1xdGgrRf/7zH/Xt21d16tSRh4eH7rvvPleXBBQJD1cXgOLtzTffVEZGhqvLQCE6efKkGjRooBdffFGlS5fW9u3bNXr0aG3fvl2LFi1ydXkoBBMnTlRkZKQmTJigsmXLavHixerdu7cOHDigUaNGubo8FJIdO3bohx9+UIMGDWS322W3211dElAkbIZTASgkv/76q6KjozVhwgT17dtX69evV3R0tKvLQhH49NNP1adPHx08eFDly5d3dTkoYMePH1eZMmWclvXp00fffPONTp06JTc3vrQsjux2u+O5fe6557RhwwZt377dxVUBhY8eDYVm4MCB6tu3r2rUqOHqUlDESpcuLUlKS0tzcSUoDNcHZUmqW7euzp49q9TUVBdUhKLAhyDcqRiGgUIxe/Zsbdu2Td999502bdrk6nJQBK5cuaL09HT98ssveuONN/T4448rMjLS1WWhiKxcuVIVKlSQv7+/q0sBgALFx0QUuAsXLmjQoEH6+9//roCAAFeXgyISERGhEiVKqF69egoLC9NXX33l6pJQRFauXKmZM2dqyJAhri4FAAocYRkF7q233lJoaKi6d+/u6lJQhBYsWKBVq1bp008/1c6dO/XYY4/pypUrri4Lhey///2vOnXqpKZNm+rFF190dTkAUOAYhoECtX//fk2YMEFz5sxxTB12/vx5x3/Pnz8vPz8/V5aIQhIVFSVJiomJUf369VWnTh3NmTNHTz31lIsrQ2E5ffq02rRpo9KlS+u7775jTCuAYomwjAK1d+9epaWl6dFHH82yrmnTpmrQoIHWrFnjgspQlKKiouTp6an//Oc/ri4FheTixYtq27atzpw5o9WrVyswMNDVJQFAoSAso0DVqVNHiYmJTsu2bNmil19+WVOmTFH9+vVdVBmK0tq1a5Wenq4qVaq4uhQUgoyMDHXs2FE7d+7UihUrVKFCBVeXBACFhrCMAhUUFKQmTZpku65evXq6//77i7YgFLoOHTooOjpaUVFRKlGihH7++WeNGzdOUVFRat++vavLQyHo16+f5s+frwkTJujs2bNO3xbVrVtX3t7eLqwOheXChQtasGCBpKtD7s6ePavZs2dLkuMqnkBxxEVJUOiSkpLUtGlTLkpSTCUkJOibb77R77//LrvdrsjISHXo0EFDhgxhNpRiKjIyUvv378923d69e5kysJjat2+fKleunO26xMREyxMlwO2OsAwAAABY4KfLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsIxCEx0drfDwcOZWvoPwnN95eM7vPDznuNNwBT8UmiNHjujgwYOuLgNFiOf8zsNzfufhOcedhjPLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMlAImFrpzsNzfufhOQfuDEwdBxQCpla68/Cc33l4zoE7A2eWAQAAAAuEZQAAAMACYRkAAACwQFgGAAAALBCWAQAAAAuEZQAAAMCCzRhjXF0EiicvLy+lp6fLzc1NYWFhri6nSB0+fFh2u/2WOna73e74fze3wvmcfCsed1G5U4/90KFDMsbIZrOpfPnyri4nz659K7TZbLna5k59zjOP29PTU2lpaa4uByh0hGUUGnd3d6eABgAoPtzc3HTlyhVXlwEUOi5KgkLj4+OjS5cuyd3dXSEhIa4up0gdO3ZMV65cuaWO3W636/DhwwoLCyu0M8u34nEXlTv12I8ePaqMjAx5eHgoNDTU1eXkmTFGhw4dUvny5XN9ZvlOfc4zj9vHx8fVpQBFgjPLwB3iwoUL8vX1VWpqqkqWLOnqclBMpKeny8vLS2lpafL09HR1OXlWXI4DQMHjB34AAACABcIyAAAAYIGwDAAAAFggLAMAAAAWCMsAAACABcIyAAAAYIGwDAAAAFggLAMAAAAWCMsAAACABcIyAAAAYIGwDAAAAFggLAMAAAAWCMsAAACABcIyAAAAYIGwDAAAAFjwcHUBAArPmTNntG3bNknSpUuXJEmrVq2Sj4+PK8tCMZKRkSFJSklJkYfH7fuWYnUctWrVUmBgoKvKAnALsBljjKuLAFA4Vq5cqcaNG7u6DOC2tWLFCjVq1MjVZQBwIYZhAAAAABYIywAAAIAFhmEAxdj1Y5ZbtGihxYsXM2YZBSYjI0NNmzZVYmLibT9mObvjYMwyAMIycIe4cOGCfH19lZqaqpIlS7q6HBQT6enp8vLyUlpamjw9PV1dTp4Vl+MAUPAYhgEAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMgAAAGCBsAwAAABYICwDAAAAFgjLAAAAgAXCMorMRx99pKioKAUEBCggIEAxMTFauHChZftPP/1UjRs3VnBwsIKDgxUXF6d169Y5tTHGaOTIkQoLC1OJEiUUFxen3bt3O7U5efKkunTpooCAAAUFBalnz546f/68U5utW7eqcePG8vHxUcWKFTV27NiCO3AAyKfly5frscceU/ny5WWz2fS///u/N9zm8uXLGj58uCIiIuTt7a3IyEhNmzbNqc23336rmjVrysfHR7Vq1dKCBQuc1hdUHwvczgjLKDLh4eFKSEjQxo0btWHDBjVr1kzt2rXTjh07sm2flJSk+Ph4JSYmavXq1apYsaJatmypgwcPOtqMHTtWkyZN0pQpU7R27Vr5+vqqVatWunTpkqNNly5dtGPHDi1evFjz58/X8uXL1adPH8f6s2fPqmXLloqIiNDGjRs1btw4jR49Wp988knhPRgAcBNSU1NVu3Zt/eMf/8j1Nh07dtTSpUv12WefadeuXfr6669Vo0YNx/pVq1YpPj5ePXv21ObNm9W+fXu1b99e27dvd7QpiD4WuO0ZwIWCg4PN1KlTc9U2IyPD+Pv7mxkzZhhjjLHb7aZcuXJm3LhxjjanT5823t7e5uuvvzbGGPPLL78YSWb9+vWONgsXLjQ2m80cPHjQGGPM5MmTTXBwsLl8+bKjzdChQ02NGjXyfXy3ktTUVCPJpKamuroUFCNpaWlGkklLS3N1KflyOx2HJDNnzpwc2yxcuNAEBgaaEydOWLbp2LGjefTRR52WNWjQwDz//PPGmILrY4Hb3U2fWY6MjJTNZtPnn39eoKH9djV69GjZbDaNHj3aJff/3HPPyWaz3fRt3759Lqk305UrVzRz5kylpqYqJiYmV9tcuHBB6enpKlWqlCRp7969OnLkiOLi4hxtAgMD1aBBA61evVqStHr1agUFBSk6OtrRJi4uTm5ublq7dq2jzcMPPywvLy9Hm1atWmnXrl06depUvo8VAIravHnzFB0drbFjx6pChQqqXr26hgwZoosXLzrarF692qn/lK72fZn9Z0H1sa6QmVVyur333nsuq+96mfXe7HtzdhnAw8NDZcuWVYsWLfTFF1/IGOO0TVJSUraPh5+fn+699169+OKLOdZhlQM///xzx76ioqJkt9uz3X7lypWy2WyKjIzM8dgWL16s7t27q3r16goICJC3t7fCwsLUokULvfvuu/rzzz8t7z+n243uNzseN73FHSQpKUlNmzZVbGyskpKS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eu211zRs2DC98cYb2bZr27atBg0apNOnT2vevHl64okn8nW/Bcllp5jc3d3VokULSdKnn36aq23atGkjKfeTa19v0aJF2Z7eX7BggU6cOCF/f3/HnISSHGdRb3bogYeHh+PrEKtPetOmTZMkx1zOtwObzSZPT09ut/ENgDV3d3eX/41yc77lZwhGQchv7rhWxYoV9frrr0uStmzZku/9FZYePXqoTp06SktLc9SbXy+//LLKlSunxMRELVy4MNs2VatWVXx8vCRp8ODBOnnyZI77PHbsmHbt2lUg9d2IS7+PHT58uDw8PPThhx9q8uTJWYZA7N+/32mi8z59+igiIkLffvuthg4dqnPnzmXZ55EjRyzD98WLF/XCCy84XYDk0KFDGjx4sCSpb9++TpdSzjzLvHv37psex5a5z48++ijLpO+ff/655s2bJ09PT7300ks3tV8AAFA08pI7Nm/erG+++SbLxc4k6fvvv5eU96GiRcFms+nvf/+7JOnLL7/Ub7/9lu99+vr6auTIkZKk9957z7LdBx98oLvuukt79+5Vo0aNtHLlyixt0tLSNG3aNNWtW1c7d+7Md2254dLvN+rXr6/PPvtMvXr1Uv/+/TV27FjVr19fdrtde/bs0c8//6yRI0c6zvb6+vrqhx9+UNu2bTV27Fh98sknioqKUnh4uC5cuKDffvtNO3fuVEhIiHr37p3l/p599lnNnz9fVapUUePGjXXp0iUtW7ZMqampiomJyfIJqlKlSoqOjtaGDRtUq1YtRUdHy8fHR2XKlFFCQkKOx9amTRuNGDFCb731llq0aKGGDRuqUqVK+vXXX7Vp0ya5u7trypQpuvfeewvuAQUAAAUmL7lj//796ty5s+PqexUrVlRGRoa2bdumXbt2ycvLq0DGAhemNm3a6OGHH9by5cv1+uuv68svv8z3Pnv37q13331Xu3fvtmwTHByslJQUderUSUlJSWrcuLEqV66sqKgolSxZUkePHtW6det0/vx5BQQEqHz58ln2cfz48Rv+qHPy5MnZzt5mxbWDgXQ1wEZHR2vixIlatmyZvv/+e/n4+KhChQrq37+/Onbs6NT+3nvv1datWzVlyhTNmTNHW7du1erVq1WmTBmFh4dryJAhluNcKleurA0bNmj48OFatmyZTp06pUqVKumZZ57R0KFDsx3g/9133+lvf/ub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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 16 } ], "metadata": { diff --git a/pyproject.toml b/pyproject.toml index db75ba16..b27dcfcb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ dependencies = [ "tsml>=0.5.0,<0.6.0", "scikit-learn>=1.0.0,<1.6.0", "matplotlib", + "seaborn", "gpustat", "psutil", ] diff --git a/tsml_eval/testing/_cicd_numba_caching.py b/tsml_eval/testing/_cicd_numba_caching.py index 64af39d7..560f552e 100644 --- a/tsml_eval/testing/_cicd_numba_caching.py +++ b/tsml_eval/testing/_cicd_numba_caching.py @@ -1,113 +1,109 @@ """CICD numba caching functions.""" +import os import pickle import subprocess import numba.core.caching - -def get_invalid_numba_files(): - """Get the files that have been changed since the last commit. - - This function is used to get the files that have been changed. This is needed - because custom logic to save the numba cache has been implemented for numba. - This function returns the file names that have been changed and if they appear - in here any numba functions cache are invalidated. - - Returns - ------- - list - List of file names that have been changed. - """ - subprocess.run(["git", "fetch", "origin", "main"], check=True) - - result = subprocess.run( - ["git", "diff", "--name-only", "origin/main"], - check=True, - capture_output=True, - text=True, # Makes the output text instead of bytes - ) - - files = result.stdout.split("\n") - - files = [file for file in files if file] - - clean_files = [] - - for temp in files: - if temp.endswith(".py"): - clean_files.append((temp.split("/")[-1]).strip(".py")) - - return clean_files - - -# Retry the git fetch and git diff commands in case of failure -retry = 0 -while retry < 3: - try: - CHANGED_FILES = get_invalid_numba_files() - break - except subprocess.CalledProcessError: - retry += 1 - -# If the retry count is reached, raise an error -if retry == 3: - raise Exception("Failed to get the changed files from git.") - - -def custom_load_index(self): - """Overwrite load index method for numba. - - This is used to overwrite the numba internal logic to allow for caching during - the cicd run. Numba traditionally checks the timestamp of the file and if it - has changed it invalidates the cache. This is not ideal for the cicd run as - the cache restore is always before the files (since it is cloned in) and - thus the cache is always invalidated. This custom method ignores the timestamp - element and instead just checks the file name. This isn't as fine grained as numba - but it is better to invalidate more and make sure the right function has been - compiled than try to be too clever and miss some. - - Returns - ------- - dict - Dictionary of the cached functions. - """ - try: - with open(self._index_path, "rb") as f: - version = pickle.load(f) - data = f.read() - except FileNotFoundError: - return {} - if version != self._version: - return {} - stamp, overloads = pickle.loads(data) - cache_filename = self._index_path.split("/")[-1].split("-")[0].split(".")[0] - if stamp[1] != self._source_stamp[1] or cache_filename in CHANGED_FILES: - return {} - else: - return overloads - - -original_load_index = numba.core.caching.IndexDataCacheFile._load_index -numba.core.caching.IndexDataCacheFile._load_index = custom_load_index - - -# Force all numba functions to be cached -original_jit = numba.core.decorators._jit - - -def custom_njit(*args, **kwargs): - """Force jit to cache. - - This is used for libraries like stumpy that doesn't cache by default. This - function will force all functions running to be cache'd - """ - target = kwargs["targetoptions"] - # This target can't be cached - if "no_cpython_wrapper" not in target: - kwargs["cache"] = True - return original_jit(*args, **kwargs) - - -# Overwrite the jit function with the custom version -numba.core.decorators._jit = custom_njit +if os.environ.get("CICD_RUNNING") == "1": # pragma: no cover + + def get_invalid_numba_files(): + """Get the files that have been changed since the last commit. + + This function is used to get the files that have been changed. This is needed + because custom logic to save the numba cache has been implemented for numba. + This function returns the file names that have been changed and if they appear + in here any numba functions cache are invalidated. + + Returns + ------- + list + List of file names that have been changed. + """ + subprocess.run(["git", "fetch", "origin", "main"], check=True) + + result = subprocess.run( + ["git", "diff", "--name-only", "origin/main"], + check=True, + capture_output=True, + text=True, # Makes the output text instead of bytes + ) + + files = result.stdout.split("\n") + + files = [file for file in files if file] + + clean_files = [] + + for temp in files: + if temp.endswith(".py"): + clean_files.append((temp.split("/")[-1]).strip(".py")) + + return clean_files + + # Retry the git fetch and git diff commands in case of failure + retry = 0 + while retry < 3: + try: + CHANGED_FILES = get_invalid_numba_files() + break + except subprocess.CalledProcessError: + retry += 1 + + # If the retry count is reached, raise an error + if retry == 3: + raise Exception("Failed to get the changed files from git.") + + def custom_load_index(self): + """Overwrite load index method for numba. + + This is used to overwrite the numba internal logic to allow for caching during + the cicd run. Numba traditionally checks the timestamp of the file and if it + has changed it invalidates the cache. This is not ideal for the cicd run as + the cache restore is always before the files (since it is cloned in) and + thus the cache is always invalidated. This custom method ignores the timestamp + element and instead just checks the file name. This isn't as fine grained as + numba but it is better to invalidate more and make sure the right function has + been compiled than try to be too clever and miss some. + + Returns + ------- + dict + Dictionary of the cached functions. + """ + try: + with open(self._index_path, "rb") as f: + version = pickle.load(f) + data = f.read() + except FileNotFoundError: + return {} + if version != self._version: + return {} + stamp, overloads = pickle.loads(data) + cache_filename = self._index_path.split("/")[-1].split("-")[0].split(".")[0] + if stamp[1] != self._source_stamp[1] or cache_filename in CHANGED_FILES: + return {} + else: + return overloads + + original_load_index = numba.core.caching.IndexDataCacheFile._load_index + numba.core.caching.IndexDataCacheFile._load_index = custom_load_index + + # Force all numba functions to be cached + original_jit = numba.core.decorators._jit + + def custom_njit(*args, **kwargs): + """Force jit to cache. + + This is used for libraries like stumpy that doesn't cache by default. This + function will force all functions running to be cache'd + """ + target = kwargs["targetoptions"] + # This target can't be cached + if "no_cpython_wrapper" not in target: + kwargs["cache"] = True + return original_jit(*args, **kwargs) + + # Overwrite the jit function with the custom version + numba.core.decorators._jit = custom_njit diff --git a/tsml_eval/testing/testing_config.py b/tsml_eval/testing/testing_config.py index f23f0de4..b23b20ef 100644 --- a/tsml_eval/testing/testing_config.py +++ b/tsml_eval/testing/testing_config.py @@ -1,6 +1,3 @@ """Test configuration.""" -import os - -if os.environ.get("CICD_RUNNING") == "1": - import tsml_eval.testing._cicd_numba_caching # noqa: F401 +import tsml_eval.testing._cicd_numba_caching # noqa: F401 diff --git a/tsml_eval/testing/testing_utils.py b/tsml_eval/testing/testing_utils.py index b71ab2e7..7868467c 100644 --- a/tsml_eval/testing/testing_utils.py +++ b/tsml_eval/testing/testing_utils.py @@ -45,7 +45,7 @@ def _check_set_method( "soft dependency", "python version", ] - if any(s in str(err) for s in exempt_errors): + if any(s in str(err) for s in exempt_errors) or "." not in str(err): continue else: raise err diff --git a/tsml_eval/testing/tests/__init__.py b/tsml_eval/testing/tests/__init__.py index b225e3e8..ff594681 100644 --- a/tsml_eval/testing/tests/__init__.py +++ b/tsml_eval/testing/tests/__init__.py @@ -1 +1,17 @@ """Tests for testing functions and classes.""" + +import pkgutil + +import tsml_eval + +# collect all modules except _wip +ALL_TSML_EVAL_MODULES = [ + x[1] for x in pkgutil.walk_packages(tsml_eval.__path__, tsml_eval.__name__ + ".") +] +ALL_TSML_EVAL_MODULES = [x for x in ALL_TSML_EVAL_MODULES if "_wip" not in x] + +ALL_TSML_EVAL_MODULES_NO_TESTS = [ + x + for x in ALL_TSML_EVAL_MODULES + if not any(part == "tests" for part in x.split(".")) +] diff --git a/tsml_eval/testing/tests/test_core_imports.py b/tsml_eval/testing/tests/test_core_imports.py new file mode 100644 index 00000000..baf1cb89 --- /dev/null +++ b/tsml_eval/testing/tests/test_core_imports.py @@ -0,0 +1,26 @@ +"""Tests that non-core dependencies are handled correctly in modules.""" + +import re +from importlib import import_module + +from tsml_eval.testing.tests import ALL_TSML_EVAL_MODULES_NO_TESTS + +if __name__ == "__main__": + """Test imports in tsml-eval modules with core dependencies only. + + Imports all modules and catch exceptions due to missing dependencies. + """ + for module in ALL_TSML_EVAL_MODULES_NO_TESTS: + try: + import_module(module) + except ModuleNotFoundError as e: # pragma: no cover + dependency = "unknown" + match = re.search(r"\'(.+?)\'", str(e)) + if match: + dependency = match.group(1) + + raise ModuleNotFoundError( + f"The module: {module} should not require any non-core dependencies, " + f"but tried importing: '{dependency}'. Make sure non-core dependencies " + f"are properly isolated outside of tests/ directories." + ) from e diff --git a/tsml_eval/testing/tests/test_softdeps.py b/tsml_eval/testing/tests/test_softdeps.py new file mode 100644 index 00000000..ce056855 --- /dev/null +++ b/tsml_eval/testing/tests/test_softdeps.py @@ -0,0 +1,36 @@ +"""Tests that soft dependencies are handled correctly in modules.""" + +import re +from importlib import import_module + +import pytest + +from tsml_eval.testing.tests import ALL_TSML_EVAL_MODULES + + +def test_module_crawl(): + """Test that we are crawling modules correctly.""" + assert "tsml_eval.experiments" in ALL_TSML_EVAL_MODULES + assert "tsml_eval.estimators" in ALL_TSML_EVAL_MODULES + assert "tsml_eval.estimators.classification" in ALL_TSML_EVAL_MODULES + + +@pytest.mark.parametrize("module", ALL_TSML_EVAL_MODULES) +def test_module_soft_deps(module): + """Test soft dependency imports in tsml-eval modules. + + Imports all modules and catch exceptions due to missing dependencies. + """ + try: + import_module(module) + except ModuleNotFoundError as e: # pragma: no cover + dependency = "unknown" + match = re.search(r"\'(.+?)\'", str(e)) + if match: + dependency = match.group(1) + + raise ModuleNotFoundError( + f"The module: {module} should not require any soft dependencies, " + f"but tried importing: '{dependency}'. Make sure soft dependencies are " + f"properly isolated." + ) from e