From f3edb7c35997c6a9e8586c670c86415c6c4bc039 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 20:33:59 +0200 Subject: [PATCH 1/9] ci: move Aqua and JET checks into shared linting.jl - Move the Aqua.jl and JET.jl checks from `test/runtests.jl` into a self-contained `test/linting.jl`. - Run the JET testset only on Julia v1.12+ (JET v0.11 supports v1.12+ only). - Run Aqua with `ambiguities = false`. Co-Authored-By: Claude Fable 5 --- test/linting.jl | 18 ++++++++++++++++++ test/runtests.jl | 10 +++------- 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 test/linting.jl diff --git a/test/linting.jl b/test/linting.jl new file mode 100644 index 0000000..9319ca6 --- /dev/null +++ b/test/linting.jl @@ -0,0 +1,18 @@ +# Shared code-quality (Aqua.jl) and type-stability (JET.jl) checks. +# Kept consistent across the Julia-XAI packages — see REFACTOR.md. +using SmoothedDifferentiation +using Test +using Aqua +using JET + +@testset "Aqua.jl" begin + @info "Running Aqua.jl code-quality tests. These might print warnings from dependencies." + Aqua.test_all(SmoothedDifferentiation; ambiguities = false) +end +# JET's v0.11 series supports Julia v1.12 and above only. +if VERSION >= v"1.12" + @testset "JET.jl" begin + @info "Running JET.jl type-stability tests." + JET.test_package(SmoothedDifferentiation; target_defined_modules = true) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 585a0d8..c852ce6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,14 +1,10 @@ using SmoothedDifferentiation using Test -using Aqua -using JET @testset "SmoothedDifferentiation.jl" begin - @testset "Code quality (Aqua.jl)" begin - Aqua.test_all(SmoothedDifferentiation) - end - @testset "Code linting (JET.jl)" begin - JET.test_package(SmoothedDifferentiation; target_defined_modules = true) + @testset verbose = true "Linting" begin + @info "Running linting tests..." + include("linting.jl") end @testset "VEJP numerical tests" begin From 5b17b065106137021b6f1befae3a558ce3e2fab4 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 21:27:48 +0200 Subject: [PATCH 2/9] ci: add prek pre-commit config (runic, typos, hygiene) --- .pre-commit-config.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..94e37a7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +repos: + - repo: https://github.com/fredrikekre/runic-pre-commit + rev: v1.0.0 + hooks: + - id: runic + + - repo: https://github.com/crate-ci/typos + rev: v1.48.0 + hooks: + - id: typos + # report and fail (no --write-changes, so no in-place edits); + # --force-exclude makes staged files honor _typos.toml excludes + args: [--force-exclude] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + # ReferenceTests golden files: trailing space and the missing + # final newline are significant output, never "fix" them + exclude: ^test/references/ + - id: end-of-file-fixer + exclude: ^test/references/ + - id: check-merge-conflict + - id: check-added-large-files + - id: check-toml + - id: check-yaml From c22008b235e5d40b3173711526b822b023acfbe0 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 21:27:48 +0200 Subject: [PATCH 3/9] style: apply trailing-whitespace and end-of-file hook fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 824a85c..3ee1cb2 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ Julia reference implementation of SmoothDiff for the NeurIPS 2025 paper *"Smoothed Differentiation Efficiently Mitigates Shattered Gradients in Explanations"*. -The full experiments can be found here: https://github.com/adrhill/smoothdiff-experiments/ \ No newline at end of file +The full experiments can be found here: https://github.com/adrhill/smoothdiff-experiments/ From 53a93bd1df13afd7fab91ba633531213ac1b18c9 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 21:27:48 +0200 Subject: [PATCH 4/9] test: add ExplicitImports.jl checks, drop unused imports --- src/SmoothedDifferentiation.jl | 4 ++-- test/Project.toml | 1 + test/linting.jl | 13 ++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/SmoothedDifferentiation.jl b/src/SmoothedDifferentiation.jl index ef2d282..a828876 100644 --- a/src/SmoothedDifferentiation.jl +++ b/src/SmoothedDifferentiation.jl @@ -5,11 +5,11 @@ using Reexport import XAIBase: call_analyzer using Base.Iterators -using Distributions: Distribution, Sampleable, Normal +using Distributions: Sampleable, Normal using Random: AbstractRNG, GLOBAL_RNG, rand! using ProgressMeter: Progress, next! -using NNlib: relu, ∇maxpool, maxpool, upsample_nearest, σ, softplus +using NNlib: relu, ∇maxpool, maxpool, upsample_nearest, σ using Zygote: pullback import ChainRulesCore: rrule, NoTangent, unthunk diff --git a/test/Project.toml b/test/Project.toml index e2fcafa..758d2ad 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" diff --git a/test/linting.jl b/test/linting.jl index 9319ca6..a4e7ed0 100644 --- a/test/linting.jl +++ b/test/linting.jl @@ -1,14 +1,25 @@ -# Shared code-quality (Aqua.jl) and type-stability (JET.jl) checks. +# Shared code-quality (Aqua.jl), type-stability (JET.jl) and import-hygiene +# (ExplicitImports.jl) checks. # Kept consistent across the Julia-XAI packages — see REFACTOR.md. using SmoothedDifferentiation using Test using Aqua using JET +using ExplicitImports @testset "Aqua.jl" begin @info "Running Aqua.jl code-quality tests. These might print warnings from dependencies." Aqua.test_all(SmoothedDifferentiation; ambiguities = false) end + +@testset "ExplicitImports.jl" begin + @info "Running ExplicitImports.jl import-hygiene tests." + @test check_no_stale_explicit_imports(SmoothedDifferentiation) === nothing + @test check_all_explicit_imports_via_owners(SmoothedDifferentiation) === nothing + @test check_all_qualified_accesses_via_owners(SmoothedDifferentiation) === nothing + @test check_no_self_qualified_accesses(SmoothedDifferentiation) === nothing +end + # JET's v0.11 series supports Julia v1.12 and above only. if VERSION >= v"1.12" @testset "JET.jl" begin From ea1c84a65b9b00e2b2739ebdab0b8bfdcb82c07a Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 22:06:09 +0200 Subject: [PATCH 5/9] test: add package-scoped Aqua.jl method-ambiguity check Add `Aqua.test_ambiguities()` alongside the existing `Aqua.test_all(...; ambiguities = false)`. test_all's built-in check broadens to [Module, Core, Base] and trips on dependency-owned ambiguities we can't fix; the scoped call only reports ambiguities where at least one conflicting method is owned by the package itself. Co-Authored-By: Claude Opus 4.8 (1M context) --- test/linting.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/linting.jl b/test/linting.jl index a4e7ed0..6fb49f0 100644 --- a/test/linting.jl +++ b/test/linting.jl @@ -10,6 +10,7 @@ using ExplicitImports @testset "Aqua.jl" begin @info "Running Aqua.jl code-quality tests. These might print warnings from dependencies." Aqua.test_all(SmoothedDifferentiation; ambiguities = false) + Aqua.test_ambiguities(SmoothedDifferentiation) end @testset "ExplicitImports.jl" begin From 2f2469a05d70993877d715a71fd22ced55a65f5c Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 22:06:10 +0200 Subject: [PATCH 6/9] ci: standardize workflow actions and test matrix Pin all GitHub Actions to their latest major moving tag: checkout@v7, setup-julia@v3, cache@v3, codecov-action@v7 (julia-actions/*, TagBot and runic-action stay @v1). The codecov bump is input-safe as every step already uses the post-v4 `files:` schema. Test only on Julia '1' and 'lts' on a single ubuntu-latest runner: drop 'pre'/'nightly'/pinned point releases and remove the os/arch matrix dimensions and hardcoded arch inputs. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 01b5a56..2194b51 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,7 +31,6 @@ jobs: - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - arch: x64 - uses: julia-actions/cache@v3 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 @@ -47,7 +46,7 @@ jobs: name: Runic formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - uses: fredrikekre/runic-action@v1 with: version: '1' From 2cce462cca9261bc44d2d996d96845ae41d09d9c Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 22:35:17 +0200 Subject: [PATCH 7/9] docs: add Runic, Aqua, and JET badges to README Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3ee1cb2..ae95590 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # SmoothDiff.jl +[![Code Style: Runic](https://img.shields.io/badge/code_style-%E1%9A%B1%E1%9A%A2%E1%9A%BE%E1%9B%81%E1%9A%B2-black)](https://github.com/fredrikekre/Runic.jl) +[![Aqua](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) +[![JET](https://img.shields.io/badge/%F0%9F%9B%A9%EF%B8%8F_tested_with-JET.jl-233f9a)](https://github.com/aviatesk/JET.jl) + Julia reference implementation of SmoothDiff for the NeurIPS 2025 paper *"Smoothed Differentiation Efficiently Mitigates Shattered Gradients in Explanations"*. The full experiments can be found here: https://github.com/adrhill/smoothdiff-experiments/ From 70920806a279b97d0f911162508d7307f870fb85 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 22:59:14 +0200 Subject: [PATCH 8/9] docs: add acknowledgements section to README Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ae95590..0abbd5f 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,7 @@ Julia reference implementation of SmoothDiff for the NeurIPS 2025 paper *"Smoothed Differentiation Efficiently Mitigates Shattered Gradients in Explanations"*. The full experiments can be found here: https://github.com/adrhill/smoothdiff-experiments/ + +## Acknowledgements + +Adrian Hill gratefully acknowledges funding from the German Federal Ministry of Education and Research under the grant BIFOLD26B. From 7e741d4fd3fdb4803f8de41418344166d1e1f437 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 13 Jul 2026 23:19:01 +0200 Subject: [PATCH 9/9] fix: access trainable via its owner Optimisers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ExplicitImports' `check_all_qualified_accesses_via_owners` failed on Julia LTS because `trainable` was accessed as `Flux.trainable`, but the name is owned by Optimisers (Flux only re-exports it). Access it through `Optimisers.trainable` instead — an identical binding across the whole Flux 0.14–0.16 compat range — and add Optimisers to deps/compat. Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 2 ++ src/SmoothedDifferentiation.jl | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3e7ee5b..856bee7 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,7 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" Metalhead = "dbeba491-748d-5e0e-a39e-b530a07fa0cc" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" +Optimisers = "3bd65402-5787-11e9-1adc-39752487f4e2" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" @@ -28,6 +29,7 @@ Distributions = "0.25" Flux = "0.14, 0.15, 0.16" Metalhead = "0.9.5" NNlib = "0.9" +Optimisers = "0.3, 0.4" ProgressMeter = "1" Random = "1" Reexport = "1" diff --git a/src/SmoothedDifferentiation.jl b/src/SmoothedDifferentiation.jl index a828876..804660d 100644 --- a/src/SmoothedDifferentiation.jl +++ b/src/SmoothedDifferentiation.jl @@ -14,6 +14,7 @@ using Zygote: pullback import ChainRulesCore: rrule, NoTangent, unthunk using Flux: Flux +using Optimisers: Optimisers include("prepare_model.jl") include("vejp/relu.jl") @@ -27,7 +28,7 @@ mytestmode!(model) = model mytestmode!(model::Flux.Chain) = Flux.testmode!(model) -samplingmode!(model, mode::Bool) = foreach(x -> samplingmode!(x, mode), Flux.trainable(model)) +samplingmode!(model, mode::Bool) = foreach(x -> samplingmode!(x, mode), Optimisers.trainable(model)) ## Interface const DEFAULT_SAMPLES = 50