From 6bead4b43010d8246fbac21472342b8205c99cd2 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 25 Nov 2024 11:48:40 +0100 Subject: [PATCH 1/5] Move output parsing into separate file --- src/SparseConnectivityTracer.jl | 1 + src/parse_outputs_to_mat.jl | 71 +++++++++++++++++++++++++++++ src/trace_functions.jl | 79 ++++----------------------------- 3 files changed, 81 insertions(+), 70 deletions(-) create mode 100644 src/parse_outputs_to_mat.jl diff --git a/src/SparseConnectivityTracer.jl b/src/SparseConnectivityTracer.jl index bfa47015..d9b74a9f 100644 --- a/src/SparseConnectivityTracer.jl +++ b/src/SparseConnectivityTracer.jl @@ -31,6 +31,7 @@ include("overloads/arrays.jl") include("overloads/ambiguities.jl") include("trace_functions.jl") +include("parse_outputs_to_mat.jl") include("adtypes_interface.jl") export TracerSparsityDetector diff --git a/src/parse_outputs_to_mat.jl b/src/parse_outputs_to_mat.jl new file mode 100644 index 00000000..5a1bb5ba --- /dev/null +++ b/src/parse_outputs_to_mat.jl @@ -0,0 +1,71 @@ +#= Parse tracers from function evaluation in `src/trace_functions.jl` into an output matrix. =# + +#==========# +# Jacobian # +#==========# + +function jacobian_pattern_to_mat( + xt::AbstractArray{T}, yt::AbstractArray{<:Real} +) where {T<:GradientTracer} + n, m = length(xt), length(yt) + I = Int[] # row indices + J = Int[] # column indices + V = Bool[] # values + for (i, y) in enumerate(yt) + if y isa T && !isemptytracer(y) + for j in gradient(y) + push!(I, i) + push!(J, j) + push!(V, true) + end + end + end + return sparse(I, J, V, m, n) +end + +function jacobian_pattern_to_mat( + xt::AbstractArray{D}, yt::AbstractArray{<:Real} +) where {P,T<:GradientTracer,D<:Dual{P,T}} + return jacobian_pattern_to_mat(tracer.(xt), _tracer_or_number.(yt)) +end + +#=========# +# Hessian # +#=========# + +function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::T) where {T<:HessianTracer} + n = length(xt) + I = Int[] # row indices + J = Int[] # column indices + V = Bool[] # values + + if !isemptytracer(yt) + for (i, j) in tuple_set(hessian(yt)) + push!(I, i) + push!(J, j) + push!(V, true) + # TODO: return `Symmetric` instead on next breaking release + push!(I, j) + push!(J, i) + push!(V, true) + end + end + h = sparse(I, J, V, n, n) + return h +end + +function hessian_pattern_to_mat( + xt::AbstractArray{D1}, yt::D2 +) where {P1,P2,T<:HessianTracer,D1<:Dual{P1,T},D2<:Dual{P2,T}} + return hessian_pattern_to_mat(tracer.(xt), tracer(yt)) +end + +function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::Number) where {T<:HessianTracer} + return hessian_pattern_to_mat(xt, myempty(T)) +end + +function hessian_pattern_to_mat( + xt::AbstractArray{D1}, yt::Number +) where {P1,T<:HessianTracer,D1<:Dual{P1,T}} + return hessian_pattern_to_mat(tracer.(xt), myempty(T)) +end diff --git a/src/trace_functions.jl b/src/trace_functions.jl index 7d2b52d8..2566d6fd 100644 --- a/src/trace_functions.jl +++ b/src/trace_functions.jl @@ -1,7 +1,8 @@ #= This file handles the actual tracing of functions: 1) creating tracers from inputs 2) evaluating the function with the created tracers -3) parsing the resulting tracers into an output matrix + +The resulting output is parsed in `src/parse_outputs_to_mat.jl`. =# #==================# @@ -62,9 +63,9 @@ to_array(x::AbstractArray) = x _tracer_or_number(x::Real) = x _tracer_or_number(d::Dual) = tracer(d) -#================# -# GradientTracer # -#================# +#==========# +# Jacobian # +#==========# # Compute the sparsity pattern of the Jacobian of `y = f(x)`. function _jacobian_sparsity( @@ -100,34 +101,9 @@ function _local_jacobian_sparsity( return jacobian_pattern_to_mat(to_array(xt), to_array(yt)) end -function jacobian_pattern_to_mat( - xt::AbstractArray{T}, yt::AbstractArray{<:Real} -) where {T<:GradientTracer} - n, m = length(xt), length(yt) - I = Int[] # row indices - J = Int[] # column indices - V = Bool[] # values - for (i, y) in enumerate(yt) - if y isa T && !isemptytracer(y) - for j in gradient(y) - push!(I, i) - push!(J, j) - push!(V, true) - end - end - end - return sparse(I, J, V, m, n) -end - -function jacobian_pattern_to_mat( - xt::AbstractArray{D}, yt::AbstractArray{<:Real} -) where {P,T<:GradientTracer,D<:Dual{P,T}} - return jacobian_pattern_to_mat(tracer.(xt), _tracer_or_number.(yt)) -end - -#===============# -# HessianTracer # -#===============# +#=========# +# Hessian # +#=========# # Compute the sparsity pattern of the Hessian of a scalar function `y = f(x)`. function _hessian_sparsity(f, x, ::Type{T}=DEFAULT_HESSIAN_TRACER) where {T<:HessianTracer} @@ -142,41 +118,4 @@ function _local_hessian_sparsity( D = Dual{eltype(x),T} xt, yt = trace_function(D, f, x) return hessian_pattern_to_mat(to_array(xt), yt) -end - -function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::T) where {T<:HessianTracer} - n = length(xt) - I = Int[] # row indices - J = Int[] # column indices - V = Bool[] # values - - if !isemptytracer(yt) - for (i, j) in tuple_set(hessian(yt)) - push!(I, i) - push!(J, j) - push!(V, true) - # TODO: return `Symmetric` instead on next breaking release - push!(I, j) - push!(J, i) - push!(V, true) - end - end - h = sparse(I, J, V, n, n) - return h -end - -function hessian_pattern_to_mat( - xt::AbstractArray{D1}, yt::D2 -) where {P1,P2,T<:HessianTracer,D1<:Dual{P1,T},D2<:Dual{P2,T}} - return hessian_pattern_to_mat(tracer.(xt), tracer(yt)) -end - -function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::Number) where {T<:HessianTracer} - return hessian_pattern_to_mat(xt, myempty(T)) -end - -function hessian_pattern_to_mat( - xt::AbstractArray{D1}, yt::Number -) where {P1,T<:HessianTracer,D1<:Dual{P1,T}} - return hessian_pattern_to_mat(tracer.(xt), myempty(T)) -end +end \ No newline at end of file From 435aa8f823052ea4afc90b369fde12f45d4448cf Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 25 Nov 2024 11:51:23 +0100 Subject: [PATCH 2/5] Rename functions --- src/SparseConnectivityTracer.jl | 2 +- ...s_to_mat.jl => parse_outputs_to_matrix.jl} | 22 ++++++++++--------- src/trace_functions.jl | 16 +++++++------- 3 files changed, 21 insertions(+), 19 deletions(-) rename src/{parse_outputs_to_mat.jl => parse_outputs_to_matrix.jl} (71%) diff --git a/src/SparseConnectivityTracer.jl b/src/SparseConnectivityTracer.jl index d9b74a9f..5eddc83c 100644 --- a/src/SparseConnectivityTracer.jl +++ b/src/SparseConnectivityTracer.jl @@ -31,7 +31,7 @@ include("overloads/arrays.jl") include("overloads/ambiguities.jl") include("trace_functions.jl") -include("parse_outputs_to_mat.jl") +include("parse_outputs_to_matrix.jl") include("adtypes_interface.jl") export TracerSparsityDetector diff --git a/src/parse_outputs_to_mat.jl b/src/parse_outputs_to_matrix.jl similarity index 71% rename from src/parse_outputs_to_mat.jl rename to src/parse_outputs_to_matrix.jl index 5a1bb5ba..f933e5d6 100644 --- a/src/parse_outputs_to_mat.jl +++ b/src/parse_outputs_to_matrix.jl @@ -4,7 +4,7 @@ # Jacobian # #==========# -function jacobian_pattern_to_mat( +function jacobian_tracers_to_matrix( xt::AbstractArray{T}, yt::AbstractArray{<:Real} ) where {T<:GradientTracer} n, m = length(xt), length(yt) @@ -23,17 +23,17 @@ function jacobian_pattern_to_mat( return sparse(I, J, V, m, n) end -function jacobian_pattern_to_mat( +function jacobian_tracers_to_matrix( xt::AbstractArray{D}, yt::AbstractArray{<:Real} ) where {P,T<:GradientTracer,D<:Dual{P,T}} - return jacobian_pattern_to_mat(tracer.(xt), _tracer_or_number.(yt)) + return jacobian_tracers_to_matrix(tracer.(xt), _tracer_or_number.(yt)) end #=========# # Hessian # #=========# -function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::T) where {T<:HessianTracer} +function hessian_tracers_to_matrix(xt::AbstractArray{T}, yt::T) where {T<:HessianTracer} n = length(xt) I = Int[] # row indices J = Int[] # column indices @@ -54,18 +54,20 @@ function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::T) where {T<:HessianTr return h end -function hessian_pattern_to_mat( +function hessian_tracers_to_matrix( xt::AbstractArray{D1}, yt::D2 ) where {P1,P2,T<:HessianTracer,D1<:Dual{P1,T},D2<:Dual{P2,T}} - return hessian_pattern_to_mat(tracer.(xt), tracer(yt)) + return hessian_tracers_to_matrix(tracer.(xt), tracer(yt)) end -function hessian_pattern_to_mat(xt::AbstractArray{T}, yt::Number) where {T<:HessianTracer} - return hessian_pattern_to_mat(xt, myempty(T)) +function hessian_tracers_to_matrix( + xt::AbstractArray{T}, yt::Number +) where {T<:HessianTracer} + return hessian_tracers_to_matrix(xt, myempty(T)) end -function hessian_pattern_to_mat( +function hessian_tracers_to_matrix( xt::AbstractArray{D1}, yt::Number ) where {P1,T<:HessianTracer,D1<:Dual{P1,T}} - return hessian_pattern_to_mat(tracer.(xt), myempty(T)) + return hessian_tracers_to_matrix(tracer.(xt), myempty(T)) end diff --git a/src/trace_functions.jl b/src/trace_functions.jl index 2566d6fd..36680c68 100644 --- a/src/trace_functions.jl +++ b/src/trace_functions.jl @@ -2,7 +2,7 @@ 1) creating tracers from inputs 2) evaluating the function with the created tracers -The resulting output is parsed in `src/parse_outputs_to_mat.jl`. +The resulting output is parsed in `src/parse_outputs_to_matrix.jl`. =# #==================# @@ -72,7 +72,7 @@ function _jacobian_sparsity( f, x, ::Type{T}=DEFAULT_GRADIENT_TRACER ) where {T<:GradientTracer} xt, yt = trace_function(T, f, x) - return jacobian_pattern_to_mat(to_array(xt), to_array(yt)) + return jacobian_tracers_to_matrix(to_array(xt), to_array(yt)) end # Compute the sparsity pattern of the Jacobian of `f!(y, x)`. @@ -80,7 +80,7 @@ function _jacobian_sparsity( f!, y, x, ::Type{T}=DEFAULT_GRADIENT_TRACER ) where {T<:GradientTracer} xt, yt = trace_function(T, f!, y, x) - return jacobian_pattern_to_mat(to_array(xt), to_array(yt)) + return jacobian_tracers_to_matrix(to_array(xt), to_array(yt)) end # Compute the local sparsity pattern of the Jacobian of `y = f(x)` at `x`. @@ -89,7 +89,7 @@ function _local_jacobian_sparsity( ) where {T<:GradientTracer} D = Dual{eltype(x),T} xt, yt = trace_function(D, f, x) - return jacobian_pattern_to_mat(to_array(xt), to_array(yt)) + return jacobian_tracers_to_matrix(to_array(xt), to_array(yt)) end # Compute the local sparsity pattern of the Jacobian of `f!(y, x)` at `x`. @@ -98,7 +98,7 @@ function _local_jacobian_sparsity( ) where {T<:GradientTracer} D = Dual{eltype(x),T} xt, yt = trace_function(D, f!, y, x) - return jacobian_pattern_to_mat(to_array(xt), to_array(yt)) + return jacobian_tracers_to_matrix(to_array(xt), to_array(yt)) end #=========# @@ -108,7 +108,7 @@ end # Compute the sparsity pattern of the Hessian of a scalar function `y = f(x)`. function _hessian_sparsity(f, x, ::Type{T}=DEFAULT_HESSIAN_TRACER) where {T<:HessianTracer} xt, yt = trace_function(T, f, x) - return hessian_pattern_to_mat(to_array(xt), yt) + return hessian_tracers_to_matrix(to_array(xt), yt) end # Compute the local sparsity pattern of the Hessian of a scalar function `y = f(x)` at `x`. @@ -117,5 +117,5 @@ function _local_hessian_sparsity( ) where {T<:HessianTracer} D = Dual{eltype(x),T} xt, yt = trace_function(D, f, x) - return hessian_pattern_to_mat(to_array(xt), yt) -end \ No newline at end of file + return hessian_tracers_to_matrix(to_array(xt), yt) +end From 14d2ba3e0c41b58e5b0222b2aa8d13380b359e21 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 25 Nov 2024 11:56:42 +0100 Subject: [PATCH 3/5] Loosen type restrictions --- src/parse_outputs_to_matrix.jl | 7 +++++-- src/trace_functions.jl | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/parse_outputs_to_matrix.jl b/src/parse_outputs_to_matrix.jl index f933e5d6..69687dd9 100644 --- a/src/parse_outputs_to_matrix.jl +++ b/src/parse_outputs_to_matrix.jl @@ -1,11 +1,14 @@ #= Parse tracers from function evaluation in `src/trace_functions.jl` into an output matrix. =# +_tracer_or_number(x::Real) = x +_tracer_or_number(d::Dual) = tracer(d) + #==========# # Jacobian # #==========# function jacobian_tracers_to_matrix( - xt::AbstractArray{T}, yt::AbstractArray{<:Real} + xt::AbstractArray{T}, yt::AbstractArray ) where {T<:GradientTracer} n, m = length(xt), length(yt) I = Int[] # row indices @@ -24,7 +27,7 @@ function jacobian_tracers_to_matrix( end function jacobian_tracers_to_matrix( - xt::AbstractArray{D}, yt::AbstractArray{<:Real} + xt::AbstractArray{D}, yt::AbstractArray ) where {P,T<:GradientTracer,D<:Dual{P,T}} return jacobian_tracers_to_matrix(tracer.(xt), _tracer_or_number.(yt)) end diff --git a/src/trace_functions.jl b/src/trace_functions.jl index 36680c68..d678119a 100644 --- a/src/trace_functions.jl +++ b/src/trace_functions.jl @@ -59,10 +59,6 @@ end to_array(x::Real) = [x] to_array(x::AbstractArray) = x -# Utilities -_tracer_or_number(x::Real) = x -_tracer_or_number(d::Dual) = tracer(d) - #==========# # Jacobian # #==========# From 30fec218cd7323ab7216ff3a835c367a39bd65af Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 25 Nov 2024 13:03:05 +0100 Subject: [PATCH 4/5] Add tests --- test/test_gradient.jl | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/test_gradient.jl b/test/test_gradient.jl index 2c515e13..87dc127e 100644 --- a/test/test_gradient.jl +++ b/test/test_gradient.jl @@ -123,6 +123,36 @@ J(f, x) = jacobian_sparsity(f, x, detector) x -> x[1] > x[2] ? x[3] : x[4], [1.0, 2.0, 3.0, 4.0] ) == [0 0 1 1;] end + + @testset "Output of type Vector{Any}" begin + function f(x::AbstractVector) + n = length(x) + ret = [] # return type will be Vector{Any} + for i in 1:(n - 1) + append!( + ret, + abs2(x[i + 1]) - abs2(x[i]) + abs2(x[n - i]) - abs2(x[n - i + 1]), + ) + end + return ret + end + x = [ + 0.263914 + 0.605532 + 1.281598 + 1.413663 + 0.178133 + -1.705427 + ] + @test J(f, x) == [ + 1 1 0 0 1 1 + 0 1 1 1 1 0 + 0 0 1 1 0 0 + 0 1 1 1 1 0 + 1 1 0 0 1 1 + ] + end + yield() end end @@ -248,6 +278,35 @@ end @test J(x -> log(det(x)), [1.0 -1.0; 2.0 2.0]) == [1 1 1 1] @test J(x -> dot(x[1:2], x[4:5]), [0, 1, 0, 1, 0]) == [1 0 0 0 1] end + @testset "Output of type Vector{Any}" begin + function f(x::AbstractVector) + n = length(x) + ret = [] # return type will be Vector{Any} + for i in 1:(n - 1) + append!( + ret, + abs2(x[i + 1]) - abs2(x[i]) + abs2(x[n - i]) - abs2(x[n - i + 1]), + ) + end + return ret + end + x = [ + 0.263914 + 0.605532 + 1.281598 + 1.413663 + 0.178133 + -1.705427 + ] + @test J(f, x) == [ + 1 1 0 0 1 1 + 0 1 1 1 1 0 + 0 0 1 1 0 0 + 0 1 1 1 1 0 + 1 1 0 0 1 1 + ] + end + yield() end end From ab56d5992ba5f827438e83049177821cfb773925 Mon Sep 17 00:00:00 2001 From: adrhill Date: Mon, 25 Nov 2024 13:07:27 +0100 Subject: [PATCH 5/5] Bump version number --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index af377123..47030898 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SparseConnectivityTracer" uuid = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" authors = ["Adrian Hill "] -version = "0.6.8" +version = "0.6.9-DEV" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"