Deferred computation for precompilable tags?
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
Direzione di ricerca
Inizia dal commento referenziato di ForwardDiff PR #807 e ispeziona l’attuale percorso runtime tagcount. Confronta l’approccio Deferred proposto con autodiff_deferred di Enzyme; il completamento richiederebbe un design concordato per la differenziazione annidata e test che coprano l’esempio fornito OuterF/InnerF.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
I was looking at https://github.com/JuliaDiff/ForwardDiff.jl/pull/807 and this comment caught my attention:
The pattern (outer derivative of an inner scalar derivative whose closure captures the outer point) is the canonical scalar nested derivative — it isn't exotic, and it's exactly what would be expected to suffer from precompile-induced tagcount inversion in downstream packages.
When the inner derivative is seeded with a literal Float64, the inner tag is Tag{, Float64} with depth 1 — even though that closure captures a Dual{T_middle, ..., 2} (the outer-nesting context is in F, not in V)
As of ForwardDiff.jl v1.4 the closure-over-AD pattern is solved via the runtime tagcount mechanism.
another known AD framework with forward mode is Enzyme, and they explicitly provide a separate function to allow nested differentiation, namely (autodiff_deferred).
is it possible to implement a similar mechanism in ForwardDiff?. Instead of recursing over the closure type, at closure construction a Deferred{F,T} struct is created that stores, along with the closure, tag information that could be exposed to the outer ForwardDiff contexts?. For example here:
using ForwardDiff
const Tag = ForwardDiff.Tag
struct OuterF end
struct InnerF
x_dual::ForwardDiff.Dual{Tag{OuterF, Float64}, Float64, 1}
end
(c::InnerF)(y) = sin(c.x_dual * y)
(::OuterF)(x_dual::ForwardDiff.Dual{Tag{OuterF, Float64}, Float64, 1}) =
ForwardDiff.derivative(InnerF(x_dual), 1.0)
# Force tagcount to be evaluated in INVERTED order (simulating a precompile race):
ForwardDiff.tagcount(Tag{InnerF, Float64}) # returns 0
ForwardDiff.tagcount(Tag{OuterF, Float64}) # returns 1
# Analytic: d/dx (d/dy sin(x*y)|_{y=1}) at x=0.5 = cos(0.5) - 0.5*sin(0.5) ≈ 0.6378697925882713
ForwardDiff.derivative(OuterF(), 0.5)
The current path creates the two tags Tag{InnerF, Float64} and Tag{OuterF, Float64}. But if we had something like:
struct Deferred{F,P}
F::F #function to be evaluated
p::P #context
end
(f::Deferred{FF,PP})(x) where {FF,PP} = f.F(f.p)(x)
#TODO: generalize for abstract arrays and other contexts.
deferred_valtype(::Type{V},p::T) where {V<:Number,T<:Number} = promote_type(V,T)
function ForwardDiff.Tag(f::Deferred{F,P},::Type{V}) where {F,P,V}
return ForwardDiff.Tag(f.F,deferred_valtype(V,f.p))
end
#use deferred instead of instantiating InnerF directly
(::OuterF)(x_dual::ForwardDiff.Dual{Tag{OuterF, Float64}, Float64, 1}) =
ForwardDiff.derivative(Deferred(InnerF,x_dual), 1.0)
#a macro for user convenience:
#(::OuterF)(x_dual::ForwardDiff.Dual{Tag{OuterF, Float64}, Float64, 1}) =
ForwardDiff.derivative(@deferred(InnerF(x_dual)), 1.0)
ForwardDiff.derivative(OuterF(), 0.5) #0.6378697925882713
In this case, the tags created would be Tag{OuterF,Float64} and Tag{InnerF,Dual{OuterF, Float64}, Float64, 1}, but that is because we transformed the problem into one that is more amenable to what ForwardDiff already does, while giving a path for tag comparisons that don't depend on a global variable that could change between sessions
- Lingua principale
- Julia
- Stelle
- 1k
- Fork
- 162
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di JuliaDiff/ForwardDiff.jl
-
missing floatmin, floatmax, and issubnormal for DualForse già presa @devmotion l’ha presa 15 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
JuliaDiff/ForwardDiff.jl#852 · 6 commenti ·
-
`fma` and `muladd` ignore NaN-safe modeForse già presa @devmotion l’ha presa oggi. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 55/100
JuliaDiff/ForwardDiff.jl#860 ·
-
Enable NaN-safe mode by defaultForse già presa @devmotion l’ha presa 2 giorni fa. Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
JuliaDiff/ForwardDiff.jl#857 · 1 reazione ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 52/100
JuliaDiff/ForwardDiff.jl#854 · 1 commento ·
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 75/100
JuliaDiff/ForwardDiff.jl#849 ·
Tutte le issue di JuliaDiff/ForwardDiff.jl
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
SciML/OrdinaryDiffEq.jl#4875 ·
I maintainer di solito rispondono entro 3 giorni
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
SciML/NonlinearSolve.jl#1372 ·
I maintainer di solito rispondono entro 1 giorno
-
Truncated discrete ccdf and logccdf return incorrect values at the lower atomForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
JuliaStats/Distributions.jl#2095 ·
-
documentation 📜 microphysics💧 ❄️
Difficoltà 2/5 1-3 ore Idoneità per principianti 63/100
NumericalEarth/Breeze.jl#1073 ·
I maintainer di solito rispondono entro 1 giorno