Line | Exclusive | Inclusive | Code |
---|---|---|---|
1 | struct Partials{N,V} <: AbstractVector{V} | ||
2 | values::NTuple{N,V} | ||
3 | end | ||
4 | |||
5 | ############################## | ||
6 | # Utility/Accessor Functions # | ||
7 | ############################## | ||
8 | |||
9 | @generated function single_seed(::Type{Partials{N,V}}, ::Val{i}) where {N,V,i} | ||
10 | ex = Expr(:tuple, [ifelse(i === j, :(one(V)), :(zero(V))) for j in 1:N]...) | ||
11 | return :(Partials($(ex))) | ||
12 | end | ||
13 | |||
14 | @inline valtype(::Partials{N,V}) where {N,V} = V | ||
15 | @inline valtype(::Type{Partials{N,V}}) where {N,V} = V | ||
16 | |||
17 | @inline npartials(::Partials{N}) where {N} = N | ||
18 | @inline npartials(::Type{Partials{N,V}}) where {N,V} = N | ||
19 | |||
20 | @inline Base.length(::Partials{N}) where {N} = N | ||
21 | @inline Base.size(::Partials{N}) where {N} = (N,) | ||
22 | |||
23 | @inline Base.@propagate_inbounds Base.getindex(partials::Partials, i::Int) = partials.values[i] | ||
24 | |||
25 | Base.iterate(partials::Partials) = iterate(partials.values) | ||
26 | Base.iterate(partials::Partials, i) = iterate(partials.values, i) | ||
27 | |||
28 | Base.IndexStyle(::Type{<:Partials}) = IndexLinear() | ||
29 | |||
30 | # Can be deleted after https://github.com/JuliaLang/julia/pull/29854 is on a release | ||
31 | Base.mightalias(x::AbstractArray, y::Partials) = false | ||
32 | |||
33 | ##################### | ||
34 | # Generic Functions # | ||
35 | ##################### | ||
36 | |||
37 | @inline Base.iszero(partials::Partials) = iszero_tuple(partials.values) | ||
38 | |||
39 | @inline Base.zero(partials::Partials) = zero(typeof(partials)) | ||
40 | @inline Base.zero(::Type{Partials{N,V}}) where {N,V} = Partials{N,V}(zero_tuple(NTuple{N,V})) | ||
41 | |||
42 | @inline Base.one(partials::Partials) = one(typeof(partials)) | ||
43 | @inline Base.one(::Type{Partials{N,V}}) where {N,V} = Partials{N,V}(one_tuple(NTuple{N,V})) | ||
44 | |||
45 | @inline Random.rand(partials::Partials) = rand(typeof(partials)) | ||
46 | @inline Random.rand(::Type{Partials{N,V}}) where {N,V} = Partials{N,V}(rand_tuple(NTuple{N,V})) | ||
47 | @inline Random.rand(rng::AbstractRNG, partials::Partials) = rand(rng, typeof(partials)) | ||
48 | @inline Random.rand(rng::AbstractRNG, ::Type{Partials{N,V}}) where {N,V} = Partials{N,V}(rand_tuple(rng, NTuple{N,V})) | ||
49 | |||
50 | Base.isequal(a::Partials{N}, b::Partials{N}) where {N} = isequal(a.values, b.values) | ||
51 | Base.:(==)(a::Partials{N}, b::Partials{N}) where {N} = a.values == b.values | ||
52 | |||
53 | const PARTIALS_HASH = hash(Partials) | ||
54 | |||
55 | Base.hash(partials::Partials) = hash(partials.values, PARTIALS_HASH) | ||
56 | Base.hash(partials::Partials, hsh::UInt64) = hash(hash(partials), hsh) | ||
57 | |||
58 | @inline Base.copy(partials::Partials) = partials | ||
59 | |||
60 | Base.read(io::IO, ::Type{Partials{N,V}}) where {N,V} = Partials{N,V}(ntuple(i->read(io, V), N)) | ||
61 | |||
62 | function Base.write(io::IO, partials::Partials) | ||
63 | for p in partials | ||
64 | write(io, p) | ||
65 | end | ||
66 | end | ||
67 | |||
68 | ######################## | ||
69 | # Conversion/Promotion # | ||
70 | ######################## | ||
71 | |||
72 | Base.promote_rule(::Type{Partials{N,A}}, ::Type{Partials{N,B}}) where {N,A,B} = Partials{N,promote_type(A, B)} | ||
73 | |||
74 | Base.convert(::Type{Partials{N,V}}, partials::Partials) where {N,V} = Partials{N,V}(partials.values) | ||
75 | Base.convert(::Type{Partials{N,V}}, partials::Partials{N,V}) where {N,V} = partials | ||
76 | |||
77 | ######################## | ||
78 | # Arithmetic Functions # | ||
79 | ######################## | ||
80 | |||
81 | 2 (1 %) |
2 (100 %)
samples spent calling
add_tuples
@inline Base.:+(a::Partials{N}, b::Partials{N}) where {N} = Partials(add_tuples(a.values, b.values))
|
|
82 | 4 (1 %) |
4 (100 %)
samples spent calling
sub_tuples
@inline Base.:-(a::Partials{N}, b::Partials{N}) where {N} = Partials(sub_tuples(a.values, b.values))
|
|
83 | @inline Base.:-(partials::Partials) = Partials(minus_tuple(partials.values)) | ||
84 | 2 (1 %) |
2 (1 %)
samples spent in *
1 (50 %) (incl.) when called from literal_pow line 565 1 (50 %) (incl.) when called from dual_definition_retval line 200
2 (100 %)
samples spent calling
*
@inline Base.:*(x::Real, partials::Partials) = partials*x
|
|
85 | |||
86 | @inline function _div_partials(a::Partials, b::Partials, aval, bval) | ||
87 | return _mul_partials(a, b, inv(bval), -(aval / (bval*bval))) | ||
88 | end | ||
89 | |||
90 | # NaN/Inf-safe methods # | ||
91 | #----------------------# | ||
92 | |||
93 | if NANSAFE_MODE_ENABLED | ||
94 | @inline function Base.:*(partials::Partials, x::Real) | ||
95 | x = ifelse(!isfinite(x) && iszero(partials), one(x), x) | ||
96 | return Partials(scale_tuple(partials.values, x)) | ||
97 | end | ||
98 | |||
99 | @inline function Base.:/(partials::Partials, x::Real) | ||
100 | x = ifelse(x == zero(x) && iszero(partials), one(x), x) | ||
101 | return Partials(div_tuple_by_scalar(partials.values, x)) | ||
102 | end | ||
103 | |||
104 | @inline function _mul_partials(a::Partials{N}, b::Partials{N}, x_a, x_b) where N | ||
105 | x_a = ifelse(!isfinite(x_a) && iszero(a), one(x_a), x_a) | ||
106 | x_b = ifelse(!isfinite(x_b) && iszero(b), one(x_b), x_b) | ||
107 | return Partials(mul_tuples(a.values, b.values, x_a, x_b)) | ||
108 | end | ||
109 | else | ||
110 | @inline function Base.:*(partials::Partials, x::Real) | ||
111 | 2 (1 %) |
2 (100 %)
samples spent calling
scale_tuple
return Partials(scale_tuple(partials.values, x))
|
|
112 | end | ||
113 | |||
114 | @inline function Base.:/(partials::Partials, x::Real) | ||
115 | return Partials(div_tuple_by_scalar(partials.values, x)) | ||
116 | end | ||
117 | |||
118 | @inline function _mul_partials(a::Partials{N}, b::Partials{N}, x_a, x_b) where N | ||
119 | 15 (6 %) |
15 (6 %)
samples spent in _mul_partials
15 (100 %) (incl.) when called from dual_definition_retval line 203
15 (100 %)
samples spent calling
mul_tuples
return Partials(mul_tuples(a.values, b.values, x_a, x_b))
|
|
120 | end | ||
121 | end | ||
122 | |||
123 | # edge cases where N == 0 # | ||
124 | #-------------------------# | ||
125 | |||
126 | @inline Base.:+(a::Partials{0,A}, b::Partials{0,B}) where {A,B} = Partials{0,promote_type(A,B)}(tuple()) | ||
127 | @inline Base.:+(a::Partials{0,A}, b::Partials{N,B}) where {N,A,B} = convert(Partials{N,promote_type(A,B)}, b) | ||
128 | @inline Base.:+(a::Partials{N,A}, b::Partials{0,B}) where {N,A,B} = convert(Partials{N,promote_type(A,B)}, a) | ||
129 | |||
130 | @inline Base.:-(a::Partials{0,A}, b::Partials{0,B}) where {A,B} = Partials{0,promote_type(A,B)}(tuple()) | ||
131 | @inline Base.:-(a::Partials{0,A}, b::Partials{N,B}) where {N,A,B} = -(convert(Partials{N,promote_type(A,B)}, b)) | ||
132 | @inline Base.:-(a::Partials{N,A}, b::Partials{0,B}) where {N,A,B} = convert(Partials{N,promote_type(A,B)}, a) | ||
133 | @inline Base.:-(partials::Partials{0,V}) where {V} = partials | ||
134 | |||
135 | @inline Base.:*(partials::Partials{0,V}, x::Real) where {V} = Partials{0,promote_type(V,typeof(x))}(tuple()) | ||
136 | @inline Base.:*(x::Real, partials::Partials{0,V}) where {V} = Partials{0,promote_type(V,typeof(x))}(tuple()) | ||
137 | |||
138 | @inline Base.:/(partials::Partials{0,V}, x::Real) where {V} = Partials{0,promote_type(V,typeof(x))}(tuple()) | ||
139 | |||
140 | @inline _mul_partials(a::Partials{0,A}, b::Partials{0,B}, afactor, bfactor) where {A,B} = Partials{0,promote_type(A,B)}(tuple()) | ||
141 | @inline _mul_partials(a::Partials{0,A}, b::Partials{N,B}, afactor, bfactor) where {N,A,B} = bfactor * b | ||
142 | @inline _mul_partials(a::Partials{N,A}, b::Partials{0,B}, afactor, bfactor) where {N,A,B} = afactor * a | ||
143 | |||
144 | ################################## | ||
145 | # Generated Functions on NTuples # | ||
146 | ################################## | ||
147 | # The below functions are generally | ||
148 | # equivalent to directly mapping over | ||
149 | # tuples using `map`, but run a bit | ||
150 | # faster since they generate inline code | ||
151 | # that doesn't rely on closures. | ||
152 | |||
153 | function tupexpr(f, N) | ||
154 | ex = Expr(:tuple, [f(i) for i=1:N]...) | ||
155 | return quote | ||
156 | $(Expr(:meta, :inline)) | ||
157 | 1 (0 %) | 23 (8 %) |
23 (8 %)
samples spent in macro expansion
@inbounds return $ex
15 (65 %) (incl.) when called from mul_tuples line 220 4 (17 %) (incl.) when called from sub_tuples line 212 2 (9 %) (incl.) when called from add_tuples line 208 1 (4 %) (ex.), 2 (9 %) (incl.) when called from scale_tuple line 200 |
158 | end | ||
159 | end | ||
160 | |||
161 | @inline iszero_tuple(::Tuple{}) = true | ||
162 | @inline zero_tuple(::Type{Tuple{}}) = tuple() | ||
163 | @inline one_tuple(::Type{Tuple{}}) = tuple() | ||
164 | @inline rand_tuple(::AbstractRNG, ::Type{Tuple{}}) = tuple() | ||
165 | @inline rand_tuple(::Type{Tuple{}}) = tuple() | ||
166 | |||
167 | @generated function iszero_tuple(tup::NTuple{N,V}) where {N,V} | ||
168 | ex = Expr(:&&, [:(z == tup[$i]) for i=1:N]...) | ||
169 | return quote | ||
170 | z = zero(V) | ||
171 | $(Expr(:meta, :inline)) | ||
172 | @inbounds return $ex | ||
173 | end | ||
174 | end | ||
175 | |||
176 | @generated function zero_tuple(::Type{NTuple{N,V}}) where {N,V} | ||
177 | ex = tupexpr(i -> :(z), N) | ||
178 | return quote | ||
179 | z = zero(V) | ||
180 | return $ex | ||
181 | end | ||
182 | end | ||
183 | |||
184 | @generated function one_tuple(::Type{NTuple{N,V}}) where {N,V} | ||
185 | ex = tupexpr(i -> :(z), N) | ||
186 | return quote | ||
187 | z = one(V) | ||
188 | return $ex | ||
189 | end | ||
190 | end | ||
191 | |||
192 | @generated function rand_tuple(rng::AbstractRNG, ::Type{NTuple{N,V}}) where {N,V} | ||
193 | return tupexpr(i -> :(rand(rng, V)), N) | ||
194 | end | ||
195 | |||
196 | @generated function rand_tuple(::Type{NTuple{N,V}}) where {N,V} | ||
197 | return tupexpr(i -> :(rand(V)), N) | ||
198 | end | ||
199 | |||
200 | 2 (1 %) |
2 (100 %)
samples spent calling
macro expansion
@generated function scale_tuple(tup::NTuple{N}, x) where N
|
|
201 | return tupexpr(i -> :(tup[$i] * x), N) | ||
202 | end | ||
203 | |||
204 | @generated function div_tuple_by_scalar(tup::NTuple{N}, x) where N | ||
205 | return tupexpr(i -> :(tup[$i] / x), N) | ||
206 | end | ||
207 | |||
208 | 2 (1 %) |
2 (100 %)
samples spent calling
macro expansion
@generated function add_tuples(a::NTuple{N}, b::NTuple{N}) where N
|
|
209 | return tupexpr(i -> :(a[$i] + b[$i]), N) | ||
210 | end | ||
211 | |||
212 | 4 (1 %) |
4 (100 %)
samples spent calling
macro expansion
@generated function sub_tuples(a::NTuple{N}, b::NTuple{N}) where N
|
|
213 | return tupexpr(i -> :(a[$i] - b[$i]), N) | ||
214 | end | ||
215 | |||
216 | @generated function minus_tuple(tup::NTuple{N}) where N | ||
217 | return tupexpr(i -> :(-tup[$i]), N) | ||
218 | end | ||
219 | |||
220 | 15 (6 %) |
15 (100 %)
samples spent calling
macro expansion
@generated function mul_tuples(a::NTuple{N}, b::NTuple{N}, afactor, bfactor) where N
|
|
221 | return tupexpr(i -> :((afactor * a[$i]) + (bfactor * b[$i])), N) | ||
222 | end | ||
223 | |||
224 | ################### | ||
225 | # Pretty Printing # | ||
226 | ################### | ||
227 | |||
228 | Base.show(io::IO, p::Partials{N}) where {N} = print(io, "Partials", p.values) |