From 3748c47974e082ad061c1697ad20cb24c61e74cf Mon Sep 17 00:00:00 2001 From: adrhill Date: Thu, 27 Jun 2024 20:17:33 +0200 Subject: [PATCH] Fixes for patterns introduced in #139 --- src/interface.jl | 17 ++++---- src/overloads/hessian_tracer.jl | 72 +++++++++++++++++++++------------ src/overloads/ifelse_global.jl | 17 +++++--- src/patterns.jl | 68 ++++++++++++++++++++----------- src/tracers.jl | 70 ++++++-------------------------- test/brusselator.jl | 2 +- test/flux.jl | 2 +- test/test_constructors.jl | 2 +- test/test_gradient.jl | 8 ++-- test/test_hessian.jl | 21 +++++----- test/tracers_definitions.jl | 13 +++--- 11 files changed, 150 insertions(+), 142 deletions(-) diff --git a/src/interface.jl b/src/interface.jl index bbd4df7..325e0b8 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,6 +1,6 @@ const DEFAULT_GRADIENT_TRACER = GradientTracer{IndexSetGradientPattern{Int,BitSet}} const DEFAULT_HESSIAN_TRACER = HessianTracer{ - IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}}} + IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}},false} } #==================# @@ -9,20 +9,19 @@ const DEFAULT_HESSIAN_TRACER = HessianTracer{ """ trace_input(T, x) - trace_input(T, x) - + trace_input(T, xs) Enumerates input indices and constructs the specified type `T` of tracer. Supports [`GradientTracer`](@ref), [`HessianTracer`](@ref) and [`Dual`](@ref). """ -trace_input(::Type{T}, x) where {T<:Union{AbstractTracer,Dual}} = trace_input(T, x, 1) +trace_input(::Type{T}, xs) where {T<:Union{AbstractTracer,Dual}} = trace_input(T, xs, 1) -function trace_input(::Type{T}, x::Real, i::Integer) where {T<:Union{AbstractTracer,Dual}} - return create_tracer(T, x, i) -end function trace_input(::Type{T}, xs::AbstractArray, i) where {T<:Union{AbstractTracer,Dual}} - indices = reshape(1:length(xs), size(xs)) .+ (i - 1) - return create_tracers(T, xs, indices) + is = reshape(1:length(xs), size(xs)) .+ (i - 1) + return create_tracers(T, xs, is) +end +function trace_input(::Type{T}, x::Real, i::Integer) where {T<:Union{AbstractTracer,Dual}} + return only(create_tracers(T, [x], [i])) end #=========================# diff --git a/src/overloads/hessian_tracer.jl b/src/overloads/hessian_tracer.jl index 054cb43..83dcf3a 100644 --- a/src/overloads/hessian_tracer.jl +++ b/src/overloads/hessian_tracer.jl @@ -12,27 +12,33 @@ end function hessian_tracer_1_to_1_inner( - p::P, is_der1_zero::Bool, is_der2_zero::Bool; shared::Bool -) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH}} + p::P, is_der1_zero::Bool, is_der2_zero::Bool +) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,false}} sg = gradient(p) sh = hessian(p) sg_out = gradient_tracer_1_to_1_inner(sg, is_der1_zero) - if shared - sh_out = if is_secondder_zero - sh - else - union_product!(sh, sg, sg) - end + sh_out = if is_der1_zero && is_der2_zero + myempty(SH) + elseif !is_der1_zero && is_der2_zero + sh + elseif is_der1_zero && is_der2_zero + union_product!(myempty(SH), sg, sg) else - sh_out = if is_der1_zero && is_secondder_zero - myempty(H) - elseif !is_der1_zero && is_secondder_zero - sh - elseif is_der1_zero && !is_secondder_zero - union_product!(myempty(H), sg, sg) - else - union_product!(copy(sh), sg, sg) - end + union_product!(copy(sh), sg, sg) + end + return P(sg_out, sh_out) # return pattern +end + +function hessian_tracer_1_to_1_inner( + p::P, is_der1_zero::Bool, is_der2_zero::Bool +) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,true}} + sg = gradient(p) + sh = hessian(p) + sg_out = gradient_tracer_1_to_1_inner(sg, is_der1_zero) + sh_out = if is_der2_zero + sh + else + union_product!(sh, sg, sg) end return P(sg_out, sh_out) # return pattern end @@ -104,17 +110,33 @@ function hessian_tracer_2_to_1_inner( is_der1_arg2_zero::Bool, is_der2_arg2_zero::Bool, is_der_cross_zero::Bool, -) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH}} +) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,false}} sgx, shx = gradient(px), hessian(px) sgy, shy = gradient(py), hessian(py) sg_out = gradient_tracer_2_to_1_inner(sgx, sgy, is_der1_arg1_zero, is_der1_arg2_zero) - if shared - sh_out = union!(shx, shy) - else - sh_out = myempty(SH) - !is_der1_arg1_zero && union!(sh_out, shx) # hessian alpha - !is_der1_arg2_zero && union!(sh_out, shy) # hessian beta - end + sh_out = myempty(SH) + !is_der1_arg1_zero && union!(sh_out, shx) # hessian alpha + !is_der1_arg2_zero && union!(sh_out, shy) # hessian beta + !is_der2_arg1_zero && union_product!(sh_out, sgx, sgx) # product alpha + !is_der2_arg2_zero && union_product!(sh_out, sgy, sgy) # product beta + !is_der_cross_zero && union_product!(sh_out, sgx, sgy) # cross product 1 + !is_der_cross_zero && union_product!(sh_out, sgy, sgx) # cross product 2 + return P(sg_out, sh_out) # return pattern +end + +function hessian_tracer_2_to_1_inner( + px::P, + py::P, + is_der1_arg1_zero::Bool, + is_der2_arg1_zero::Bool, + is_der1_arg2_zero::Bool, + is_der2_arg2_zero::Bool, + is_der_cross_zero::Bool, +) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,true}} + sgx, shx = gradient(px), hessian(px) + sgy, shy = gradient(py), hessian(py) + sg_out = gradient_tracer_2_to_1_inner(sgx, sgy, is_der1_arg1_zero, is_der1_arg2_zero) + sh_out = union!(shx, shy) !is_der2_arg1_zero && union_product!(sh_out, sgx, sgx) # product alpha !is_der2_arg2_zero && union_product!(sh_out, sgy, sgy) # product beta !is_der_cross_zero && union_product!(sh_out, sgx, sgy) # cross product 1 diff --git a/src/overloads/ifelse_global.jl b/src/overloads/ifelse_global.jl index 0164a0f..e330c31 100644 --- a/src/overloads/ifelse_global.jl +++ b/src/overloads/ifelse_global.jl @@ -15,13 +15,18 @@ function output_union(px::P, py::P) where {P<:IndexSetGradientPattern} return P(union(set(px), set(py))) # return pattern end - function output_union(px::P, py::P) where {G,H,shared,P<:IndexSetHessianPattern{G,H,shared}} + function output_union( + px::P, py::P + ) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,false}} # non-mutating + g_out = union(gradient(px), gradient(py)) + h_out = union(hessian(px), hessian(py)) + return P(g_out, h_out) # return pattern + end + function output_union( + px::P, py::P + ) where {I,SG,SH,P<:IndexSetHessianPattern{I,SG,SH,true}} # mutating g_out = union(gradient(px), gradient(py)) - h_out = if shared - union!(hessian(tx), hessian(ty)) - else - union(hessian(px), hessian(py)) - end + h_out = union!(hessian(px), hessian(py)) return P(g_out, h_out) # return pattern end diff --git a/src/patterns.jl b/src/patterns.jl index a3d9b47..afa7b38 100644 --- a/src/patterns.jl +++ b/src/patterns.jl @@ -9,11 +9,20 @@ AbstractPattern ├── AbstractGradientPattern: used in GradientTracer │ └── IndexSetGradientPattern └── AbstractHessianPattern: used in HessianTracer - └── IndexSetHessianPattern + ├── IndexSetHessianPattern + └── SharedIndexSetHessianPattern ``` """ abstract type AbstractPattern end +""" + isshared(pattern) + +Indicates whether patterns share memory (mutate). +""" +isshared(::P) where {P<:AbstractPattern} = isshared(P) +isshared(::Type{P}) where {P<:AbstractPattern} = false + """ myempty(T) myempty(tracer) @@ -25,13 +34,11 @@ Constructor for an empty tracer or pattern of type `T` representing a new number myempty """ - seed(T, i) - seed(tracer, i) - seed(pattern, i) + create_patterns(P, xs, is) -Constructor for a tracer or pattern of type `T` that only contains the given index `i`. +Convenience constructor for patterns of type `P` for multiple inputs `xs` and their indices `is`. """ -seed +create_patterns #==========================# # Utilities on AbstractSet # @@ -69,18 +76,17 @@ For use with [`GradientTracer`](@ref). ## Expected interface -* `myempty(::Type{MyPattern})`: return a pattern representing a new number (usually an empty pattern) -* `seed(::Type{MyPattern}, i::Integer)`: return an pattern that only contains the given index `i` -* `gradient(p::MyPattern)`: return non-zero indices `i` for use with `GradientTracer` - -Note that besides their names, the last two functions are usually identical. +* [`myempty`](@ref) +* [`create_patterns`](@ref) +* `gradient(p::MyPattern)`: return non-zero indices `i` in the gradient representation +* [`isshared`](@ref) in case the pattern is shared (mutates). Defaults to false. """ abstract type AbstractGradientPattern <: AbstractPattern end """ $(TYPEDEF) -Vector sparsity pattern represented by an `AbstractSet` of indices ``{i}`` of non-zero values. +Gradient sparsity pattern represented by an `AbstractSet` of indices ``{i}`` of non-zero values. ## Fields $(TYPEDFIELDS) @@ -97,8 +103,9 @@ Base.show(io::IO, p::IndexSetGradientPattern) = Base.show(io, set(p)) function myempty(::Type{IndexSetGradientPattern{I,S}}) where {I,S} return IndexSetGradientPattern{I,S}(myempty(S)) end -function seed(::Type{IndexSetGradientPattern{I,S}}, i) where {I,S} - return IndexSetGradientPattern{I,S}(seed(S, i)) +function create_patterns(::Type{P}, xs, is) where {I,S,P<:IndexSetGradientPattern{I,S}} + sets = seed.(S, is) + return P.(sets) end # Tracer compatibility @@ -118,29 +125,42 @@ For use with [`HessianTracer`](@ref). ## Expected interface -* `myempty(::Type{MyPattern})`: return a pattern representing a new number (usually an empty pattern) -* `seed(::Type{MyPattern}, i::Integer)`: return an pattern that only contains the given index `i` in the first-order representation +* [`myempty`](@ref) +* [`create_patterns`](@ref) * `gradient(p::MyPattern)`: return non-zero indices `i` in the first-order representation * `hessian(p::MyPattern)`: return non-zero indices `(i, j)` in the second-order representation +* [`isshared`](@ref) in case the pattern is shared (mutates). Defaults to false. """ abstract type AbstractHessianPattern <: AbstractPattern end """ - IndexSetHessianPattern(vector::AbstractGradientPattern, mat::AbstractMatrixPattern) +$(TYPEDEF) + +Hessian sparsity pattern represented by: +* an `AbstractSet` of indices ``i`` of non-zero values representing first-order sparsity +* an `AbstractSet` of index tuples ``(i,j)`` of non-zero values representing second-order sparsity -Gradient and Hessian sparsity patterns constructed by combining two AbstractSets. +## Fields + +$(TYPEDFIELDS) """ -struct IndexSetHessianPattern{I<:Integer,SG<:AbstractSet{I},SH<:AbstractSet{Tuple{I,I}}} <: - AbstractHessianPattern +struct IndexSetHessianPattern{ + I<:Integer,SG<:AbstractSet{I},SH<:AbstractSet{Tuple{I,I}},mutating +} <: AbstractHessianPattern gradient::SG hessian::SH end +isshared(::Type{IndexSetHessianPattern{I,SG,SH,true}}) where {I,SG,SH} = true -function myempty(::Type{IndexSetHessianPattern{I,SG,SH}}) where {I,SG,SH} - return IndexSetHessianPattern{I,SG,SH}(myempty(SG), myempty(SH)) +function myempty(::Type{P}) where {I,SG,SH,M,P<:IndexSetHessianPattern{I,SG,SH,M}} + return P(myempty(SG), myempty(SH)) end -function seed(::Type{IndexSetHessianPattern{I,SG,SH}}, index) where {I,SG,SH} - return IndexSetHessianPattern{I,SG,SH}(seed(SG, index), myempty(SH)) +function create_patterns( + ::Type{P}, xs, is +) where {I,SG,SH,M,P<:IndexSetHessianPattern{I,SG,SH,M}} + gradients = seed.(SG, is) + hessian = myempty(SH) + return P.(gradients, Ref(hessian)) end # Tracer compatibility diff --git a/src/tracers.jl b/src/tracers.jl index e74aa44..0c96967 100644 --- a/src/tracers.jl +++ b/src/tracers.jl @@ -136,76 +136,32 @@ end # Utilities # #===========# -myempty(::T) where {T<:AbstractTracer} = myempty(T) +# isshared(::Type{T}) where {P,T<:GradientTracer{P}} = isshared(P) # no shared AbstractGradientPattern yet +isshared(::Type{T}) where {P,T<:HessianTracer{P}} = isshared(P) -# myempty(::Type{T}) where {P,T<:AbstractTracer{P}} = T(myempty(P), true) # JET complains about this +myempty(::T) where {T<:AbstractTracer} = myempty(T) +# myempty(::Type{T}) where {P,T<:AbstractTracer{P}} = T(myempty(P), true) # JET complains about this myempty(::Type{T}) where {P,T<:GradientTracer{P}} = T(myempty(P), true) myempty(::Type{T}) where {P,T<:HessianTracer{P}} = T(myempty(P), true) -seed(::T, i) where {T<:AbstractTracer} = seed(T, i) - -# seed(::Type{T}, i) where {P,T<:AbstractTracer{P}} = T(seed(P, i)) # JET complains about this -seed(::Type{T}, i) where {P,T<:GradientTracer{P}} = T(seed(P, i)) -seed(::Type{T}, i) where {P,T<:HessianTracer{P}} = T(seed(P, i)) - -""" - create_tracer(T, index) - -Convenience constructor for [`GradientTracer`](@ref) and [`HessianTracer`](@ref) from input indices. -""" -function create_tracer(::Type{T}, ::Real, index::Integer) where {P,T<:AbstractTracer{P}} - return T(seed(P, index)) -end - -function create_tracer(::Type{Dual{P,T}}, primal::Real, index::Integer) where {P,T} - return Dual(primal, create_tracer(T, primal, index)) -end - -function create_tracer(::Type{ConnectivityTracer{I}}, ::Real, index::Integer) where {I} - return ConnectivityTracer{I}(seed(I, index)) -end -function create_tracer(::Type{GradientTracer{G}}, ::Real, index::Integer) where {G} - return GradientTracer{G}(seed(G, index)) -end -function create_tracer( - ::Type{HessianTracer{G,H,shared}}, ::Real, index::Integer -) where {G,H,shared} - return HessianTracer{G,H,shared}(seed(G, index), myempty(H)) -end - """ create_tracers(T, xs, indices) -Convenience constructor for [`ConnectivityTracer`](@ref), [`GradientTracer`](@ref), [`HessianTracer`](@ref) and [`Dual`](@ref) from multiple inputs and their indices. - -Allows the creation of shared tracer fields (sofar only for the Hessian). +Convenience constructor for [`GradientTracer`](@ref), [`HessianTracer`](@ref) and [`Dual`](@ref) +from multiple inputs `xs` and their indices `is`. """ function create_tracers( ::Type{T}, xs::AbstractArray{<:Real,N}, indices::AbstractArray{<:Integer,N} -) where {T<:Union{AbstractTracer,Dual},N} - return create_tracer.(T, xs, indices) +) where {P<:AbstractPattern,T<:AbstractTracer{P},N} + patterns = create_patterns(P, xs, indices) + return T.(patterns) end function create_tracers( - ::Type{HessianTracer{G,H,true}}, - xs::AbstractArray{<:Real,N}, - indices::AbstractArray{<:Integer,N}, -) where {G,H,N} - sh = myempty(H) # shared - return map(indices) do index - HessianTracer{G,H,true}(seed(G, index), sh) - end -end - -function create_tracers( - ::Type{Dual{P,HessianTracer{G,H,true}}}, - xs::AbstractArray{<:Real,N}, - indices::AbstractArray{<:Integer,N}, -) where {P<:Real,G,H,N} - sh = myempty(H) # shared - return map(xs, indices) do x, index - Dual(x, HessianTracer{G,H,true}(seed(G, index), sh)) - end + ::Type{D}, xs::AbstractArray{<:Real,N}, indices::AbstractArray{<:Integer,N} +) where {P,T,D<:Dual{P,T},N} + tracers = create_tracers(T, xs, indices) + return D.(xs, tracers) end # Pretty-printing of Dual tracers diff --git a/test/brusselator.jl b/test/brusselator.jl index c2cae04..51e897f 100644 --- a/test/brusselator.jl +++ b/test/brusselator.jl @@ -6,7 +6,7 @@ using SparseConnectivityTracer: DuplicateVector, RecursiveSet, SortedVector using SparseConnectivityTracerBenchmarks.ODE: Brusselator! using Test -# Load definitions of GRADIENT_TRACERS and HESSIAN_TRACERS +# Load definitions of GRADIENT_TRACERS, GRADIENT_PATTERNS, HESSIAN_TRACERS and HESSIAN_PATTERNS include("tracers_definitions.jl") function test_brusselator(method::AbstractSparsityDetector) diff --git a/test/flux.jl b/test/flux.jl index d28f7bd..d6db1b3 100644 --- a/test/flux.jl +++ b/test/flux.jl @@ -6,7 +6,7 @@ using SparseConnectivityTracer using SparseConnectivityTracer: DuplicateVector, RecursiveSet, SortedVector using Test -# Load definitions of GRADIENT_TRACERS and HESSIAN_TRACERS +# Load definitions of GRADIENT_TRACERS, GRADIENT_PATTERNS, HESSIAN_TRACERS and HESSIAN_PATTERNS include("tracers_definitions.jl") const INPUT_FLUX = reshape( diff --git a/test/test_constructors.jl b/test/test_constructors.jl index 1bf9ed3..cc8b906 100644 --- a/test/test_constructors.jl +++ b/test/test_constructors.jl @@ -4,7 +4,7 @@ using SparseConnectivityTracer: primal, tracer, isemptytracer using SparseConnectivityTracer: myempty, name using Test -# Load definitions of GRADIENT_TRACERS and HESSIAN_TRACERS +# Load definitions of GRADIENT_TRACERS, GRADIENT_PATTERNS, HESSIAN_TRACERS and HESSIAN_PATTERNS include("tracers_definitions.jl") function test_nested_duals(::Type{T}) where {T<:AbstractTracer} diff --git a/test/test_gradient.jl b/test/test_gradient.jl index 795e851..ee6dcf4 100644 --- a/test/test_gradient.jl +++ b/test/test_gradient.jl @@ -6,7 +6,7 @@ using SpecialFunctions: erf, beta using NNlib: NNlib using Test -# Load definitions of GRADIENT_TRACERS and HESSIAN_TRACERS +# Load definitions of GRADIENT_TRACERS, GRADIENT_PATTERNS, HESSIAN_TRACERS and HESSIAN_PATTERNS include("tracers_definitions.jl") NNLIB_ACTIVATIONS_S = ( @@ -39,7 +39,8 @@ NNLIB_ACTIVATIONS_F = ( NNLIB_ACTIVATIONS = union(NNLIB_ACTIVATIONS_S, NNLIB_ACTIVATIONS_F) @testset "Jacobian Global" begin - @testset "$T" for T in GRADIENT_TRACERS + @testset "$P" for P in GRADIENT_PATTERNS + T = GradientTracer{P} method = TracerSparsityDetector(; gradient_tracer_type=T) f(x) = [x[1]^2, 2 * x[1] * x[2]^2, sin(x[3])] @@ -130,7 +131,8 @@ NNLIB_ACTIVATIONS = union(NNLIB_ACTIVATIONS_S, NNLIB_ACTIVATIONS_F) end @testset "Jacobian Local" begin - @testset "$T" for T in GRADIENT_TRACERS + @testset "$P" for P in GRADIENT_PATTERNS + T = GradientTracer{P} method = TracerLocalSparsityDetector(; gradient_tracer_type=T) # Multiplication diff --git a/test/test_hessian.jl b/test/test_hessian.jl index 9fcbed4..af0da26 100644 --- a/test/test_hessian.jl +++ b/test/test_hessian.jl @@ -1,14 +1,16 @@ using SparseConnectivityTracer -using SparseConnectivityTracer: Dual, HessianTracer, MissingPrimalError, trace_input, empty +using SparseConnectivityTracer: Dual, HessianTracer, MissingPrimalError +using SparseConnectivityTracer: trace_input, create_tracers, pattern, isshared using ADTypes: hessian_sparsity using SpecialFunctions: erf, beta using Test -# Load definitions of GRADIENT_TRACERS and HESSIAN_TRACERS +# Load definitions of GRADIENT_TRACERS, GRADIENT_PATTERNS, HESSIAN_TRACERS and HESSIAN_PATTERNS include("tracers_definitions.jl") @testset "Global Hessian" begin - @testset "$T" for T in HESSIAN_TRACERS + @testset "$P" for P in HESSIAN_PATTERNS + T = HessianTracer{P} method = TracerSparsityDetector(; hessian_tracer_type=T) @test hessian_sparsity(identity, rand(), method) ≈ [0;;] @@ -243,7 +245,8 @@ include("tracers_definitions.jl") end @testset "Local Hessian" begin - @testset "$T" for T in HESSIAN_TRACERS + @testset "$P" for P in HESSIAN_PATTERNS + T = HessianTracer{P} method = TracerLocalSparsityDetector(; hessian_tracer_type=T) f1(x) = x[1] + x[2] * x[3] + 1 / x[4] + x[2] * max(x[1], x[5]) @@ -337,8 +340,8 @@ end end end -@testset "Shared HessianTracer - same objects" begin - H = HessianTracer{BitSet,Set{Tuple{Int,Int}},true} +@testset "Shared IndexSetHessianPattern - same objects" begin + H = HessianTracer{IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}},true}} function multi_output_for_shared_test(x::AbstractArray) z = ones(eltype(x), size(x)) @@ -354,7 +357,7 @@ end xt = create_tracers(H, x, eachindex(x)) yt = multi_output_for_shared_test(xt) - @test yt[1].hessian === yt[2].hessian - @test yt[1].hessian === yt[3].hessian - @test_broken yt[1].hessian === yt[4].hessian + @test pattern(yt[1]).hessian === pattern(yt[2]).hessian + @test pattern(yt[1]).hessian === pattern(yt[3]).hessian + @test_broken pattern(yt[1]).hessian === pattern(yt[4]).hessian end diff --git a/test/tracers_definitions.jl b/test/tracers_definitions.jl index 671f3e2..6b49ed1 100644 --- a/test/tracers_definitions.jl +++ b/test/tracers_definitions.jl @@ -2,7 +2,7 @@ using SparseConnectivityTracer: AbstractTracer, GradientTracer, HessianTracer, D using SparseConnectivityTracer: IndexSetGradientPattern, IndexSetHessianPattern using SparseConnectivityTracer: DuplicateVector, RecursiveSet, SortedVector -VECTOR_PATTERNS = ( +GRADIENT_PATTERNS = ( IndexSetGradientPattern{Int,BitSet}, IndexSetGradientPattern{Int,Set{Int}}, IndexSetGradientPattern{Int,DuplicateVector{Int}}, @@ -10,12 +10,13 @@ VECTOR_PATTERNS = ( ) HESSIAN_PATTERNS = ( - IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}}}, - IndexSetHessianPattern{Int,Set{Int},Set{Tuple{Int,Int}}}, - IndexSetHessianPattern{Int,DuplicateVector{Int},DuplicateVector{Tuple{Int,Int}}}, - IndexSetHessianPattern{Int,SortedVector{Int},SortedVector{Tuple{Int,Int}}}, + IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}},true}, + IndexSetHessianPattern{Int,BitSet,Set{Tuple{Int,Int}},false}, + IndexSetHessianPattern{Int,Set{Int},Set{Tuple{Int,Int}},false}, + IndexSetHessianPattern{Int,DuplicateVector{Int},DuplicateVector{Tuple{Int,Int}},false}, + IndexSetHessianPattern{Int,SortedVector{Int},SortedVector{Tuple{Int,Int}},false}, # TODO: test on RecursiveSet ) -GRADIENT_TRACERS = (GradientTracer{P} for P in VECTOR_PATTERNS) +GRADIENT_TRACERS = (GradientTracer{P} for P in GRADIENT_PATTERNS) HESSIAN_TRACERS = (HessianTracer{P} for P in HESSIAN_PATTERNS)