From a9457ad6e9891bc114434bad2f24db8a93c82e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 01:45:35 +0200 Subject: [PATCH 1/6] Fix typos Fix misspellings in comments, docstrings, docs and tests found by typos, and rename variables that typos flags (`dne` -> `nt`, `somes` -> `results`). Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docs/src/assets/make_logo.jl | 2 +- docs/src/rule_author/superpowers/opt_out.md | 2 +- docs/src/rule_author/tips_for_packages.md | 10 +-- docs/src/rule_author/writing_good_rules.md | 6 +- src/accumulation.jl | 2 +- src/projection.jl | 4 +- src/rule_definition_tools.jl | 4 +- src/rules.jl | 4 +- src/tangent_arithmetic.jl | 2 +- src/tangent_types/abstract_zero.jl | 8 +-- src/tangent_types/structural_tangent.jl | 22 +++---- src/tangent_types/thunks.jl | 4 +- test/config.jl | 2 +- test/rule_definition_tools.jl | 2 +- test/rules.jl | 8 +-- test/tangent_types/abstract_zero.jl | 70 ++++++++++----------- test/tangent_types/structural_tangent.jl | 10 +-- test/tangent_types/thunks.jl | 4 +- 19 files changed, 84 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index dbde80f15..308bb2098 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ | ⚠️ The ChainRules project is currently seeking a new primary maintainer ⚠️ | |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| See this [Discourse thread](https://discourse.julialang.org/t/chainrules-project-looking-for-a-new-primary-maintainer/115636). We currently have many secondary maintainers and contributors with commit rights, but noone who has committed to leading the efforts. Until this is resolved PRs and issues may not recieve timely response. | +| See this [Discourse thread](https://discourse.julialang.org/t/chainrules-project-looking-for-a-new-primary-maintainer/115636). We currently have many secondary maintainers and contributors with commit rights, but noone who has committed to leading the efforts. Until this is resolved PRs and issues may not receive timely response. | The ChainRulesCore package provides a light-weight dependency for defining sensitivities for functions in your packages, without you needing to depend on ChainRules itself. diff --git a/docs/src/assets/make_logo.jl b/docs/src/assets/make_logo.jl index c023c308f..41b680344 100644 --- a/docs/src/assets/make_logo.jl +++ b/docs/src/assets/make_logo.jl @@ -1,5 +1,5 @@ using Pkg: @pkg_str -# For reproducability only dependency this has is Luxor, +# For reproducibility only dependency this has is Luxor, # and it was created with Luxor v1.6.0 pkg"add Luxor@v1.6" diff --git a/docs/src/rule_author/superpowers/opt_out.md b/docs/src/rule_author/superpowers/opt_out.md index 49b1dfe0e..f0a0b71d5 100644 --- a/docs/src/rule_author/superpowers/opt_out.md +++ b/docs/src/rule_author/superpowers/opt_out.md @@ -24,7 +24,7 @@ end That is a fairly reasonable `rrule` for the vast majority of cases. You might have a custom array type for which you could write a faster rule. In which case you would do that, by writing a faster, more specific, `rrule`. -But sometimes, it is the case that ADing the (faster, more specific) primal for your custom array type would yeild the faster pullback without you having to write a `rrule` by hand. +But sometimes, it is the case that ADing the (faster, more specific) primal for your custom array type would yield the faster pullback without you having to write a `rrule` by hand. Consider a summing [`SkewSymmetric` (anti-symmetric)](https://en.wikipedia.org/wiki/Skew-symmetric_matrix) matrix. The skew symmetric matrix has structural zeros on the diagonal, and off-diagonals are paired with their negation. diff --git a/docs/src/rule_author/tips_for_packages.md b/docs/src/rule_author/tips_for_packages.md index 5418ee76d..954314ee1 100644 --- a/docs/src/rule_author/tips_for_packages.md +++ b/docs/src/rule_author/tips_for_packages.md @@ -9,25 +9,25 @@ It captures the functionality of both `Zygote.ignore` and `Zygote.dropgrad`. For example, Zygote does not support mutation, so it will break if you try to store intermediate values as in the following example: ```julia -somes = [] +results = [] things = [] function loss(x, y) - some = f(x, y) + result = f(x, y) thing = g(x) # log - push!(somes, some) + push!(results, result) push!(things, thing) - return some + thing + return result + thing end ``` It is possible to get around this by using the `ignore_derivatives` function ```julia ignore_derivatives() do - push!(somes, some) + push!(results, result) push!(things, thing) end ``` diff --git a/docs/src/rule_author/writing_good_rules.md b/docs/src/rule_author/writing_good_rules.md index 5c1bf9340..a94c09378 100644 --- a/docs/src/rule_author/writing_good_rules.md +++ b/docs/src/rule_author/writing_good_rules.md @@ -173,7 +173,7 @@ They can be thought of as a wrapper of the value the computation returns. In this sense they wrap either a natural or structural tangent. !!! warning "You should support AbstractThunk inputs even if you don't use thunks" - Unfortunately the AD sytems do not know which rules support thunks and which do not. + Unfortunately the AD systems do not know which rules support thunks and which do not. So all rules have to; at least if they want to play nicely with arbitrary AD systems. Luckily it is not hard: much of the time they will duck-type as the object they wrap. If not, then just add a [`unthunk`](@ref) after the start of your pullback. @@ -181,7 +181,7 @@ In this sense they wrap either a natural or structural tangent. If you are using [`@thunk`](@ref) and the input is only needed for one of them then the `unthunk` should be in that one. If not, and you have a bunch of pullbacks you might like to write a little helper `unthunking(f) = x̄ -> f(unthunk(x̄))` that you can wrap your pullback function in before returning it from the `rrule`. Yes, this is a bit of boiler-plate, and it is unfortunate. - Sadly, it is needed because if the AD wants to benefit it can't get that benifit unless things are not unthunked unnecessarily. + Sadly, it is needed because if the AD wants to benefit it can't get that benefit unless things are not unthunked unnecessarily. Which eventually allows them in some cases to never be unthunked at all. There are two ways common things are never unthunked. One is if the unthunking happens inside a `@thunk` which is never unthunked itself because it is the tangent for a primal input that never has it's tangent queried. @@ -226,7 +226,7 @@ function ChainRulesCore.rrule(::typeof(double_it), x) return double_it(x), double_it_pullback end ``` -Ends up infering a return type of `Any` +Ends up inferring a return type of `Any` ```julia-repl julia> _, pullback = rrule(double_it, [2.0, 3.0]) ([4.0, 6.0], var"#double_it_pullback#8"(Core.Box(var"#double_it_pullback#8"(#= circular reference @-2 =#)))) diff --git a/src/accumulation.jl b/src/accumulation.jl index 26d0fbb27..c4b5c7459 100644 --- a/src/accumulation.jl +++ b/src/accumulation.jl @@ -94,7 +94,7 @@ function Base.showerror(io::IO, err::BadInplaceException) if err.accumuland == err.returned_value println( io, - "Which in this case happenned to be equal. But they are not the same object.", + "Which in this case happened to be equal. But they are not the same object.", ) end end diff --git a/src/projection.jl b/src/projection.jl index ba1696fd5..be8268b8a 100644 --- a/src/projection.jl +++ b/src/projection.jl @@ -183,7 +183,7 @@ end (::ProjectTo{T})(dx::Integer) where {T<:Complex{<:AbstractFloat}} = convert(T, dx) # Other numbers, including e.g. ForwardDiff.Dual and Symbolics.Sym, should pass through. -# We assume (lacking evidence to the contrary) that it is the right subspace of numebers. +# We assume (lacking evidence to the contrary) that it is the right subspace of numbers. (::ProjectTo{<:Number})(dx::Number) = dx (project::ProjectTo{<:Real})(dx::Complex) = project(real(dx)) @@ -275,7 +275,7 @@ end ##### # Ref -# Note that Ref is mutable. This causes Zygote to represent its structral tangent not as a NamedTuple, +# Note that Ref is mutable. This causes Zygote to represent its structural tangent not as a NamedTuple, # but as `Ref{Any}((x=val,))`. Here we use a Tangent, there is at present no mutable version, but see # https://github.com/JuliaDiff/ChainRulesCore.jl/issues/105 function ProjectTo(x::Ref) diff --git a/src/rule_definition_tools.jl b/src/rule_definition_tools.jl index c81eb3e62..f3e19cd6f 100644 --- a/src/rule_definition_tools.jl +++ b/src/rule_definition_tools.jl @@ -275,7 +275,7 @@ end """ _propagator_inputs(n) -Declares properly hygenic inputs for propagation expressions +Declares properly hygienic inputs for propagation expressions """ _propagator_inputs(n) = [esc(gensym(Symbol(:Δ, i))) for i in 1:n] @@ -487,7 +487,7 @@ end This allows you to opt-out of an `frule` or an `rrule` by providing a more specific method, that says to use the AD system to differentiate it. -For example, consider some function `foo(x::AbtractArray)`. +For example, consider some function `foo(x::AbstractArray)`. In general, you know an efficient and generic way to implement its `rrule`. You do so, (likely making use of [`ProjectTo`](@ref)). But it actually turns out that for some `FancyArray` type it is better to let the AD do its diff --git a/src/rules.jl b/src/rules.jl index 153a37b81..93985d4dd 100644 --- a/src/rules.jl +++ b/src/rules.jl @@ -45,7 +45,7 @@ julia> Δsincosx[2] == -sin(x) true ``` -Note that techically speaking julia does not have multiple output functions, just functions +Note that technically speaking julia does not have multiple output functions, just functions that return a single output that is iterable, like a `Tuple`. So this is actually a [`Tangent`](@ref): ```jldoctest frule @@ -149,7 +149,7 @@ end const NO_RRULE_DOC = """ no_rrule -This is an piece of infastructure supporting opting out of [`rrule`](@ref). +This is an piece of infrastructure supporting opting out of [`rrule`](@ref). It follows the signature for `rrule` exactly. A collection of type-tuples is stored in its method-table. If something has this defined, it means that it must having a must also have a `rrule`, diff --git a/src/tangent_arithmetic.jl b/src/tangent_arithmetic.jl index 9a93a67a8..269cb8e03 100644 --- a/src/tangent_arithmetic.jl +++ b/src/tangent_arithmetic.jl @@ -10,7 +10,7 @@ Notice: The precedence goes: `NotImplemented, NoTangent, ZeroTangent, AbstractThunk, Tangent, Any` Thus each of the @eval loops create most definitions of + and * - defines the combination this type with all types of lower precidence. + defines the combination this type with all types of lower precedence. This means each eval loops is 1 item smaller than the previous. ==# diff --git a/src/tangent_types/abstract_zero.jl b/src/tangent_types/abstract_zero.jl index c5260489e..c08816a2d 100644 --- a/src/tangent_types/abstract_zero.jl +++ b/src/tangent_types/abstract_zero.jl @@ -106,9 +106,9 @@ and suitable for accumulating against. For types without a tangent space (e.g. singleton structs) this returns `NoTangent()`. In general, it is more likely to produce a structural tangent. -!!! warning "Exprimental" +!!! warning "Experimental" `zero_tangent`is an experimental feature, and is part of the mutation support featureset. - While this notice remains it may have changes in behavour, and interface in any _minor_ version of ChainRulesCore. + While this notice remains it may have changes in behaviour, and interface in any _minor_ version of ChainRulesCore. Exactly how it should be used (e.g. is it forward-mode only?) """ function zero_tangent end @@ -134,7 +134,7 @@ end zero_tangent(getfield(primal, $(QuoteNode(fname)))) else # This is going to be potentially bad, but that's what they get for not giving us a primal - # This will never me mutated inplace, rather it will alway be replaced with an actual value first + # This will never me mutated inplace, rather it will always be replaced with an actual value first ZeroTangent() end ) @@ -191,7 +191,7 @@ end guess_zero_tangent_type(T::Type) = Any guess_zero_tangent_type(::Type{Union{}}) = Union{} # This will only show up for empty containers -# Stuff that conceptually has its own identity regardless of structual implementation and doesn't have a tangent +# Stuff that conceptually has its own identity regardless of structural implementation and doesn't have a tangent zero_tangent(::Base.AbstractLogger) = NoTangent() # Prevent zero_tangent going wild on the internals diff --git a/src/tangent_types/structural_tangent.jl b/src/tangent_types/structural_tangent.jl index 05d90144d..94a0e4e48 100644 --- a/src/tangent_types/structural_tangent.jl +++ b/src/tangent_types/structural_tangent.jl @@ -4,12 +4,12 @@ Representing the type of the tangent of a `struct` `P` (or a `Tuple`/`NamedTuple`). as an object with mirroring fields. -!!! warning "Exprimental" +!!! warning "Experimental" `StructuralTangent` is an experimental feature, and is part of the mutation support featureset. The `StructuralTangent` constructor returns a `MutableTangent` for mutable structs. `MutableTangent` is an experimental feature. Thus use of `StructuralTangent` (rather than `Tangent` directly) is also experimental. - While this notice remains it may have changes in behavour, and interface in any _minor_ version of ChainRulesCore. + While this notice remains it may have changes in behaviour, and interface in any _minor_ version of ChainRulesCore. """ abstract type StructuralTangent{P} <: AbstractTangent end @@ -32,7 +32,7 @@ For `Tangent`s of `Tuple`s, `iterate` and `getindex` are overloaded to behave si to for a tuple. For `Tangent`s of `struct`s, `getproperty` is overloaded to allow for accessing values via `tangent.fieldname`. -Any fields not explictly present in the `Tangent` are treated as being set to `ZeroTangent()`. +Any fields not explicitly present in the `Tangent` are treated as being set to `ZeroTangent()`. To make a `Tangent` have all the fields of the primal the [`canonicalize`](@ref) function is provided. """ @@ -60,15 +60,15 @@ end This type represents the tangent to a mutable struct. It itself is also mutable. -!!! warning "Exprimental" +!!! warning "Experimental" MutableTangent is an experimental feature, and is part of the mutation support featureset. - While this notice remains it may have changes in behavour, and interface in any _minor_ version of ChainRulesCore. + While this notice remains it may have changes in behaviour, and interface in any _minor_ version of ChainRulesCore. Exactly how it should be used (e.g. is it forward-mode only?) !!! warning "Do not directly mess with the tangent backing data" It is relatively straight forward for a forwards-mode AD to work correctly in the presence of mutation and aliasing of primal values. However, this requires that the tangent is aliased in turn and conversely that it is copied when the primal is). - If you seperately alias the backing data, etc by using the internal `ChainRulesCore.backing` function you can break this. + If you separately alias the backing data, etc by using the internal `ChainRulesCore.backing` function you can break this. """ struct MutableTangent{P,F} <: StructuralTangent{P} backing::F @@ -127,7 +127,7 @@ Base.iszero(t::StructuralTangent) = all(iszero, backing(t)) function Base.map(f, tangent::StructuralTangent{P}) where {P} #TODO: is it even useful to support this on MutableTangents? - #TODO: we implictly assume only linear `f` are called and that it is safe to ignore noncanonical Zeros + #TODO: we implicitly assume only linear `f` are called and that it is safe to ignore noncanonical Zeros # This feels like a fair assumption since all normal operations on tangents are linear L = propertynames(backing(tangent)) vals = map(f, Tuple(backing(tangent))) @@ -185,7 +185,7 @@ backing(x::MutableTangent) = map(getindex, getfield(x, :backing)) # For generic structs function backing(x::T)::NamedTuple where {T} # note: all computation outside the if @generated happens at runtime. - # so the first 4 lines of the branchs look the same, but can not be moved out. + # so the first 4 lines of the branches look the same, but can not be moved out. # see https://github.com/JuliaLang/julia/issues/34283 if @generated !isstructtype(T) && @@ -223,7 +223,7 @@ This internally is called to construct structs of the primal type `T`, after an operation such as the addition of a primal to a tangent It should be overloaded, if `T` does not have a default constructor, -or if `T` needs to maintain some invarients between its fields. +or if `T` needs to maintain some invariants between its fields. """ function construct(::Type{T}, fields::NamedTuple{L}) where {T,L} # Tested and verified that that this avoids a ton of allocations @@ -246,7 +246,7 @@ construct(::Type{T}, fields::T) where {T<:Tuple} = fields elementwise_add(a::Tuple, b::Tuple) = map(+, a, b) function elementwise_add(a::NamedTuple{an}, b::NamedTuple{bn}) where {an,bn} - # Rule of Tangent addition: any fields not present are implict hard Zeros + # Rule of Tangent addition: any fields not present are implicit hard Zeros # Base on the `merge(:;NamedTuple, ::NamedTuple)` code from Base. # https://github.com/JuliaLang/julia/blob/592748adb25301a45bd6edef3ac0a93eed069852/base/namedtuple.jl#L220-L231 @@ -422,7 +422,7 @@ Base.conj(tangent::Tangent) = map(conj, tangent) Return the canonical `Tangent` for the primal type `P`. The property names of the returned `Tangent` match the field names of the primal, -and all fields of `P` not present in the input `tangent` are explictly set to `ZeroTangent()`. +and all fields of `P` not present in the input `tangent` are explicitly set to `ZeroTangent()`. """ function canonicalize(tangent::Tangent{P,<:NamedTuple{L}}) where {P,L} nil = _zeroed_backing(P) diff --git a/src/tangent_types/thunks.jl b/src/tangent_types/thunks.jl index 6f23ff782..5e40370b4 100644 --- a/src/tangent_types/thunks.jl +++ b/src/tangent_types/thunks.jl @@ -186,7 +186,7 @@ julia> unthunk(t) ``` ### When to `@thunk`? -When writing `rrule`s (and to a lesser exent `frule`s), it is important to `@thunk` +When writing `rrule`s (and to a lesser extent `frule`s), it is important to `@thunk` appropriately. Propagation rules that return multiple derivatives may not have all deriviatives used. By `@thunk`ing the work required for each derivative, they then compute only what is needed. @@ -231,7 +231,7 @@ Base.convert(::Type{<:Thunk}, a::AbstractZero) = @thunk(a) A wrapper for a `Thunk`, that allows it to define an inplace `add!` function. `add!` should be defined such that: `ithunk.add!(Δ) = Δ .+= ithunk.val` -but it should do this more efficently than simply doing this directly. +but it should do this more efficiently than simply doing this directly. (Otherwise one can just use a normal `Thunk`). Most operations on an `InplaceableThunk` treat it just like a normal `Thunk`; diff --git a/test/config.jl b/test/config.jl index 58d943252..e0f7d24f6 100644 --- a/test/config.jl +++ b/test/config.jl @@ -45,7 +45,7 @@ end #define some functions for testing @testset "config.jl" begin - @testset "basic fall to two arg verion for $Config" for Config in ( + @testset "basic fall to two arg version for $Config" for Config in ( MostBoringConfig, MockForwardsConfig, MockReverseConfig, MockBothConfig ) counting_id_count = Ref(0) diff --git a/test/rule_definition_tools.jl b/test/rule_definition_tools.jl index bad5b5b3d..89ca372d3 100644 --- a/test/rule_definition_tools.jl +++ b/test/rule_definition_tools.jl @@ -143,7 +143,7 @@ end # https://github.com/JuliaDiff/ChainRulesCore.jl/issues/213 # problem was that `@nondiff Foo(x)` was also defining rules for other types. - # make sure that isn't happenning + # make sure that isn't happening @test frule((ZeroTangent(), 1.2), NonDiffCounterExample, 2.0) === nothing @test rrule(NonDiffCounterExample, 2.0) === nothing diff --git a/test/rules.jl b/test/rules.jl index 54c10b160..829635897 100644 --- a/test/rules.jl +++ b/test/rules.jl @@ -43,10 +43,10 @@ function ChainRulesCore.frule( return type_constraints(x, y), Δx + Δy end -mixed_vararg_type_constaint(x::Float64, y::Real, z::Vararg{Float64}) = x + y + sum(z) +mixed_vararg_type_constraint(x::Float64, y::Real, z::Vararg{Float64}) = x + y + sum(z) function ChainRulesCore.frule( dargs::Tuple{Any,Float64,Real,Vararg{Float64}}, - ::typeof(mixed_vararg_type_constaint), + ::typeof(mixed_vararg_type_constraint), x::Float64, y::Real, z::Vararg{Float64}, @@ -107,12 +107,12 @@ _second(t) = Base.tuple_type_head(Base.tuple_type_tail(t)) @test( frule( - (nothing, 3.0, 2.0, 1.0, 0.0), mixed_vararg_type_constaint, 3.0, 2.0, 1.0, 0.0 + (nothing, 3.0, 2.0, 1.0, 0.0), mixed_vararg_type_constraint, 3.0, 2.0, 1.0, 0.0 ) == (6.0, 6.0) ) # violates type constraints, thus an frule should not be found. - @test frule((nothing, 3, 2.0, 1.0, 5.0), mixed_vararg_type_constaint, 3, 2.0, 1.0, 0) == + @test frule((nothing, 3, 2.0, 1.0, 5.0), mixed_vararg_type_constraint, 3, 2.0, 1.0, 0) == nothing @test frule((nothing, nothing, 5.0), Core._apply, dummy_identity, 4.0) == (4.0, 5.0) diff --git a/test/tangent_types/abstract_zero.jl b/test/tangent_types/abstract_zero.jl index 85a9e1d07..e782a8111 100644 --- a/test/tangent_types/abstract_zero.jl +++ b/test/tangent_types/abstract_zero.jl @@ -104,22 +104,22 @@ end @testset "NoTangent" begin - dne = NoTangent() - @test dne + dne == dne - @test dne + 1 == 1 - @test 1 + dne == 1 - @test dne - dne == dne - @test dne - 1 == -1 - @test 1 - dne == 1 - @test -dne == dne - @test dne * dne == dne - @test dne * 11.1 == dne - @test 12.1 * dne == dne - @test dot(dne, dne) == dne - @test dot(dne, 17.2) == dne - @test dot(11.9, dne) == dne - - @test eltype(dne) === NoTangent + nt = NoTangent() + @test nt + nt == nt + @test nt + 1 == 1 + @test 1 + nt == 1 + @test nt - nt == nt + @test nt - 1 == -1 + @test 1 - nt == 1 + @test -nt == nt + @test nt * nt == nt + @test nt * 11.1 == nt + @test 12.1 * nt == nt + @test dot(nt, nt) == nt + @test dot(nt, 17.2) == nt + @test dot(11.9, nt) == nt + + @test eltype(nt) === NoTangent @test eltype(NoTangent) === NoTangent @test promote_type(NoTangent, Bool) == Bool @@ -131,36 +131,36 @@ @test promote_type(NoTangent, ComplexF64) == ComplexF64 @test promote_type(ComplexF64, NoTangent) == ComplexF64 - @test ZeroTangent() + dne == dne - @test dne + ZeroTangent() == dne - @test ZeroTangent() - dne == dne - @test dne - ZeroTangent() == dne + @test ZeroTangent() + nt == nt + @test nt + ZeroTangent() == nt + @test ZeroTangent() - nt == nt + @test nt - ZeroTangent() == nt - @test ZeroTangent() * dne == ZeroTangent() - @test dne * ZeroTangent() == ZeroTangent() - @test dot(ZeroTangent(), dne) == ZeroTangent() - @test dot(dne, ZeroTangent()) == ZeroTangent() + @test ZeroTangent() * nt == ZeroTangent() + @test nt * ZeroTangent() == ZeroTangent() + @test dot(ZeroTangent(), nt) == ZeroTangent() + @test dot(nt, ZeroTangent()) == ZeroTangent() @test norm(ZeroTangent()) == 0 @test norm(ZeroTangent(), 0.4) == 0 - for x in dne - @test x === dne + for x in nt + @test x === nt end - @test broadcastable(dne) isa Ref{NoTangent} + @test broadcastable(nt) isa Ref{NoTangent} for f in (transpose, adjoint, conj) - @test f(dne) === dne + @test f(nt) === nt end - @test dne / 2 === dne / [1, 3] === dne + @test nt / 2 === nt / [1, 3] === nt @test convert(Int64, NoTangent()) == 0 @test convert(Float64, NoTangent()) == 0.0 - @test dne[1] === dne - @test dne[1:3] === dne - @test dne[1, 2] === dne - @test getindex(dne) === dne - @test dne.foo === dne + @test nt[1] === nt + @test nt[1:3] === nt + @test nt[1, 2] === nt + @test getindex(nt) === nt + @test nt.foo === nt end @testset "ambiguities" begin @@ -228,7 +228,7 @@ end end @testset "Weird types" begin - @test iszero(zero_tangent(typeof(Int))) # primative type + @test iszero(zero_tangent(typeof(Int))) # primitive type @test iszero(zero_tangent(typeof(Base.RefValue))) # struct @test iszero(zero_tangent(Vector)) # UnionAll @test iszero(zero_tangent(Union{Int, Float64})) # Union diff --git a/test/tangent_types/structural_tangent.jl b/test/tangent_types/structural_tangent.jl index 0972e1dce..c0b26217e 100644 --- a/test/tangent_types/structural_tangent.jl +++ b/test/tangent_types/structural_tangent.jl @@ -14,7 +14,7 @@ struct Bar x::Float64 end -# For testing Tangent: it is an invarient of the type that x2 = 2x +# For testing Tangent: it is an invariant of the type that x2 = 2x # so simple addition can not be defined struct StructWithInvariant x @@ -306,8 +306,8 @@ end ChainRulesCore.debug_mode() = false # disable it again end - # Now we define constuction for ChainRulesCore.jl's purposes: - # It is going to determine the root quanity of the invarient + # Now we define construction for ChainRulesCore.jl's purposes: + # It is going to determine the root quantity of the invariant function ChainRulesCore.construct(::Type{StructWithInvariant}, nt::NamedTuple) x = (nt.x + nt.x2 / 2) / 2 return StructWithInvariant(x) @@ -407,7 +407,7 @@ end @testset "show" begin @test repr(Tangent{Foo}(; x=1)) == "Tangent{Foo}(x = 1,)" - # check for exact regex match not occurence( `^...$`) + # check for exact regex match not occurrence( `^...$`) # and allowing optional whitespace (`\s?`) @test occursin( r"^Tangent{Tuple{Int64,\s?Int64}}\(1,\s?2\)$", @@ -423,7 +423,7 @@ end end @testset "internals" begin - @testset "Can't do backing on primative type" begin + @testset "Can't do backing on primitive type" begin @test_throws Exception ChainRulesCore.backing(1.4) end diff --git a/test/tangent_types/thunks.jl b/test/tangent_types/thunks.jl index f9c5801fb..cca5df782 100644 --- a/test/tangent_types/thunks.jl +++ b/test/tangent_types/thunks.jl @@ -39,7 +39,7 @@ end @testset "erroring thunks should include the source in the backtrack" begin - expected_line = (@__LINE__) + 2 # for testing it is at right palce + expected_line = (@__LINE__) + 2 # for testing it is at right place try x = @thunk(error()) unthunk(x) @@ -156,7 +156,7 @@ @test diagm(0 => v) == diagm(0 => tv) @test diagm(3, 4, 0 => v) == diagm(3, 4, 0 => tv) - # Check against accidential type piracy + # Check against accidental type piracy # https://github.com/JuliaDiff/ChainRulesCore.jl/issues/472 @test Base.which(diagm, Tuple{}()).module != ChainRulesCore @test Base.which(diagm, Tuple{Int,Int}).module != ChainRulesCore From 82eabbfdebea430c5a786e8d2873dcd276ecf9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 01:45:35 +0200 Subject: [PATCH 2/6] Modernize CI setup - Replace CompatHelper with Dependabot for Julia packages (root and docs) - Add spell check workflow using typos - Test lts and 1 on Linux, macOS (native aarch64) and Windows; run pre on Linux only and allow it to fail; remove separate JuliaNightly workflow - Build docs on Julia 1 using [sources] instead of Pkg.develop - Downgrade: use lts, pass downgrade mode, add cache, drop unused settings - Add permissions for julia-actions/cache where missing Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 10 +++++++ .github/workflows/CI.yml | 21 ++++++++------- .github/workflows/CompatHelper.yml | 16 ----------- .github/workflows/Downgrade.yml | 21 ++++++++++----- .github/workflows/IntegrationTest.yml | 6 ++++- .github/workflows/JuliaNightly.yml | 38 --------------------------- .github/workflows/SpellCheck.yml | 23 ++++++++++++++++ 7 files changed, 63 insertions(+), 72 deletions(-) delete mode 100644 .github/workflows/CompatHelper.yml delete mode 100644 .github/workflows/JuliaNightly.yml create mode 100644 .github/workflows/SpellCheck.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 700707ced..4dae5f9d6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,13 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + - package-ecosystem: "julia" + directories: + - "/" + - "/docs" + schedule: + interval: "weekly" + groups: + all-julia-packages: + patterns: + - "*" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f2b96c5d9..adad12ef8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -20,23 +20,27 @@ jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.allow-failure == true }} strategy: fail-fast: false matrix: version: - "lts" # Long-term support version - "1" # Latest Release - - "pre" # Pre-release/nightly os: - ubuntu-latest - - macOS-latest + - macos-latest - windows-latest arch: - default include: - os: ubuntu-latest - version: "1" + version: "1" arch: x86 + - os: ubuntu-latest + version: "pre" + arch: default + allow-failure: true steps: - uses: actions/checkout@v7 - uses: julia-actions/setup-julia@v2 @@ -57,6 +61,7 @@ jobs: name: Documentation runs-on: ubuntu-latest permissions: + actions: write contents: write # Required when authenticating with `GITHUB_TOKEN`, not needed when authenticating with SSH deploy keys pull-requests: read # Required when using `push_preview=true` statuses: write # Optional, used to report documentation build statuses @@ -64,14 +69,10 @@ jobs: - uses: actions/checkout@v7 - uses: julia-actions/setup-julia@v2 with: - version: '1.10' + version: '1' - uses: julia-actions/cache@v3 - - run: | - julia --project=docs -e ' - using Pkg - Pkg.develop(PackageSpec(path=pwd())) - Pkg.instantiate() - include("docs/make.jl")' + - run: julia --project=docs -e 'using Pkg; Pkg.instantiate()' + - run: julia --project=docs docs/make.jl env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml deleted file mode 100644 index 804232605..000000000 --- a/.github/workflows/CompatHelper.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: CompatHelper -on: - schedule: - - cron: '0 0 * * *' # Everyday at midnight - workflow_dispatch: -jobs: - CompatHelper: - runs-on: ubuntu-latest - steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} - run: julia -e 'using CompatHelper; CompatHelper.main(; subdirs = ["", "docs"])' diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 725acdd8d..5a54b0fb1 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -10,27 +10,34 @@ on: - main paths-ignore: - 'docs/**' +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +# needed to allow julia-actions/cache to delete old caches that it has created +permissions: + actions: write + contents: read + jobs: test: runs-on: ubuntu-latest strategy: matrix: - group: - - Core downgrade_mode: ['alldeps'] - julia-version: ['1.10'] + julia-version: ['lts'] steps: - uses: actions/checkout@v7 - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.julia-version }} + - uses: julia-actions/cache@v3 - uses: julia-actions/julia-downgrade-compat@v2 -# if: ${{ matrix.version == '1.6' }} with: - skip: Pkg,TOML + mode: ${{ matrix.downgrade_mode }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 with: ALLOW_RERESOLVE: false - env: - GROUP: ${{ matrix.group }} diff --git a/.github/workflows/IntegrationTest.yml b/.github/workflows/IntegrationTest.yml index f3e26c4c4..68ada11af 100644 --- a/.github/workflows/IntegrationTest.yml +++ b/.github/workflows/IntegrationTest.yml @@ -11,6 +11,11 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +# needed to allow julia-actions/cache to delete old caches that it has created +permissions: + actions: write + contents: read + jobs: test: name: ${{ matrix.package.name }} @@ -44,7 +49,6 @@ jobs: - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.julia-version }} - arch: x64 - uses: julia-actions/cache@v3 - uses: julia-actions/julia-buildpkg@v1 - name: Clone Downstream diff --git a/.github/workflows/JuliaNightly.yml b/.github/workflows/JuliaNightly.yml deleted file mode 100644 index e7817ee3e..000000000 --- a/.github/workflows/JuliaNightly.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: JuliaNightly -# Nightly Version of Julia. -# Runs on PRs and pushes to `master`, but seperate workflow to `CI` -# to avoid inclusion in nightly cron build with failure notifications. -on: - push: - branches: [main] - tags: ["*"] - pull_request: -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -# needed to allow julia-actions/cache to delete old caches that it has created -permissions: - actions: write - contents: read - -jobs: - test: - name: Julia Nightly - Ubuntu - x64 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - uses: julia-actions/setup-julia@v2 - with: - version: nightly - - uses: julia-actions/cache@v3 - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v6 - with: - files: lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true \ No newline at end of file diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml new file mode 100644 index 000000000..99782e94e --- /dev/null +++ b/.github/workflows/SpellCheck.yml @@ -0,0 +1,23 @@ +name: Spell Check + +on: + push: + branches: [main] + pull_request: + +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +permissions: + contents: read + +jobs: + typos: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: crate-ci/typos@v1.51.1 From 7b9f5fb2ea472fff517eb01ee3090acef1536be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 01:49:54 +0200 Subject: [PATCH 3/6] Reword comment flagged by typos Co-Authored-By: Claude Opus 5.5 --- test/projection.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/projection.jl b/test/projection.jl index 3d1e150bf..cf4ac3b17 100644 --- a/test/projection.jl +++ b/test/projection.jl @@ -458,7 +458,7 @@ struct NoSuperType end psymm = ProjectTo(Symmetric(rand(10^3, 10^3))) # The Union{Symmetric{…,Matrix}, Symmetric{…,ReshapedArray}} return value is only - # unboxed since JuliaLang/julia#55045 (sret_union ABI for pointer-ful types) + # unboxed since JuliaLang/julia#55045 (sret_union ABI for types containing pointers) @test 0 == @ballocated($psymm(dx), setup = (dx = Symmetric(rand(10^3, 10^3)))) broken = ( VERSION < v"1.14.0-DEV.1348" ) From 83ac315d960109b9e86eb8bb1d74f73ca0b520ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 10:54:53 +0200 Subject: [PATCH 4/6] Fix macOS label casing in CI workflow --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index adad12ef8..416a68930 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -29,7 +29,7 @@ jobs: - "1" # Latest Release os: - ubuntu-latest - - macos-latest + - macOS-latest - windows-latest arch: - default From a506b4a6432c6b91e8d4bcdf19486e6243ca0411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 13:03:08 +0200 Subject: [PATCH 5/6] Fix formatting and remaining typos Apply JuliaFormatter suggestions, fix typos missed by typos, and rename the NoTangent test variable to `no_tangent` (`nt` usually denotes a NamedTuple in this codebase). Co-Authored-By: Claude Opus 5.5 --- README.md | 6 +-- src/accumulation.jl | 3 +- src/tangent_types/abstract_zero.jl | 2 +- src/tangent_types/thunks.jl | 2 +- test/rules.jl | 5 ++- test/tangent_types/abstract_zero.jl | 68 ++++++++++++++--------------- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 308bb2098..333d02914 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ [![](https://img.shields.io/badge/docs-main-blue.svg)](https://juliadiff.org/ChainRulesCore.jl/dev) [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliadiff.org/ChainRulesCore.jl/stable) -| ⚠️ The ChainRules project is currently seeking a new primary maintainer ⚠️ | -|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| See this [Discourse thread](https://discourse.julialang.org/t/chainrules-project-looking-for-a-new-primary-maintainer/115636). We currently have many secondary maintainers and contributors with commit rights, but noone who has committed to leading the efforts. Until this is resolved PRs and issues may not receive timely response. | +| ⚠️ The ChainRules project is currently seeking a new primary maintainer ⚠️ | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| See this [Discourse thread](https://discourse.julialang.org/t/chainrules-project-looking-for-a-new-primary-maintainer/115636). We currently have many secondary maintainers and contributors with commit rights, but no one who has committed to leading the efforts. Until this is resolved PRs and issues may not receive timely response. | The ChainRulesCore package provides a light-weight dependency for defining sensitivities for functions in your packages, without you needing to depend on ChainRules itself. diff --git a/src/accumulation.jl b/src/accumulation.jl index c4b5c7459..093ab5361 100644 --- a/src/accumulation.jl +++ b/src/accumulation.jl @@ -93,8 +93,7 @@ function Base.showerror(io::IO, err::BadInplaceException) if err.accumuland == err.returned_value println( - io, - "Which in this case happened to be equal. But they are not the same object.", + io, "Which in this case happened to be equal. But they are not the same object." ) end end diff --git a/src/tangent_types/abstract_zero.jl b/src/tangent_types/abstract_zero.jl index c08816a2d..cde5eeda9 100644 --- a/src/tangent_types/abstract_zero.jl +++ b/src/tangent_types/abstract_zero.jl @@ -134,7 +134,7 @@ end zero_tangent(getfield(primal, $(QuoteNode(fname)))) else # This is going to be potentially bad, but that's what they get for not giving us a primal - # This will never me mutated inplace, rather it will always be replaced with an actual value first + # This will never be mutated inplace, rather it will always be replaced with an actual value first ZeroTangent() end ) diff --git a/src/tangent_types/thunks.jl b/src/tangent_types/thunks.jl index 5e40370b4..cada27096 100644 --- a/src/tangent_types/thunks.jl +++ b/src/tangent_types/thunks.jl @@ -188,7 +188,7 @@ julia> unthunk(t) ### When to `@thunk`? When writing `rrule`s (and to a lesser extent `frule`s), it is important to `@thunk` appropriately. -Propagation rules that return multiple derivatives may not have all deriviatives used. +Propagation rules that return multiple derivatives may not have all derivatives used. By `@thunk`ing the work required for each derivative, they then compute only what is needed. #### How do thunks prevent work? diff --git a/test/rules.jl b/test/rules.jl index 829635897..1eb71f4d7 100644 --- a/test/rules.jl +++ b/test/rules.jl @@ -112,8 +112,9 @@ _second(t) = Base.tuple_type_head(Base.tuple_type_tail(t)) ) # violates type constraints, thus an frule should not be found. - @test frule((nothing, 3, 2.0, 1.0, 5.0), mixed_vararg_type_constraint, 3, 2.0, 1.0, 0) == - nothing + @test frule( + (nothing, 3, 2.0, 1.0, 5.0), mixed_vararg_type_constraint, 3, 2.0, 1.0, 0 + ) == nothing @test frule((nothing, nothing, 5.0), Core._apply, dummy_identity, 4.0) == (4.0, 5.0) diff --git a/test/tangent_types/abstract_zero.jl b/test/tangent_types/abstract_zero.jl index e782a8111..51bd2d1e1 100644 --- a/test/tangent_types/abstract_zero.jl +++ b/test/tangent_types/abstract_zero.jl @@ -104,22 +104,22 @@ end @testset "NoTangent" begin - nt = NoTangent() - @test nt + nt == nt - @test nt + 1 == 1 - @test 1 + nt == 1 - @test nt - nt == nt - @test nt - 1 == -1 - @test 1 - nt == 1 - @test -nt == nt - @test nt * nt == nt - @test nt * 11.1 == nt - @test 12.1 * nt == nt - @test dot(nt, nt) == nt - @test dot(nt, 17.2) == nt - @test dot(11.9, nt) == nt - - @test eltype(nt) === NoTangent + no_tangent = NoTangent() + @test no_tangent + no_tangent == no_tangent + @test no_tangent + 1 == 1 + @test 1 + no_tangent == 1 + @test no_tangent - no_tangent == no_tangent + @test no_tangent - 1 == -1 + @test 1 - no_tangent == 1 + @test -no_tangent == no_tangent + @test no_tangent * no_tangent == no_tangent + @test no_tangent * 11.1 == no_tangent + @test 12.1 * no_tangent == no_tangent + @test dot(no_tangent, no_tangent) == no_tangent + @test dot(no_tangent, 17.2) == no_tangent + @test dot(11.9, no_tangent) == no_tangent + + @test eltype(no_tangent) === NoTangent @test eltype(NoTangent) === NoTangent @test promote_type(NoTangent, Bool) == Bool @@ -131,36 +131,36 @@ @test promote_type(NoTangent, ComplexF64) == ComplexF64 @test promote_type(ComplexF64, NoTangent) == ComplexF64 - @test ZeroTangent() + nt == nt - @test nt + ZeroTangent() == nt - @test ZeroTangent() - nt == nt - @test nt - ZeroTangent() == nt + @test ZeroTangent() + no_tangent == no_tangent + @test no_tangent + ZeroTangent() == no_tangent + @test ZeroTangent() - no_tangent == no_tangent + @test no_tangent - ZeroTangent() == no_tangent - @test ZeroTangent() * nt == ZeroTangent() - @test nt * ZeroTangent() == ZeroTangent() - @test dot(ZeroTangent(), nt) == ZeroTangent() - @test dot(nt, ZeroTangent()) == ZeroTangent() + @test ZeroTangent() * no_tangent == ZeroTangent() + @test no_tangent * ZeroTangent() == ZeroTangent() + @test dot(ZeroTangent(), no_tangent) == ZeroTangent() + @test dot(no_tangent, ZeroTangent()) == ZeroTangent() @test norm(ZeroTangent()) == 0 @test norm(ZeroTangent(), 0.4) == 0 - for x in nt - @test x === nt + for x in no_tangent + @test x === no_tangent end - @test broadcastable(nt) isa Ref{NoTangent} + @test broadcastable(no_tangent) isa Ref{NoTangent} for f in (transpose, adjoint, conj) - @test f(nt) === nt + @test f(no_tangent) === no_tangent end - @test nt / 2 === nt / [1, 3] === nt + @test no_tangent / 2 === no_tangent / [1, 3] === no_tangent @test convert(Int64, NoTangent()) == 0 @test convert(Float64, NoTangent()) == 0.0 - @test nt[1] === nt - @test nt[1:3] === nt - @test nt[1, 2] === nt - @test getindex(nt) === nt - @test nt.foo === nt + @test no_tangent[1] === no_tangent + @test no_tangent[1:3] === no_tangent + @test no_tangent[1, 2] === no_tangent + @test getindex(no_tangent) === no_tangent + @test no_tangent.foo === no_tangent end @testset "ambiguities" begin From 4e8921184f03fa2526977b337739de91be140886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 14:40:18 +0200 Subject: [PATCH 6/6] Drop Diffractor from integration tests Diffractor only runs on Julia nightly and has been failing for a long time because its dependency Cthulhu does not precompile there; its last code change was in June 2025. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/IntegrationTest.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/IntegrationTest.yml b/.github/workflows/IntegrationTest.yml index 68ada11af..a6fd0cb1f 100644 --- a/.github/workflows/IntegrationTest.yml +++ b/.github/workflows/IntegrationTest.yml @@ -38,11 +38,6 @@ jobs: - {user: dfdx, name: Yota} - {user: JuliaStats, name: StatsFuns} - {user: JuliaStats, name: LogExpFunctions} - # Diffractor needs to run on Julia nightly - include: - - julia-version: nightly - os: ubuntu-latest - package: {user: JuliaDiff, name: Diffractor} steps: - uses: actions/checkout@v7