New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error Propagation #46
Conversation
Note that Measurement.jl implements (some form of - see below) linear error propagation as well (see here). It uses Calculus.jl to calculate the necessary derivatives. Note the caveats and warnings on wikipedia regarding linear error propagation. |
Also, there is MonteCarloMeasurements.jl (not related to Markov chain Monte Carlo) which uses a random sampling method (thus dubbed "MonteCarlo") to go beyond Measurements.jl and handle nonlinear uncertainty propagation well. It falls into the first of 5 categories listed here on wikipedia to handle strong non-linear functions. |
Maybe http://mathfaculty.fullerton.edu/sbehseta/Pages%20from%20KassBehseta.pdf is a good read to understand the differences between
Regarding the difference between 2 and 3 see also this answer on stackexchange. |
Stupid test on this branch: using Measurements, MonteCarloMeasurements, BinningAnalysis, Distributions, Statistics
function error_BinningAnalysis(f, x, y)
ep = Error_Propagator1()
for i in 1:length(x)
push!(ep, x[i], y[i])
end
sqrt(BinningAnalysis.var_O1(f, ep))
end
function error_Measurements(f, x, y)
mx = measurement(mean(x), std(x))
my = measurement(mean(y), std(y))
Measurements.uncertainty(f(mx,my)) / sqrt(length(x))
end
function error_MonteCarloMeasurements(f, x, y)
px = Particles(x)
py = Particles(y)
std(f(px,py)) / sqrt(length(x))
end
x = rand(100)
y = x .+ rand(Normal(0., 0.2), 100)
f = (x,y) -> sin(abs(x))/log(y+1) # crazy non-linear function
@btime error_BinningAnalysis($f, $x, $y)
@btime error_Measurements($f, $x, $y)
@btime error_MonteCarloMeasurements($f, $x, $y)
@btime jackknife($f, $x, $y)[2] This is what I get julia> @btime error_BinningAnalysis($f, $x, $y)
287.129 ns (3 allocations: 96 bytes)
0.03476324406512978
julia> @btime error_Measurements($f, $x, $y)
352.799 ns (16 allocations: 752 bytes)
0.09049688511741694
julia> @btime error_MonteCarloMeasurements($f, $x, $y)
3.212 μs (18 allocations: 4.59 KiB)
1.248968883896152
julia> @btime jackknife($f, $x, $y)[2]
4.957 μs (28 allocations: 3.33 KiB)
0.03473403285416829 Assuming that MonteCarloMeasurements.jl's method is the most reliable, the other methods are completely off for this (crazy) non-linear function. Why does 1 and 4 almost perfectly agree here? For a simple linear function we find: julia> f = (x,y) -> x + y
#32 (generic function with 1 method)
julia> @btime error_BinningAnalysis($f, $x, $y)
240.098 ns (3 allocations: 96 bytes)
0.06111615859525542
julia> @btime error_Measurements($f, $x, $y)
236.547 ns (8 allocations: 368 bytes)
0.04508900420179231
julia> @btime error_MonteCarloMeasurements($f, $x, $y)
707.307 ns (7 allocations: 1008 bytes)
0.06111615859525539
julia> @btime jackknife($f, $x, $y)[2]
3.400 μs (28 allocations: 3.33 KiB)
0.06111615859525534 One would expect almost perfect agreement between all methods. Why is Measurements off here? It looks like Measurements.jl is doing a simplified linear error propagation which amounts to dropping the covariance terms. See the docs
Even simpler test: julia> f = (x,y) -> x
#42 (generic function with 1 method)
julia> @btime error_BinningAnalysis($f, $x, $y)
242.818 ns (3 allocations: 96 bytes)
0.02852033713783177
julia> @btime error_Measurements($f, $x, $y)
192.845 ns (4 allocations: 192 bytes)
0.028520337137831746
julia> @btime error_MonteCarloMeasurements($f, $x, $y)
467.347 ns (2 allocations: 32 bytes)
0.028520337137831746
julia> @btime jackknife($f, $x, $y)[2]
3.462 μs (28 allocations: 3.33 KiB)
0.028520337137831794 Also, julia> z = zeros(eltype(x), size(x));
julia> f = (x,y) -> x + y
#9 (generic function with 1 method)
julia> error_BinningAnalysis(f, x, z)
0.02713603571750006
julia> error_Measurements(f, x, z)
0.027136035717500083 somewhat confirming that Measurements negelects covariances. |
It'd be also interesting to think about which of those methods are usable with correlated input data where (not all elements of |
Just a quick note on my performance tests - I included filling an array (by index, not with |
MonteCarloMeasurement doesn't seem to do error propagation like this. It's simply calculating In @forward @eval($PT).particles Statistics.mean, Statistics.cov, Statistics.var, Statistics.std, Statistics.median, Statistics.quantile, Statistics.middle which just forwards the values in a Particle to the normal definitions of |
I thought that's the idea. Sample the input distribution, propagate the samples through the function and obtain samples of the output distribution (basically this is the first of the two estimators in the QMC book). If the density is high enough this should work I guess. However, I fail to see that this works generally, i.e. for lower densities and arbitrary functions. Maybe I'm also just using the package wrongly. |
Covar.jl should produce identical results to what you are doing here - it's linear propagation theory (LPT) in matrix form - and it proves to be in my tests (that I sent you by email). |
It also makes sense that Jackknife agrees with LPT in most cases as it is basically "LPT + weak non-linearities". It should be closer to the true uncertainty than LPT in non-linear scenarios but probably not by much. Bootstrap should be a way to handle strong non-linearities well. We should implement it as well. If only for comparisons. |
What I mean is that |
I added a third version which includes the I tested the error propagator with the converging data test we have for |
or rather a check that dimensionality between arguments and N_arguments matches. Should be safer and faster
It might be a good idea to let the user supply the partial derivatives for |
Codecov Report
@@ Coverage Diff @@
## master #46 +/- ##
==========================================
+ Coverage 95.87% 96.38% +0.51%
==========================================
Files 7 9 +2
Lines 218 360 +142
==========================================
+ Hits 209 347 +138
- Misses 9 13 +4
Continue to review full report at Codecov.
|
I've rewritten ErrorPropagator in the style of LogBinner, with all its functionality. I dropped the convergence functions though. I also copied the related tests. I dropped ForwardDiff in favor of giving either the gradient as a function, or as a vector of values at I copied the jackknife related tests and adjusted the values for the ´f(x) = identity´ cases. They now reflect the "correct" results obtained from |
I was calculating the convariance matrix incorrectly. The correct formula is I also adjusted the tests now. ErrorPropagator and jackknife only differ for |
This is ready to be reviewed/merged. |
I adjusted the tests again, now I'll look through this stuff again and if I don't find anything wrong with it within a week or so I'll just merge it. |
As an alternative to jackknife, one can estimate the error of some function via an expansion of the expectation value of g() around the mean of x. This is, for example, discussed in the book Quantum Monte Carlo Methods, page 55, 56. Unlike jackknife (and bootstrap) this algorithm can be implemented online.
For the initial commit I added two versions, one using Welfords algorithm (1) and one using summations (2) of, for example x^2 (like in LogBinner). I have done some benchmarking and comparing between the two implementations and jackknife:
g(x, y) = (exp(sin(x)) + exp(-cos(y))) ^ (y - x^2) / log(10.0 + x^2 + y)
)TODO