API
Julia's REPL defines its own global variable err (the last thrown error, the companion of ans) in Base.MainInclude. At the REPL prompt, in julia -e scripts, or at the top level of an included file, this variable shadows the err function exported by ADerrors, producing an UndefVarError. Inside functions and user modules bare err(...) always works. At the top level of a script or at the REPL, use the qualified form ADerrors.err(...).
Observables: creating uwreal values
ADerrors.uwreal — Type
uwreal(x::Float64)
uwreal([value::Float64, error::Float64], mcid)
uwreal(data::Vector{Float64}, mcid[, replica::Vector{Int64}])
uwreal(data::Vector{Float64}, mcid[, replica::Vector{Int64}], idm::Vector{Int64}, nms::Int64)Returns an uwreal data type. Depending on the first argument, the uwreal stores the following information:
- Input is a single
Float64. In this case the variable acts in exactly the same way as a real number. This is understood as a quantity with zero error. - Input is a 2 element Vector of
Float64[value, error]. In this case the data is understood asvalue +/- error. - Input is a Vector of
Float64of length larger than 4. In this case the data is understood as consecutive measurements of an observable in a Monte Carlo (MC) simulation.
In the last two cases, an ensemble ID is required as input. Data with the same ID are considered as correlated (i.e. fully correlated for the case of a value +/- error observables measured on the same sample for the case of data from a MC simulation). The preferred way to input the ensemble tag is via a String that uniquely identifies the ensemble, but an integer is also supported for legacy reasons. Note that the legacy integer interface is deprecated and a warning is issued on each use; also note that a String name that parses as an integer (e.g. "100") is mapped to the same internal ID as the legacy integer 100, so the two interfaces share the ID space and such names should be avoided. For example:
using ADerrors # hide
a = uwreal([1.2, 0.2], "Simple var with error") # a = 1.2 +/- 0.2
b = uwreal([1.2, 0.2], "Simple var with error") # b = 1.2 +/- 0.2
c = uwreal([1.2, 0.2], "Another var with error") # c = 1.2 +/- 0.2
d = a-b
uwerr(d)
println("d has zero error because a and b are correlated", d)
e = a-c
uwerr(e)
println("e has non zero error because a and c are independent", e)Replica
data can contain measurements in several replica (i.e. independent simulations with the same physical and algorithmic parameters). How many measurements correspond to each replica are specified by an optional replica vector argument replica. Note that length(replica) is the number of replica (i.e. independent simulations), and that sum(replica) must match length(data)
using ADerrors # hide
# 1000 measurements in three replica of lengths
# 500, 100 and 400
a = uwreal(rand(1000), "Ensemble with three replica", [500, 100, 400]) Gaps in the measurements
In some situations an observable is not measured in every configuration. In this case two additional arguments are needed to define the observable
idm. TypeVector{Int64}.idm[n]labels the configuration wheredata[n]is measured.nms. TypeInt64. The total number of measurements in the ensemble
using ADerrors # hide
# Observable measured on the odd configurations
# 1, 3, 5, ..., 999 on an emsemble of length 1000
a = uwreal(rand(500), "Observable with gaps", collect(1:2:999), 1000)
# Observable measured on the first 900 configurations
# on the same emsemble
b = uwreal(rand(900), "Observable with gaps", collect(1:900), 1000)Note that in this case, if the ensemble has different replica, sum(replica) must match nms
using ADerrors # hide
# Observable measured on the even configurations
# 2, 4, 6, ..., 200 on an emsemble of length 200
# with two replica of lengths 75, 125
a = uwreal(data_a[1:500], "Observable with gaps in an ensemble with replica", [75, 125], collect(2:2:200), 200)ADerrors.cobs — Function
cobs(avgs::Vector{Float64}, Mcov::Array{Float64, 2}, str::String)
cobs(avgs::Vector{Float64}, Mcov::Array{Float64, 2}, ids::Vector{Int64})Returns a vector of uwreal such that their mean values are avgs[:] and their covariance is Mcov[:,:]. In order to construct these observables n=length(avgs) ensemble ID are used. These are generated either from the string str or have to be specified in the vector ids[:].
using ADerrors # hide
# Put some average values and covariance
avg = [16.26, 0.12, -0.0038]
Mcov = [0.478071 -0.176116 0.0135305
-0.176116 0.0696489 -0.00554431
0.0135305 -0.00554431 0.000454180]
# Produce observables with ensemble ID
# [1, 2001, 32]. Do error analysis
p = cobs(avg, Mcov, "GF beta function parameters")
uwerr.(p)
# Check central values are ok
avg2 = value.(p)
println("Better be zero: ", sum((avg.-avg2).^2))
# Check that the covariance is ok
Mcov2 = cov(p)
println("Better be zero: ", sum((Mcov.-Mcov2).^2))uwreal values can be created from MC series measured on a named ensemble (or replica ensembles, for several runs with the same simulation parameters), or from central values with externally known covariances (cobs).
Error analysis (uwerr)
ADerrors.uwerr — Function
uwerr(a::uwreal[, wpm::Dict{Int64,Vector{Float64}}])
uwerr(a::uwreal[, wpm::Dict{String,Vector{Float64}}])Performs error analysis on the observable a.
using ADerrors # hide
a = uwreal([1.3, 0.01], "Var with error") # 1.3 +/- 0.01
b = sin(2.0*a)
uwerr(b)
println("Look how I propagate errors: ", b)
c = 1.0 + b - 2.0*sin(a)*cos(a)
uwerr(c)
println("Look how good I am at this (zero error!): ", c)Optimal window
Error in data coming from a Monte Carlo ensemble is determined by summing the autocorrelation function $\Gamma_{\rm ID}(t)$ of the data for each ensemble ID. In practice this sum is truncated up to a window $W_{\rm ID}$.
By default, the summation window is determined as proposed by U. Wolff with a parameter $S_{\tau} = 4$, but other methods are avilable via the optional argument wpm.
For each ensemble, one a pass the following parameters:
window: The autocorrelation function is summed up tot = round(vp[1]).stau: The sumation window is determined using U. Wolff poposal with $S_{\tau} = {\rm vp[2]}$.signal: The autocorrelation function $\Gamma(t)$ is summed up a point where its error $\delta\Gamma(t)$ is a factorvp[3]times larger than the signal.
These three parameters fix the summation window. The following options also affect the error analysis
texp: Add a tail to the autocorrelation function with this value of $\tau_{\rm exp}$. See the reference for an explanation of the procedure.bin: Bin the data before computing the autocorrelation function, usg this value as bin size.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
# Load the data in a uwreal
a = uwreal(x.^2, "Random walk in [-1,1]")
wpm = Dict{String,Dict{String, Real}}()
# Use default analysis (stau = 4.0)
uwerr(a)
println("default: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# This will still do default analysis because
# a does not depend on emsemble foo
wpm["Ensemble foo"] = Dict("window" => 34)
uwerr(a, wpm)
println("default: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# Use default analysis (stau = 4.0), bin data with bin size 5
wpm["Random walk in [-1,1]"] = Dict("bin" => 5)
uwerr(a, wpm)
println("default (bin size 5): ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# Fix the summation window to 1 (i.e. uncorrelated analysis)
wpm["Random walk in [-1,1]"] = Dict("window" => 1)
uwerr(a, wpm)
println("uncorrelated: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# Use stau = 1.5
wpm["Random walk in [-1,1]"] = Dict("stau" => 1.5)
uwerr(a, wpm)
println("stau = 1.5: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# Use fixed window 2 and add tail with texp = 100.0
wpm["Random walk in [-1,1]"] = Dict("window" => 2, "texp" => 100.0)
uwerr(a, wpm)
println("Fixed window 2, texp=100: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))
# Sum up to the point that the signal in Gamma is
# 1.5 times the error and add a tail with texp = 10.0
wpm["Random walk in [-1,1]"] = Dict("signal" => 1.5, "texp" => 10.0)
uwerr(a, wpm)
println("signal/noise=1.5, texp=10: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
println(" - window: ", window(a, "Random walk in [-1,1]"))Old (legacy) way to set the window parameters
For each ensemble ID one can pass a vector of Float64 of length 4. The first three components of the vector specify the criteria to determine the summation window:
vp[1]: The autocorrelation function is summed up tot = round(vp[1]).vp[2]: The sumation window is determined using U. Wolff poposal with $S_{\tau} = {\rm vp[2]}$.vp[3]: The autocorrelation function $\Gamma(t)$ is summed up a point where its error $\delta\Gamma(t)$ is a factorvp[3]times larger than the signal.
An additional fourth parameter vp[4], tells ADerrors to add a tail to the error with $\tau_{\rm exp} = {\rm vp[4]}$. See the reference for an explanation of the procedure.
Note that:
- Negative values of
vp[1:4]are ignored. - One, and only one, of the components
vp[1:3]has to be positive. This chooses your criteria to determine the summation window.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
# Load the data in a uwreal
a = uwreal(x.^2, "Random walk in [-1,1]")
wpm = Dict{String,Vector{Float64}}()
# Use default analysis (stau = 4.0)
uwerr(a)
println("default: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
# This will still do default analysis because
# a does not depend on emsemble foo
wpm["Ensemble foo"] = [-1.0, 8.0, -1.0, 145.0]
uwerr(a, wpm)
println("default: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
# Fix the summation window to 1 (i.e. uncorrelated data)
wpm["Random walk in [-1,1]"] = [1.0, -1.0, -1.0, -1.0]
uwerr(a, wpm)
println("uncorrelated: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
# Use stau = 1.5
wpm["Random walk in [-1,1]"] = [-1.0, 1.5, -1.0, -1.0]
uwerr(a, wpm)
println("stau = 1.5: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
# Use fixed window 15 and add tail with texp = 100.0
wpm["Random walk in [-1,1]"] = [15.0, -1.0, -1.0, 100.0]
uwerr(a, wpm)
println("Fixed window 15, texp=100: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")
# Sum up to the point that the signal in Gamma is
# 1.5 times the error and add a tail with texp = 10.0
wpm["Random walk in [-1,1]"] = [-1.0, -1.0, 1.5, 30.0]
uwerr(a, wpm)
println("signal/noise=1.5, texp=10: ", a, " (tauint = ", taui(a, "Random walk in [-1,1]"), ")")The error analysis uses the $\Gamma$-method of Wolff (with the automatic-differentiation implementation of Ramos 2019). The summation window is determined automatically, or can be set per ensemble with the wpm criteria accepted by uwerr and most other routines; see the uwerr documentation for details.
Managing the ensemble ID database
ADerrors.change_id — Function
change_id(; from::String, to::String)Changes the ensemble id from the string/integer from to the string to. Note that a call to this routine will affect the output of all currently defined uwreal types that depend on ensemble ID from. On the other hand, it will not affect data that is read after the call to change_id. This means that:
- All input should be performed before any calls to
change_id. - All output (i.e. calls to
write_uwrealordetails) is affected by previous calls tochange_id
using ADerrors # hide
a = uwreal([1.2, 0.2], "Simple var with error") # a = 1.2 +/- 0.2
b = uwreal([1.2, 0.2], 1233) # c = 1.2 +/- 0.2
d = a-b
uwerr(d)
details(d)
change_id(from=1233, to="Error source from experiment")
change_id(from="Simple var with error", to="Error source from simulations")
details(d)ADerrors.ensembles — Function
ensembles(a::uwreal)Returns the list of ensembles in lexicographic order contributing to the error of a as a Vector{String}.
using ADerrors # hide
using BDIO
a = uwreal(rand(2000), "Ensemble A")
b = uwreal(rand(2034), "Ensemble B")
c = sin(a+b)
for en in ensembles(c)
println("Ensemble "*en*" contributing")
endThe internal workspace database is not thread-safe. Concurrent calls to ADerrors analysis functions from multiple tasks may corrupt the global workspace (wsg), since new ensemble IDs are registered there lazily (unsynchronized dictionary updates and ID counters). Run error analysis from a single task, or ensure that all ensemble IDs are registered (e.g. by running the relevant error analyses) before parallelizing any further work.
I/O
ADerrors.write_uwreal — Function
write_uwreal(p::uwreal, fb, iu::Int)Given a BDIO file handler fb, this writes the observable p in a BDIO record with user info iu.
using ADerrors # hide
using BDIO
a = uwreal(rand(2000), 12)
# Create a BDIO file and write observable with user info 8.
fb = BDIO_open("/tmp/foo.bdio", "w", "Test file")
write(a, fb, 8)
BDIO_close(fb)
# Open the file and move to first record
fb = BDIO_open("/tmp/foo.bdio", "r")
BDIO_seek!(fb)
# Read observable
b = read_uwreal(fb)
# Check
c = a - b
uwerr(c)
println("Better be zero: ", c)ADerrors.read_uwreal — Function
read_uwreal(fb[, map_ids::Dict{Int64, Int64}])Given a BDIO file handler fb, this routine returns the observable stored in the current record. Optionally, ensemble ID stored in the file can be changed at read time by passing a dictionary map_ids
using ADerrors # hide
using BDIO
a = uwreal(rand(2000), 12)
fb = BDIO_open("/tmp/foo.bdio", "w", "Test file")
write(a, fb, 8)
BDIO_close(fb)
# Open the file and move to first record
fb = BDIO_open("/tmp/foo.bdio", "r")
BDIO_seek!(fb)
# Read observable
changeID_12_by_120 = Dict(12 => 120)
b = read_uwreal(fb, changeID_12_by_120)
# Check
c = a - b
uwerr(c)
print("Better be zero: ")
details(c)Correlations between observables
ADerrors.cov — Function
cov(a::Vector{uwreal}[, wpm]; method::Symbol = :sym)Determine the covariance matrix between the vector of observables a[:].
The cross-covariances of observables measured on the same Monte Carlo ensemble can be estimated with two conventions, selected with the method keyword:
method = :sym(default): symmetrized estimator, averaging both lag directions of the cross-correlation function. Statistically preferable; this is the convention used internally bychiexp/fit_error/trcov.method = :legacy: historical one-direction convention (kept to reproduce published numbers computed with older versions of ADerrors).
For uncorrelated data (or diagonal elements) both conventions agree exactly.
using ADerrors, LinearAlgebra # hide
a = uwreal([1.3, 0.01], "Var 1") # 1.3 +/- 0.01
b = uwreal([5.3, 0.23], "Var 2") # 5.3 +/- 0.23
uwerr(a)
uwerr(b)
x = [a+b, a-b]
mat = ADerrors.cov(x)
println("Covariance: ", mat[1,1], " ", mat[1,2])
println(" ", mat[2,1], " ", mat[2,2])
println("Check (should be zero): ", mat[1,1] - mat[2,2])
println("Check (should be zero): ", mat[1,2] - (err(a)^2-err(b)^2))Case of Monte Carlo data
An optional parameter wpm can be used to choose the summation window for the relevant autocorrelation functions. The situation is completely analogous to the case of error analysis of single variables. All three forms of the window dictionary are accepted: Dict{Int64,Vector{Float64}}, Dict{String,Vector{Float64}} and the preferred Dict{String,Dict{String,Real}} (named parameters; see uwerr).
ADerrors.trcov — Function
trcov(M::Array{Float64, 2}, a::Vector{uwreal}[, wmp])Given a vector of uwreal, a[:] and a two dimensional symmtric positive definite array M, this routine computes ${\rm tr}(MC)$, where $C_{ij} = {\rm cov}(a[i], a[j])$.
using ADerrors, LinearAlgebra # hide
a = uwreal([1.3, 0.01], "Var with error 1") # 1.3 +/- 0.01
b = uwreal([5.3, 0.23], "Var with error 2") # 5.3 +/- 0.23
c = uwreal(rand(2000), "White noise ensemble")
x = [a+b+sin(c), a-b+cos(c), c-b/a]
M = [1.0 0.2 0.1
0.2 2.0 0.3
0.1 0.3 1.0]
mcov = cov(x)
d = tr(mcov * M)
println("Better be zero: ", d -trcov(M, x))
# Case of Monte Carlo data
An optional parameter `wpm` can be used to choose the summation window for the relevant autocorrelation functions. The situation is completely analogous to the case of error analysis of single variables. All three forms of the window dictionary are accepted: `Dict{Int64,Vector{Float64}}`, `Dict{String,Vector{Float64}}` and the preferred `Dict{String,Dict{String,Real}}` (named parameters; see `uwerr`).cov gives the covariance matrix of a set of observables; trcov and trcorr give direct access to the (auto- and cross-correlation) $\Gamma$ functions between ensembles.
Inspecting results
There are several methods to get information about the error analysis of a uwreal variable. With the exception of value, these methods require that a proper error analysis has been performed via a call to the uwerr method.
ADerrors.value — Function
value(a::uwreal)Returns the (mean) value of the uwreal variable a
using ADerrors # hide
a = uwreal([1.2, 0.2], 12) # a = 1.2 +/- 0.2
uwerr(a)
println("a has central value: ", value(a))ADerrors.err — Function
err(a::uwreal)Returns the error of the uwreal variable a. It is assumed that uwerr has been run on the variable so that an error is available. Otherwise an error message is printed.
Julia's REPL defines a global variable err (last thrown errors) in Base.MainInclude, which shadows the exported err in the Main module (REPL prompt, julia -e scripts, top-level include). Inside functions and user modules, bare err(...) always resolves to this function. At the top level of a script or at the REPL, use the qualified form ADerrors.err(...).
using ADerrors # hide
a = uwreal([1.2, 0.2], 12) # a = 1.2 +/- 0.2
uwerr(a)
println("a has error: ", err(a))ADerrors.derror — Function
derror(a::uwreal)Returns an estimate of teh error of the error of the uwreal variable a. It is assumed that uwerr has been run on the variable so that an error is available. Otherwise an error message is printed.
using ADerrors # hide
a = uwreal([1.2, 0.2], 12) # a = 1.2 +/- 0.2
uwerr(a)
println("a has error of the error: ", derror(a))ADerrors.taui — Function
taui(a::uwreal, mcid)Returns the value of tauint for the ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed. mcid can be either an Int64 (the proper ensemble ID), or a String (the ensemble tag).
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
println("Error analysis result: ", a, " (tauint = ", taui(a, "Some simple ensemble"), ")")ADerrors.dtaui — Function
dtaui(a::uwreal, mcid)Returns an estimate on the error of tauint for the ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
println("Error analysis result: ", a,
" (tauint = ", taui(a, "Some simple ensemble"), " +/- ", dtaui(a, "Some simple ensemble"), ")")ADerrors.rho — Function
rho(a::uwreal, mcid)Returns the normalized autocorrelation function of a for the ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
v = rho(a, "Some simple ensemble")
for i in 1:length(v)
println(i, " ", v[i])
endADerrors.drho — Function
drho(a::uwreal, mcid)Returns an estimate of the error on the normalized autocorrelation function of a for the ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
v = rho(a, "Some simple ensemble")
dv = drho(a, "Some simple ensemble")
for i in 1:length(v)
println(i, " ", v[i], " +/- ", dv[i])
endADerrors.mchist — Function
mchist(a::uwreal, mcid)Returns the fluctuations of the uwreal variable a on ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
v = mchist(a, "Some simple ensemble")
for i in 1:length(v)
println(i, " ", v[i])
endADerrors.replicas — Function
replicas(a::uwreal, mcid::String)Returns the replica vector for ensemble mcid contributing to observable a
ADerrors.window — Function
window(a::uwreal, mcid)Returns the summation window for the ensemble mcid. It is assumed that uwerr has been run on the variable and that mcid contributes to the observable a. Otherwise an error message is printed.
using ADerrors # hide
# Generate some correlated data
eta = randn(1000)
x = Vector{Float64}(undef, 1000)
x[1] = 0.0
for i in 2:1000
x[i] = x[i-1] + eta[i]
if abs(x[i]) > 1.0
x[i] = x[i-1]
end
end
a = uwreal(x.^2, "Some simple ensemble")
uwerr(a)
println("Error analysis result: ", a,
" (window = ", window(a, "Some simple ensemble"), ")")ADerrors.details — Function
details(a::uwreal; io::IO=stdout, names::Dict{Int64, String} = Dict{Int64, String}())
details(a::uwreal, ensemble::String, ws::wspace, io::IO=stdout)Write out a detailed information on the error of a.
Arguments
If a the ergument ensemble is present, this routine writes information of the fluctuations of the observable in this ensemble. Optionally one can pass as a keyword argument (io) the IO stream to write to.
Example
using ADerrors # hide
a = uwreal(rand(2000), "Ensemble A12", ["A12 REP 0", "A12 REP 1", "A12 REP 2"], [1000, 500, 500])
b = uwreal([1.2, 0.023], "Ensemble XYZ")
c = uwreal([5.2, 0.03], "Ensemble RRR")
d = a + b - c
uwerr(d)
details(d)
for en in ensembles(d)
details(d, en)
endADerrors.neid — Function
neid(a::uwreal)Returns the number of different ensemble ID's contributing to a
using ADerrors # hide
a = uwreal([1.2, 0.2], 12) # a = 1.2 +/- 0.2
b = uwreal([7.2, 0.5], 13) # a = 7.2 +/- 0.5
c = a*b
println("Number od ID contributing to c: ", neid(a))ADerrors.derivative — Function
derivative(a::uwreal, b::uwreal)Determines the derivate of the observable a with respect to observable b (i.e. `rac{{ m d} a}{{ m d} b}). Observablebmust depend on only a single ensemble (typicallyb` is just a primary observable).
using ADerrors # hide
# Put some data
a = uwreal(1.2 .+ randn(2000), "Ensemble test A", ["R0", "Rep 1", "Last rep"], [1000, 550, 450])
b = uwreal(0.8 .+ randn(2034), "Ensemble test B", [1000, 584, 450])
d = sin(a+b)
println("Derivative of d w.r.t a: ", derivative(d, a))
println("Derivative of d w.r.t log(a): ", derivative(d, log(a)))Comparisons and ordering
Comparison operators (<, >, ≤, ≥, ≠) and isless compare the mean values only (fluctuations are ignored). isless enables sort, minimum, etc. isapprox (and ≈) also compares mean values. == between uwreal values throws an error, as it is meaningless for observables with statistical errors.
Base.:< — Function
comparison operators on `uwreal`Comparison operators (<, >, ≤, ≥, ≠) on uwreal values act on the mean value only; errors (noise) are completely ignored. The == operator is not meaningful for uwreal and throws an error; use ≈ (isapprox) to compare mean values approximately. The isless method is defined analogously, enabling sort, minimum, maximum and searchsorted on vectors of uwreal (by mean value).
Base.:> — Function
comparison operators on `uwreal`Comparison operators (<, >, ≤, ≥, ≠) on uwreal values act on the mean value only; errors (noise) are completely ignored. The == operator is not meaningful for uwreal and throws an error; use ≈ (isapprox) to compare mean values approximately. The isless method is defined analogously, enabling sort, minimum, maximum and searchsorted on vectors of uwreal (by mean value).
Base.:≤ — Function
comparison operators on `uwreal`Comparison operators (<, >, ≤, ≥, ≠) on uwreal values act on the mean value only; errors (noise) are completely ignored. The == operator is not meaningful for uwreal and throws an error; use ≈ (isapprox) to compare mean values approximately. The isless method is defined analogously, enabling sort, minimum, maximum and searchsorted on vectors of uwreal (by mean value).
Base.:≥ — Function
comparison operators on `uwreal`Comparison operators (<, >, ≤, ≥, ≠) on uwreal values act on the mean value only; errors (noise) are completely ignored. The == operator is not meaningful for uwreal and throws an error; use ≈ (isapprox) to compare mean values approximately. The isless method is defined analogously, enabling sort, minimum, maximum and searchsorted on vectors of uwreal (by mean value).
Base.:≠ — Function
comparison operators on `uwreal`Comparison operators (<, >, ≤, ≥, ≠) on uwreal values act on the mean value only; errors (noise) are completely ignored. The == operator is not meaningful for uwreal and throws an error; use ≈ (isapprox) to compare mean values approximately. The isless method is defined analogously, enabling sort, minimum, maximum and searchsorted on vectors of uwreal (by mean value).
Error propagation in iterative algorithms
Finding root of functions
ADerrors.root_error — Function
root_error(fnew::Function, x0::Float64, data::Vector{uwreal})Returns x such that fnew(x, data) = 0 (i.e. a root of the function). The error in data is propagated to the root position x.
using ADerrors # hide
# First define some arbitrary data
data = Vector{uwreal}(undef, 3)
data[1] = uwreal([1.0, 0.2], "Var A")
data[2] = uwreal([1.2, 0.023], "Var B")
data[3] = uwreal(rand(1000), "White noise ensemble")
# Now define a function
f(x, p) = x + p[1]*x + cos(p[2]*x+p[3])
# Find its root using x0=1.0 as initial
# guess of the position of the root
x = root_error(f, 1.0, data)
uwerr(x)
println("Root: ", x)
# Check
z = f(x, data)
uwerr(z)
print("Better be zero (with zero error): ")
details(z)Integrals
ADerrors.int_error — Function
int_error(fint::Function, a, b, p::AbstractVector{uwreal})Computes the integral
$\int_a^b {\rm fint}(x; p)\, {\rm d}x$
The errors in the parameters p[:] and (optionally) in the limits of the intgeral (a, b) are propagated to an error of the integral. The result is returned as an uwreal.
using ADerrors
# Average values and covariance from
# https://inspirehep.net/literature/1477411
# here we try to reproduce the result
# Scale ratio = 21.86(42) Eq.5.6
avg = [16.26, 0.12, -0.0038]
Mcov = [ 0.478071 -0.176116 0.0135305
-0.176116 0.0696489 -0.00554431
0.0135305 -0.00554431 0.000454180]
p = cobs(avg, Mcov, "Beta function fit parameters")
g1s = uwreal([2.6723, 0.0064], 4)
g2s = 11.31
fint(x, p) = - (p[1] + p[2]*x^2 + p[3]*x^4)/x^3
g1 = sqrt(g1s)
g2 = sqrt(g2s)
sint = 2.0*exp(-int_error(fint, g1, g2, p))
uwerr(sint)
print(" From integral evaluation: ")
details(sint)Fits and p-values
ADerrors.jl is agnostic about how you approach a fit (i.e. minimize a $\chi^2$ function), but once the central values of the fit parameters have been found (i.e. using LsqFit.jl, LeastSquaresOptim.jl, etc...) ADerrors.jl is able to determine the error of the fit parameters, returning them as uwreal type.
For goodness of fit, ADerrors assumes the $\chi^2$ is quadratic in the data. Under this assumption the expected $\chi^2$ is the mean of a generalized $\chi^2$ distribution, $\mathrm{tr}(C \cdot P_x)$, and its p-value is computed from the (eigen)spectrum of the projected data covariance: either with the deterministic Imhof inversion of the characteristic function (default, accurate to ~1e-10) or with a Monte Carlo estimate (method = :mc). A pivoted factorization (Ctol > 0) regularizes close-to-singular (MC-estimated) covariances. pvalue returns the tuple (Q, chie) where Q is the p-value and chie = tr(C \cdot P_x) the mean of the generalized $\chi^2$ actually used for Q — consistent with it by construction; for uncorrelated fits chie = m - n exactly. fit_error(pval = true) returns the same chie together with the fit parameters and p-value.
ADerrors.fit_error — Function
fit_error(chisq::Function, xp::Vector{Float64}, data::Vector{uwreal}[, wpm][, W];
chi_exp = true, C = nothing, pval = false, Ctol = 0.0)Given a $\chi^2(p, d)$, function of the fit parameters p[:] and the data d[:], this routine returns the fit parameters as uwreal type and optionally, the expected value of $\chi^2(p, d)$ and the p-value of the fit.
Arguments
chisq: Must be a function of two vectors (i.e.chisq(p::Vector, d::Vector)). To determine the fit parameters, the function can be arbitrary, but for the determination of the expected $\chi^2(p, d)$ is assumed to have the form
$\chi^2(p, d) = \sum_{ij} [d_i - f_i(p)]W_{ij}[d_j - f_j(p)]$
where the function $f_i(p)$ is an arbitrary function of the fit parameters. In simple words, the expected $\chi^2(p, d)$ is determined assuming that the function $\chi^2(p, d)$ is quadratic in the data.
xp: A vector ofFloat64. The value of the fit parameters at the minima.data: A vector ofuwreal. The data whose fluctuations enter in the evaluation of thechisq.wpm:Dict{Int64,Vector{Float64}}orDict{String,Vector{Float64}}. The criteria to determine the summation window. See the documentation onuwerrfunction for more details.W: A matrix. The weights that enter in the evaluation of thechisqfunction. If a vector is passed, the matrix is assumed to be diagonal (i.e. uncorrelated fit). If no weights are passed, the routines assumes thatWis diagonal with entries given by the inverse errors squared of the data (i.e. thechisqis weighted with the errors of the data). All entries ofWmust be positive.C: A matrix. The data covariance matrix. If not passed, it is computed internally withcov_sym. Only used for the expected $\chi^2$ and the p-value.chi_exp: Bool type (keyword). If false, do not compute the expected $\chi^2(p, d)$. Implied true ifpval = true.pval: Bool type (keyword, defaultfalse). If true, also compute the p-value of the fit, i.e. $P(\chi^2 \geq \chi^2_{\rm obs})$ for the generalized $\chi^2$ distribution with mean χexp (using the Imhof method; seepvalue), reusing the Hessian computed for the expected $\chi^2$. Requiresm > n(more data points than fit parameters); otherwise anArgumentErroris thrown. The routine then returns a 3-tuple(param, cse, p). Whenpval = truethe returnedcseis the meantr(C \cdot P_x)of the generalized $\chi^2$ distribution actually used for the p-value (consistent with it by construction); for uncorrelated fitscse = m - nexactly.Ctol: Float64 (keyword, default0.0). Only relevant ifpval = true. Relative tolerance for the factorization of the data covarianceC; see the documentation ofpvaluefor details.
Return value
pval = false:(param, cse)— fit parameters as a vector ofuwrealand the expected $\chi^2$ (cse = 0.0ifm <= n). If alsochi_exp = false, onlyparamis returned.pval = true:(param, cse, p)— as above plus the p-valuepof the fit.
The computation of the covariance between data is by far the most expensive operation in this routine. This is only needed to determine the p-value. When the same data (or a subset) is fitted to different functional forms, it is encouraged to pre-compute the covariance and pass the keyword parameter C=....
Example
using ADerrors, Distributions # hide
# Generate correlated samples with average 0.1
npt = 12
sig = zeros(npt, npt)
dx = zeros(npt)
for i in 1:npt
dx[i] = 0.01*i
sig[i,i] = dx[i]^2
for j in i+1:npt
sig[i,j] = 0.0001 - 0.000005*abs(i-j)
sig[j,i] = 0.0001 - 0.000005*abs(i-j)
end
end
dmv = MvNormal([0.1 for n in 1:npt], sig)
vs = rand(dmv, 1)
# Create the uwreal data that we want to
# fit to a constant
dt = cobs(vs[:,1], sig, "Data points")
# Define the chi^2
chisq(p, d) = sum( (d .- p[1]) .^ 2 ./ dx .^2 )
# The result of an uncorrelated fit to a
# constant is the weighted average
xp = [sum(value.(dt) ./ dx)/sum(1.0 ./ dx)]
# Propagate errors to the fit parameters and
# determine the expected chi^2
(fitp, csqexp) = fit_error(chisq, xp, dt)
uwerr.(fitp)
println(" *** FIT RESULTS ***")
print("Fit parameter: ")
details.(fitp)
println("chi^2 / chi_exp^2: ", chisq(xp, value.(dt)), " / ", csqexp, " (dof: ", npt-1, ")")ADerrors.chiexp — Function
chiexp(chisq::Function,
xp::Vector{Float64},
data::Vector{uwreal};
W = Vector{Float64}(),
C = nothing)Given a $\chi^2(p, d)$, function of the fit parameters p[:] and the data d[:], compute the expected value of the $\chi^2(p, d)$.
Arguments
chisq: Must be a function of two vectors (i.e.chisq(p::Vector, d::Vector)). The function is assumed to have the form
$\chi^2(p, d) = \sum_i [d_i - f_i(p)]W_{ij}[d_j - f_j(p)]$
where the function $f_i(p)$ is an arbitrary function of the fit parameters. In simple words, the function is assumed to be quadratic in the data.
xp: A vector ofFloat64. The value of the fit parameters at the minima.data: A vector ofuwreal. The data whose fluctuations enter in the evaluation of thechisq.W: A matrix. The weights that enter in the evaluation of thechisqfunction. If a vector is passed, the matrix is assumed to be diagonal (i.e. uncorrelated fit). If no weights are passed, the routines assumes thatWis diagonal with entries given by the inverse errors squared of the data (i.w. thechisqis weighted with the errors of the data). All entries ofWmust be positive.C: A matrix. The covariance matrix of the data. if not given (i.e. isnothing), ADerrors will infer the covariance matrix form the data.
Example
using ADerrors, Distributions # hide
# Generate correlated samples with average 0.1
npt = 12
sig = zeros(npt, npt)
dx = zeros(npt)
for i in 1:npt
dx[i] = 0.01*i
sig[i,i] = dx[i]^2
for j in i+1:npt
sig[i,j] = 0.0001 - 0.000005*abs(i-j)
sig[j,i] = 0.0001 - 0.000005*abs(i-j)
end
end
dmv = MvNormal([0.1 for n in 1:npt], sig)
vs = rand(dmv, 1)
# Create the uwreal data that we want to
# fit to a constant
dt = cobs(vs[:,1], sig, [100+n for n in 1:npt])
# Define the chi^2
chisq(p, d) = sum( (d .- p[1]) .^ 2 ./ dx .^2 )
# The result of an uncorrelated fit to a
# constant is the weighted average
xp = [sum(value.(dt) ./ dx)/sum(1.0 ./ dx)]
# Compare chi^2 and expected chi^2
println("chi^2 / chi_exp^2: ", chisq(xp, value.(dt)), " / ", chiexp(chisq, xp, dt))ADerrors.pvalue — Function
pvalue(chisq::Function, chi2::Float64, xp::AbstractVector{Float64},
data::AbstractVector{uwreal},
W::AbstractVector{Float64}=Vector{Float64}();
wpm = Dict{Int64,Vector{Float64}}(), nmc::Int64 = 5000,
C::AbstractMatrix{Float64} = zeros(0,0),
method::Symbol = :imhof,
Ctol::Float64 = 0.0,
rng::AbstractRNG = Random.default_rng())Computes the p-value of a previously done fit, using as input the observed $\chi^2$ of the fit at the minimum, the fit parameters and the fitted data. The p-value for a given observed $\chi^2$ is the probability of, given the data you have, finding such a $\chi^2$ or worse from a fit, and still have the data well described by the fit function.
method = :imhof(default): deterministic numerical inversion of the characteristic function of the generalized $\chi^2$ distribution (Imhof 1961; Davies 1973). Accurate to ~1e-10. Reduces exactly to the textbook $\chi^2$ result for (weighted) uncorrelated fits.method = :mc: Monte Carlo estimate withnmcsamples (statistical resolution ~$\sqrt{p(1-p)/nmc}$); useful as a cross-check.Ctol: relative tolerance for the factorization of the data covarianceC. If zero (default), a plain Cholesky factorization is used (fails loudly ifCis not positive definite). If positive, a pivoted (RowMaximum) Cholesky with relative toleranceCtolis used instead: modes ofCbelow the tolerance are treated as noise-free (exact zero eigenvalues of the projected covariance), a rank-revealing regularization for close-to-singular MC covariances. A warning is issued if the effective rank drops to the number of fit parameters or below.
Arguments
chisq: Must be a function of two vectors (i.e.chisq(p::Vector, d::Vector)), the same $\chi^2$ function used in the fit. For the determination of the expected $\chi^2(p, d)$ is assumed to have the form
$\chi^2(p, d) = \sum_{ij} [d_i - f_i(p)]W_{ij}[d_j - f_j(p)]$
chi2:Float64. The observed value of $\chi^2(p, d)$ of the fit at the minimum (e.g.fit.ssrin LeastSquaresOptim, orchisq(xp, value.(data))).xp: A vector ofFloat64. The value of the fit parameters at the minima.data: A vector ofuwreal. The data whose fluctuations enter in the evaluation of thechisq.W: The weights that enter in the evaluation of thechisqfunction. If a vector is passed, the matrix is assumed to be diagonal (i.e. uncorrelated fit). If no weights are passed, the routine assumes thatWis diagonal with entries given by the inverse errors squared of the data. All entries ofWmust be positive.wpm:Dict{Int64,Vector{Float64}}orDict{String,Vector{Float64}}. The criteria to determine the summation window. See the documentation onuwerrfunction for more details. Only used ifCis not passed.nmc: Number of Monte Carlo samples (only used ifmethod = :mc).C: A matrix. The data covariance matrix. If not passed, it is computed internally withcov_sym.method::imhof(default) or:mc. See above.Ctol: Relative tolerance for the factorization ofC. See above.rng:AbstractRNGused by the Monte Carlo method (only used ifmethod = :mc).
Return value
A tuple (Q, chie):
Q: the p-value, $P(\chi^2 \geq \chi^2_{\rm obs})$ for the generalized $\chi^2$ distribution with the spectrum of the projected covariance.chie: the mean of that generalized $\chi^2$ distribution, $\mathrm{tr}(C \cdot P_x)$ — consistent by construction with the spectrum used forQ. For (weighted) uncorrelated fits,chie = m - nexactly. If an explicitCis passed,chieis computed from thatC.
Requires m > n (more data points than fit parameters). Use ADerrors.pvalue at the REPL / top level (bare pvalue works inside functions and modules).
function fit_defs(f::Function, x, W)
chisq(p, d) = sum((d - f(x, p)).^2 .* W)
return chisq
end
# given the result of a fit: chi2 (observed chi^2), xp (parameters), y (data)
wpm = Dict{Int64,Vector{Float64}}()
wpm[1] = [-1.0, -1.0, 4.0, -1.0]
(Q, chie) = pvalue(chisq, chi2, value.(xp), y, 1.0 ./ ADerrors.err.(y) .^ 2; wpm = wpm)
# (Q, chie) = pvalue(chisq, chi2, value.(xp), y, 1.0 ./ ADerrors.err.(y) .^ 2;
# method = :mc, nmc = 10000)Reference
Mattia Bruno, Rainer Sommer, Comput.Phys.Commun. 285 (2023), 108643, arxiv: 2209.14188 J. P. Imhof, Biometrika 48 (1961) 419.
Acknowledgments
The pvalue routine was developed and debugged with contributions from Antonino D'Anna and Alejandro Saez.