Commit a2f4bcfc authored by Antonino's avatar Antonino

bug fixes in fit routine

parent 5f3ab2cc
"""
"""
plat_av(obs::Vector{uwreal}, plat::Vector{Int64}, [W::VecOrMat{Float64},] wpm::Union{Dict{Int64},Vector{Float{64}},Dict{String,Vector{Float{64}},Nothing}=nothing} )
It computes plateau average of `obs` in the range `plat`. Effectively this perform a fit to a constant.
The field `W` is optional. When absent, `W` is a `Vector{Float64}` constructed from the error of `obs`.
It computes plateau average of `obs` in the range `plat`. Effectively this perform a fit to a constant.
The field `W` is optional. When absent, `W` is a `Vector{Float64}` constructed from the error of `obs`.
When `W` is a `Vector{Float64}` it performs an uncorrelated fit, when `W` is a `Matrix{Float64}`, it perfom a correlated fit
# Potential side effects
......@@ -16,7 +15,7 @@ function plat_av(obs::Vector{uwreal}, plat::Vector{Int64}, wpm::Union{Dict{Int64
isnothing(wpm) ? uwerr.(obs) : [uwerr(obs_aux, wpm) for obs_aux in obs]
w = 1 ./ err.(obs)[plat[1]:plat[2]].^2
av = sum(w .* obs[plat[1]:plat[2]]) / sum(w)
return av
return av
end
plat_av(obs::Vector{uwreal}, plat::Vector{Int64}, W::Vector{Float64},wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing) = sum(W .* obs[plat[1]:plat[2]]) / sum(W)
......@@ -74,16 +73,16 @@ y_lin_fit(par::Vector{uwreal}, x::Union{uwreal, Float64}) = par[1] + par[2] * x
@doc """
get_chi(model::Function,x::Vector,par,data,W::VecOrMat{Float64})
It computes the chi square of a fit. `model` is assumed to be of the type f(xdata,par).
It computes the chi square of a fit. `model` is assumed to be of the type f(xdata,par).
If `W::Vector` the chisquare is computed for an uncorrelated fit
If `W::Matrix` the chisquare is computed for a correlated fit
"""
function get_chi(model::Function,x,par,data,W::Array{Float64,2})
return sum((data.-model(x,par))' * W * (data.-model(x,par)))
return sum((data.-model(x,par))' * W * (data.-model(x,par)))
end
function get_chi(model::Function,x,par,data,W::Vector{Float64})
return sum((data.-model(x,par)).^2 .* W)
return sum((data.-model(x,par)).^2 .* W)
end
......@@ -91,12 +90,12 @@ end
fit_routine(model::Function,xdata::AbstractArray{<:Real},ydata::AbstractArray{ADerrors.uwreal},npar; kwargs...)
fit_routine(model::Function,xdata::AbstractArray{uwreal},ydata::AbstractArray{ADerrors.uwreal},npar; kwargs...)
It executes a fit of the `ydata` with the fit function `model`.
It executes a fit of the `ydata` with the fit function `model`.
# Arguments:
- `model`: Fit function used to fit the data. the function assumes that its called as `model(xdata,parameters)`.
- `xdata`: indipendent variable in the fit. It can be an `AbstractArray{<:Real}`, that is the "default" method, or
- `xdata`: indipendent variable in the fit. It can be an `AbstractArray{<:Real}`, that is the "default" method, or
a `AbstractArray{uwreal}`. In the latter case, the method builds the function
``math
F(q_i) = \begin{cases}
......@@ -105,7 +104,7 @@ F(q_i) = \begin{cases}
\end{cases}
``
and then call the default method with dummy `xdata` and `vcat(ydata,xdata)` as new `ydata`
- `ydata`: Data variable that carries the error
- `ydata`: Data variable that carries the error
- `npar`: number of parameters used in the fit.
# Keyword parameters:
......@@ -125,11 +124,11 @@ F(q_i) = \begin{cases}
If `W` is not given, and `corr` is `true`, then it is used to compute `W`.
If need to computed `W` but not given, then `C = ADerrors.cov(ydata,wpm)`. See also ADerrors documentation.
If available, it will be used to better estimate the fit pvalue and the expected chi-square,
but it will not be computed if not available at this point.
but it will not be computed if not available at this point.
- `guess::Vector{Float64}`: Initial guess for the parameter used in the fit routine. If not given, default to `fill(0.5,npar)`.
If `xdata isa Vector{uwreal}`, the mean value of `xdata` are used as guess for the relevant `q_i` parameters.
- `logfile`: handle to a logfile. If `nothing`, no log will be written. It can be `IO` file or a custom log structure that has
an overloading for `print()` and `println()`.
......@@ -137,15 +136,15 @@ F(q_i) = \begin{cases}
It returns a `NamedTuple` with names:
- `:par`: fit parameter as `Vector{uwreal}`
- `:chi2`: chisquare,
- `:chiexp`: chisquare expected
- `:pval`: pvalue
- `:chiexp`: chisquare expected
- `:pval`: pvalue
"""
function fit_routine(model::Function,
xdata::AbstractArray{<:Real},
ydata::AbstractArray{uwreal},
npar::Int64;
npar::Int64;
wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}}=Dict{Int64,Vector{Float64}}(),
corr::Bool = true,
corr::Bool = true,
W::AbstractArray{Float64}=Vector{Float64}(),
guess::AbstractVector{Float64} = fill(0.5,npar),
logfile = nothing,
......@@ -158,8 +157,8 @@ function fit_routine(model::Function,
[uwerr(y, wpm) for y in ydata]
if length(W) ==0
if corr
C = length(C) ==0 ? GammaMethod.cov_AD(ydata,wpm) : C
if corr
C = length(C) ==0 ? GammaMethod.cov_AD(ydata) : C
W = LinearAlgebra.pinv(C);
W = (W'+W)*0.5
else
......@@ -173,9 +172,9 @@ function fit_routine(model::Function,
chi2 = sum(fit.resid.^2)
par = fit_error(chisq,LsqFit.coef(fit),ydata,wpm,W,false)
# if the fit is correlated we have already C, no need to recompute it.
chiexp,pval = if length(C) !=0
chiexp,pval = if length(C) !=0
GammaMethod.chiexp(chisq,LsqFit.coef(fit),value.(ydata),C,W),juobs.pvalue(chisq,chi2,LsqFit.coef(fit),ydata,W,C=C)
else
else
ADerrors.chiexp(chisq,LsqFit.coef(fit),ydata,wpm,W=W),juobs.pvalue(chisq,chi2,LsqFit.coef(fit),ydata,W)
end
......@@ -244,7 +243,7 @@ function fit_routine(model::Function,
guess=new_guess, wpm=wpm,corr=corr,
W=W,C=C,logfile=nothing)
if !isnothing(logfile)
if !isnothing(logfile)
uwerr.(fit.par)
println(logfile, "juobs.fit_routine output:")
println(logfile, "\t\txdata carries uncertainty")
......@@ -270,7 +269,7 @@ end
fit_routine(model::AbstractArray{Function},
xdata::AbstractArray{<:AbstractArray},
ydata::AbstractArray{<:AbstractArray{ADerrors.uwreal}},npar; kwargs...)
It executes a combined fit of the `ydata` with the fit functions in `model`. The method define the general model `F(X_{mi},p) = model[m](xdata[i],p)`
and call `fit_routine(F,vcat(xdata),vcat(ydata),npar;kwargs....)`. The `kwargs` parameters are accordingly updated to reflect the new function.
......@@ -279,39 +278,39 @@ and call `fit_routine(F,vcat(xdata),vcat(ydata),npar;kwargs....)`. The `kwargs`
It returns a `NamedTuple` with names:
- `:par`: fit parameter as `Vector{uwreal}`
- `:chi2`: chisquare,
- `:chiexp`: chisquare expected
- `:pval`: pvalue
- `:chiexp`: chisquare expected
- `:pval`: pvalue
"""
function fit_routines(models::AbstractArray{Function},
function fit_routine(models::AbstractArray{Function},
xdata::AbstractArray{<:AbstractArray},
ydata::AbstractArray{<:AbstractArray{ADerrors.uwreal}},
npar::Int64;
W::AbstractArray = Float64[],
C::AbstractArray = Float64[],
W = Float64[],
C = Float64[],
wpm::AbstractDict = Dict{Int64,Vector{Float64}}(),
corr::Bool = true,
guess::AbstractArray = fill(0.5,npar),
logfile = nothing,
)
Nmodel = length(models)
ndata = length.(xdata)
Ntotal = sum(ndata);
if Nmodel != length(xdata) != length(ydata)
error("[juobs] Error: you need the same number of model, xdata and ydata")
end
if !(length(W) == 0 || length(W)==Nmodel)
error("[juobs] Error: You need to pass a W matrix for model or none")
if !(length(W) == 0 || length(W)==Nmodel || length(W) == Ntotal^2)
error("[juobs] Error: You need to pass a W matrix for model, for all the data or none")
end
if !(length(C) == 0 || length(C)==Nmodel)
error("[juobs] Error: You need to pass a C matrix for model or none")
if !(length(C) == 0 || length(C)==Nmodel || length(C) == Ntotal^2)
error("[juobs] Error: You need to pass a C matrix for model, for all the data or none")
end
ndata = length.(xdata)
if !all(length.(ydata).== ndata)
error("[juobs] Error: Mismatch in xdata and ydata. Make sure that the data corresponds")
end
Ntotal = sum(ndata);
X = vcat(xdata...)
Y = vcat(ydata...)
function make_matrix(M)
function make_matrix(M::Vector{<:AbstractMatrix})
aux = zeros(Ntotal,Ntotal)
ie = 0
for m in 1:Nmodel
......@@ -321,6 +320,7 @@ function fit_routines(models::AbstractArray{Function},
end
return aux
end
make_matrix(M::T where {T<:AbstractMatrix}) = M
WW = length(W)==0 ? W : make_matrix(W);
CC = length(C)==0 ? C : make_matrix(C);
......@@ -331,17 +331,17 @@ function fit_routines(models::AbstractArray{Function},
is= 1
ie= 0
for i in 1:Nmodel
ie += ndata[i]
ie += ndata[i]
rd = is:ie
res[i] = models[i](x[rd],p)
is = ie +1
end
return vcat(res...)
end
fit = juobs.fit_routine(Model,X,Y,npar,guess = guess,W=WW,C=CC,corr=corr,logfile=nothing,wpm=wpm)
if !isnothing(logfile)
if !isnothing(logfile)
uwerr.(fit.par)
flag = typeof(xdata) <: AbstractArray{<:AbstractArray{ADerrors.uwreal}}
println(logfile, "juobs.fit_routine output:")
......
#=
using ForwardDiff, LinearAlgebra
for op in (:eigvals, :eigvecs)
@eval function LinearAlgebra.$op(a::Matrix{uwreal})
function fvec(x::Matrix)
return LinearAlgebra.$op(x)
end
return fvec(value.(a))
#return uwreal(LinearAlgebra.$op(a.mean), a.prop, ForwardDiff.derivative($op, a.mean)*a.der)
end
end
=#
@doc raw"""
get_matrix(diag::Vector{Array}, upper::Vector{Array} )
This function allows the user to build an array of matrices, where each matrix is a symmetric matrix of correlators at a given timeslice.
It takes as input:
- `diag::Vector{Array}`: vector of correlators. Each correlator will constitute a diagonal element of the matrices A[i] where i runs over the timeslices, i.e.
`A[i][1,1] = diag[1], .... A[i][n,n] = diag[n]`
`Given n=length(diag)`, the matrices will have dimension n*n
- `upper::Vector{Array}`: vector of correlators liying on the upper diagonal position.
`A[i][1,2] = upper[1], .. , A[i][1,n] = upper[n-1], `
`A[i][2,3] = upper[n], .. , A[i][n-1,n] = upper[n(n-1)/2]`
Given n, `length(upper)=n(n-1)/2`
The method returns an array of symmetric matrices of dimension n for each timeslice
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
Julia> matrices[i]
pp[i] ap[i]
pa[i] aa[i]
## where i runs over the timeslices
```
"""
function get_matrix(corr_diag::Vector{Vector{uwreal}}, corr_upper::Vector{Vector{uwreal}})
time = length(corr_diag[1]) # total time slices
n = length(corr_diag) # matrix dimension
d = length(corr_upper) # upper elements
if d != (n*(n-1) / 2)
error("Error get_matrix")
end
res = Vector{Matrix{uwreal}}(undef, time) # array with all the matrices at each time step.
aux = Matrix{uwreal}(undef, n, n)
for t in 1:time
count=0
for i in range(n-1,1, step=-1)
for j in range(n, i+1, step=-1)
aux[i,j] = corr_upper[d-count][t]
aux[j,i] = corr_upper[d-count][t]
count +=1
end
aux[i,i] = corr_diag[i][t]
end
aux[n,n] = corr_diag[n][t]
res[t] = copy(aux)
end
return res
end
get_matrix(corr_diag::Vector{Corr}, corr_upper::Vector{Corr}) = get_matrix(getfield.(corr_diag, :obs), getfield.(corr_upper, :obs))
@doc raw"""
energies(evals::Vector{Array}; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
This method computes the energy level from the eigenvalues according to:
``E_i(t) = \log(λ(t) / λ(t+1))``
where `i=1,..,n` with `n=length(evals[1])` and `t=1,..,T` total time slices.
It returns a vector array en where each entry en[i][t] contains the i-th states energy at time t
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
## solve the GEVP
evals = getall_eigvals(matrices, 5) #where t_0=5
en = energies(evals)
Julia> en[i] # i-th state energy at each timeslice
```
"""
function energies(evals::Union{Vector{Vector{uwreal}},Array{Array{uwreal}} }; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
time = length(evals)
n = length(evals[1])
eff_en = Array{Array{uwreal}}(undef, n)
aux_en = Vector{uwreal}(undef, time-1)
for i in 1:n
for t in 1:time-1
ratio = evals[t][i] / evals[t+1][i]
aux_en[t] = 0.5*log(ratio * ratio)
end
#uwerr.(aux_en)
#isnothing(wpm) ? uwerr.(aux_en) : [uwerr(a, wpm) for a in aux_en]
eff_en[i] = copy(aux_en)
end
return eff_en
end
@doc raw"""
getall_eigvals(a::Vector{Matrix}, t0; iter=30 )
This function solves a GEVP problem, returning the eigenvalues, for a list of matrices, taking as generalised matrix the one
at index t0, i.e:
``C(t_i)v_i = λ_i C(t_0) v_i``, with i=1:lenght(a)
It takes as input:
- `a::Vector{Matrix}` : a vector of matrices
- `t0::Int64` : idex value at which the fixed matrix is taken
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res` = Vector{Vector{uwreal}}
where `res[i]` are the generalised eigenvalues of the i-th matrix of the input array.
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
## solve the GEVP
#evals = getall_eigvals(matrices, 5) #where t_0=5
Julia>
```
"""
function getall_eigvals(a::Vector{Matrix{uwreal}}, t0::Int64; iter=5 )
n = length(a)
res = Vector{Vector{uwreal}}(undef, n)
[res[i] = uweigvals(a[i], a[t0], iter=iter) for i=1:n]
return res
end
@doc raw"""
uweigvals(a::Matrix{uwreal}; iter = 30)
uweigvals(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvalues of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvalues instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30`: optional flag to set the iterations of the qr algorithm used to solve the eigenvalue problem
It returns:
- `res = Vector{uwreal}`: a vector where each elements is an eigenvalue
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
res = uweigvals(a) ##eigenvalues
res1 = uweigvals(a,b) ## generalised eigenvalues
```
"""
function uweigvals(a::Matrix{uwreal}; iter::Int64=5, diag::Bool=true)
n = size(a,1)
if n == 2
return uweigvals_dim_geq3(a, iter=iter, diag=diag) #uweigvals_dim2(a)
else
return uweigvals_dim_geq3(a, iter=iter, diag=diag)
end
end
function uweigvals(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 5, diag::Bool=true)
c = uwdot(invert(b),a)
n = size(c,1)
if n == 2
return uweigvals_dim_geq3(c, iter=iter, diag=diag) #uweigvals_dim2(c)
else
return uweigvals_dim_geq3(c, iter=iter, diag=diag)
end
#for k in 1:iter
# q_k, r_k = qr(c)
# c = uwdot(r_k, q_k)
#end
end
function uweigvals_dim_geq3(a::Matrix{uwreal}; iter = 5, diag::Bool=true)
n = size(a,1)
#a = tridiag_reduction(a)
for k in 1:iter
q_k, r_k = qr(a)
a = uwdot(r_k, q_k)
end
diag == true ? (return get_diag(a)) : (return a)
end
function uweigvals_dim2(a::Matrix{uwreal})
res = Vector{uwreal}(undef,2)
aux_sum = a[1,1] + a[2,2]
delta = ADerrors.sqrt((aux_sum)^2 - 4*(a[1,1]*a[2,2]- a[1,2]*a[2,1]))
res[1] = (aux_sum + delta) / 2
res[2] = (aux_sum - delta) / 2
return res
end
@doc raw"""
getall_eigvecs(a::Vector{Matrix}, delta_t; iter=30 )
This function solves a GEVP problem, returning the eigenvectors, for a list of matrices.
``C(t_i)v_i = λ_i C(t_i-\delta_t) v_i ``, with i=1:lenght(a)
Here `delta_t` is the time shift within the two matrices of the problem, and is kept fixed.
It takes as input:
- `a::Vector{Matrix}` : a vector of matrices
- `delta_t::Int64` : the fixed time shift t-t_0
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res = Vector{Matrix{uwreal}}`
where each `res[i]` is a matrix with the eigenvectors as columns
Examples:
```@example
mat_array = get_matrix(diag, upper_diag)
evecs = getall_eigvecs(mat_array, 5)
```
"""
function getall_eigvecs(a::Vector{Matrix{uwreal}}, delta_t; iter=5)
n = length(a)-delta_t
res = Vector{Matrix{uwreal}}(undef, n)
[res[i] = uweigvecs(a[i+delta_t], a[i]) for i=1:n]
return res
end
@doc raw"""
uweigvecs(a::Matrix{uwreal}; iter = 30)
uweigvecs(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvectors of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvectors instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res = Matrix{uwreal}`: a matrix where each column is an eigenvector
Examples:
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
res = uweigvecs(a) ##eigenvectors in column
res1 = uweigvecs(a,b) ## generalised eigenvectors in column
```
"""
function uweigvecs(a::Matrix{uwreal}; iter = 10)
n = size(a,1)
# if n > 2
# a, q = tridiag_reduction(a, q_mat=true)
# end
u = idty(n)
for k in 1:iter
q_k, r_k = qr(a)
a = uwdot(r_k, q_k)
u = uwdot(u, q_k)
end
#n > 2 ? (return uwdot(q,u)) : (return u)
return u
end
function uweigvecs(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 10)
c = uwdot(invert(b), a)
#return uweigvecs(c, iter=iter)
n = size(c,1)
u = idty(n)
for k in 1:iter
q_k, r_k = qr(c)
c = uwdot(r_k, q_k)
u = uwdot(u, q_k)
end
return u
end
@doc raw"""
uweigen(a::Matrix{uwreal}; iter = 30)
uweigen(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvalues and the eigenvectors of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvalues and eigenvectors instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `evals = Vector{uwreal}`: a vector where each elements is an eigenvalue
- `evecs = Matrix{uwreal}`: a matrix where the i-th column is the eigenvector of the i-th eigenvalue
Examples:
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
eval, evec = uweigen(a)
eval1, evec1 = uweigvecs(a,b)
```
"""
function uweigen(a::Matrix{uwreal}; iter = 5, diag::Bool=true)
return uweigvals(a, iter=iter,diag=diag), uweigvecs(a, iter=iter)
end
function uweigen(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 5, diag::Bool=true)
return uweigvals(a, b, iter=iter, diag=diag), uweigvecs(a, b, iter=iter)
end
function norm_evec(evec::Matrix{uwreal}, ctnot::Matrix{uwreal})
n=size(evec,1)
res_evec=Matrix{uwreal}(undef, n, n)
for i =1:n
aux_norm = (uwdot(evec[:,i], uwdot(ctnot, evec[:,i])).^2).^0.25
res_evec[:,i] = 1 ./aux_norm .* evec[:,i]
end
return res_evec
end
function get_diag(a::Matrix{uwreal})
n = size(a,1)
res = [a[k, k] for k = 1:n]
return res
end
function get_diag(v::Vector{uwreal})
n = length(v)
res = idty(n)
for i =1:n
res[i,i] = v[i]
end
return res
end
"""
Given an input matrix of uwreals, this function returns the inverse of that matrix using QR decomposition and
backward substitution.
"""
function invert(a::Matrix{uwreal})
q,r = qr(a)
r_inv = upper_inversion(r)
return uwdot(r_inv, transpose(q))
end
"""
Performs a Cholesky decomposition of A, which must
be a symmetric and positive definite matrix. The function
returns the lower variant triangular matrix, L.
"""
function uwcholesky(A::Matrix{uwreal})
n = size(A,1)
L = fill(uwreal(0), n, n)
for i in 1:n
for k in 1:i
tmp_sum = sum(L[i,j] * L[k,j] for j in 1:k)
if i==k
L[i,k] = sqrt(((A[i,i] - tmp_sum)))
else
L[i,k] = (1.0 / L[k,k] * (A[i,k]-tmp_sum))
end
end
end
return L
end
"""
qr(A::Matrix{uwreal})
qr(A::Matrix{Float64})
This methods returns the QR decomposition of a matrix A of either uwreal or Float64 variables
"""
function qr(A::Matrix{uwreal})
m,n = size(A)
Q = idty(m)
for i in 1:(n-(m==n))
H = idty(m)
H[i:m,i:m] = make_householder(A[i:m,i])
Q = uwdot(Q,H)
A = uwdot(H,A)
end
for i in 2:n
for j in 1:i-1
A[i,j] = 0.0
end
end
return Q,A
end
function qr(A::Matrix{Float64})
m,n = size(A)
Q = Matrix{Float64}(I,m,m)
for i in 1:(n-(m==n))
H = Matrix{Float64}(I,m,m)
H[i:m,i:m] = make_householder(A[i:m,i])
X = similar(Q)
Y = similar(A)
Q = mul!(X,Q,H)
A = mul!(Y,H,A)
end
return Q,A
end
"""
make_householder(a::Vector{uwreal})
make_householder(a::Vector{Float64})
This method returns the householder matrix used in qr decomposition to get an upper triangular matrix
It takes as input either a vector of uwreal or a vector of Float64
"""
function make_householder(a::Vector{uwreal})
n = length(a)
v = Vector{uwreal}(undef, n)
for i in 1:n
v[i] = a[i] / (a[1] + uwcopysign(uwnorm(a), a[1]))
end
v[1] = uwreal(1.0)
H = idty(n)
H = H - (2.0 / uwdot(v,v)) * uwdot(reshape(v, :,1), transpose(v)) #this last term is a vector product
return H
end
function make_householder(a::Vector{Float64})
n = length(a)
v = a / (a[1] + copysign(norm(a), a[1]))
v[1] = 1.0
H = Matrix{Float64}(I, n, n)
println(typeof(v))
y = similar(H)
println(typeof(y))
H = H .- (2.0 / dot(v,v)) * mul!(y, v, transpose(v))
return H
end
"""
This function computes a vector u that generates a Householder reflection H = I - uu* satisfying Ha=nu*e1.
Accumulating this transformations any matrix can
be reduced to upper Hessenberg form.
"""
function house_gen(a::Vector{uwreal})
u = deepcopy(a)
nu = uwnorm(a)
if nu == 0
u[1] = sqrt(2)
end
if u[1] != 0
rho = u[1] / sqrt(u[1]*u[1])
else
rho = 1
end
u = uwdot(rho/nu, u)
u[1] += 1
u = uwdot(u , 1/sqrt(u[1]))
nu = -(rho) * nu
return u, nu
end
"""
hess_red(a::Matrix{uwreal})
Given a matrix of uwreal variables, this function returns the Hessenberg form of the matrix by applying householder transformations.
"""
function hess_reduce(a::Matrix{uwreal}; q_mat::Bool=false)
n = size(a,1)
H = deepcopy(a)
Q = idty(n)
for k in 1:n-2
u, H[k+1,k] = house_gen(H[k+1:n,k])
Q[k+1:n,k] = u
v = uwdot(u,H[k+1:n, k+1:n])
H[k+1:n, k+1:n] .= H[k+1:n, k+1:n] .- uwdot(reshape(u,length(u),1),v)
H[k+2:n,k] .= 0.0
v = uwdot(H[1:n,k+1:n], u)
H[1:n,k+1:n] .= H[1:n,k+1:n] .- uwdot(v,reshape(u,1,length(u)))
end
if q_mat
Id = idty(n)
for k in reverse(1:n-2)
u = Q[k+1:n, k]
v = uwdot(u, Q[k+1:n,k+1:n])
Q[k+1:n,k+1:n] .= Q[k+1:n,k+1:n] .- uwdot(reshape(u,length(u),1),v)
Q[:,k] = Id[:,k]
end
return H,Q
else
return H
end
end
"""
"""
function tridiag_reduction(a::Matrix{uwreal}; q_mat::Bool=false)
n = size(a,1)
H = deepcopy(a)
Q = idty(n)
for k in 1:n-2
u, H[k+1,k] = house_gen(H[k+1:n,k])
Q[k+1:n,k] = u
v = uwdot(u,H[k+1:n, k+1:n])
H[k+1:n, k+1:n] .= H[k+1:n, k+1:n] .- uwdot(reshape(u,length(u),1),v)
H[k+2:n,k] .= 0.0
v = uwdot(H[1:n,k+1:n], u)
H[1:n,k+1:n] .= H[1:n,k+1:n] .- uwdot(v,reshape(u,1,length(u)))
H[k,k+2:n] .= 0.0
end
if q_mat
Id = idty(n)
for k in reverse(1:n-2)
u = Q[k+1:n, k]
v = uwdot(u, Q[k+1:n,k+1:n])
Q[k+1:n,k+1:n] .= Q[k+1:n,k+1:n] .- uwdot(reshape(u,length(u),1),v)
Q[:,k] = Id[:,k]
end
return H,Q
else
return H
end
end
"""
upper_inversion(u::Matrix{uwreal})
This method uses backward propagation to compute the inverse of an upper triangular matrix u. Note that the inverse of u must be upper triangular as well.
Tests executed so far always confirmed this property.
"""
function upper_inversion(u::Matrix{uwreal})
n = size(u,1)
u_inv = Matrix{uwreal}(undef, n, n)
for i in 1:n
e_i = canonical_basis(n, i)
x = backward_sub(u, e_i)
u_inv[:,i] = x
end
return u_inv
end
"""
backward_sub(u::Matrix{uwreal}, y::Vector{uwreal})
This method operates backward substitution to solve a liner system Ux = y where U is an upper triangular uwreal matrix
and y is a uwreal vector. It returns a vector of uwreal x that might be used to inverte the upper triangular matrix U.
"""
function backward_sub(u::Matrix{uwreal}, y::Vector{uwreal})
n = length(y)
x = Vector{uwreal}(undef, n)
x[n] = y[n] / u[n,n]
for i in range(n-1,1, step=-1)
temp = 0.0
for j in i+1:n
temp += u[i,j] * x[j]
end
x[i] = (y[i] - temp) /u[i,i]
if x[i] !=0
end
end
return x
end
"""
canonical_basis(n::Int64, k::Int64)
Given the dimension n and the position k, canonical_basis returns a vector of uwreal with all entries set to zero
except for the k-th entry which is set to 1.0.
This method is used as input vector in backward_sub to compute the inverse of an upper triangular matrix.
"""
function canonical_basis(n::Int64, k::Int64)
e = zeros(n)
e[k] = 1.0
return [uwreal(e[i]) for i=1:n]
end
"""
uwdot(a::Vector{uwreal}, b::Vector{uwreal})
uwdot(a::Matrix{uwreal}, b::Matrix{uwreal})
Depending on the arguments, uwdot returns the vector vector or the matrix matrix product
of uwreal data type.
"""
uwdot(a::Vector{uwreal}, b::Vector{uwreal}) = sum(a .* b)
uwdot(a::Vector{uwreal}, b::uwreal) = [a[i] * b for i=1:length(a)]
uwdot(a::uwreal, b::Vector{uwreal}) = uwdot(b,a)
uwdot(a::Matrix{uwreal}, b::Vector{uwreal}) = uwdot(a, reshape(b,(length(b),1)))
uwdot(a::Vector{uwreal}, b::Matrix{uwreal}) = uwdot(reshape(a,(1,length(a))), b)
function uwdot(a::Matrix{uwreal}, b::Matrix{uwreal})
n,m = size(a)
k,p = size(b)
c = Matrix{uwreal}(undef, n, p)
if m != k
error("Error uwdot: in a*b the size of a is ", size(a), " while the size of b is ", size(b) )
end
for i = 1:n
for j = 1:p
c[i, j] = sum(a[i, :] .* b[:, j])
end
end
return c
end
"""
uwnorm(a::Vector{uwreal})
It returns the norm of a vector of uwreal
"""
function uwnorm(a::Vector{uwreal}; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
norm = sqrt(uwdot(a,a))
#isnothing(wpm) ? uwerr(norm) : uwerr(norm, wpm)
return norm
end
"""
Return the transpose of a matrix or vector of uwreals
"""
function Base.transpose(a::Matrix{uwreal})
res = similar(a)
n = size(a, 1)
if size(a,1) == 1
return reshape(a, :,1)
elseif size(a,2) == 1
return reshape(a, 1,:)
end
for i in 1:n-1
for j in i+1:n
temp = a[i,j]
res[i,j] = a[j,i]
res[j,i] = temp
end
res[i,i] = a[i,i]
end
res[n,n] = a[n,n]
return res
end
function Base.transpose(vec::Vector{uwreal})
res = deepcopy(vec)
return reshape(res,1,:)
end
"""
uwcopysign(a::uwreal, b::uwreal)
It implements the copysign function for uwreal variables
"""
function uwcopysign(a::uwreal, b::uwreal)
c = deepcopy(a)
aux = copysign(value(a), value(b))
c.mean = aux
return c
end
"""
idty(n::Int64)
It returns an indetity matrix of uwreal variables given the size n
"""
function idty(n::Int64)
res = Matrix{uwreal}(undef,n,n)
for i in 1:n
for j in 1:n
if i==j
res[i,j]=1.0
else
res[i,j]=0.0
end
end
end
return res
end
function make_positive_def(A::Matrix; eps::Float64=10^(-14))
vals, vecs = eigen(Symmetric(A))
idx = findall(x-> x<0.0, vals)
vals[idx] .= eps
return Symmetric(vecs * Diagonal(vals) * vecs')
#B = 0.5 * (A + A')
#U, S, V = svd(B)
#H = V * Diagonal(S) * V'
#A_hat = 0.5 * (B + H)
#A_hat = 0.5 * ( A_hat + A_hat')
#k = 0
#while !isposdef(A_hat)
# mineig = minimum(eigvals(A_hat))
# eps = 2.220446049250313e-16
# A_hat = A_hat + (-mineig*k^2 .+ eps*Matrix(I, size(A)))
# k+=1
#end
#return A_hat
end
function invert_covar_matrix(A::Matrix; eps::Float64=10^(-9))
F = svd(A)
s_diag = F.S
for i in 1:length(s_diag)
if s_diag[i] < eps
s_diag[i] = 0.0
else
s_diag[i] = 1 / s_diag[i]
end
end
cov_inv = F.V * Diagonal(s_diag) * F.U'
return cov_inv
end
function more_penrose_inv(A::Matrix)
F = svd(A)
end
########################
# OPERATOR OVERLOADING
########################
function Base.:*(x::uwreal, y::Array{Any})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{Any}, y::uwreal) = Base.:*(y,x)
function Base.:*(x::uwreal, y::Array{Float64})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{Float64}, y::uwreal) = Base.:*(y,x)
function Base.:*(x::uwreal, y::Array{uwreal})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{uwreal}, y::uwreal) = Base.:*(y,x)
function Base.:+(x::uwreal, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:+(x::Float64, y::Vector{Float64})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:+(x::Float64, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:-(x::Float64, y::Vector{Any})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.:-(x::Float64, y::Vector{Float64})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.:-(x::Float64, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.length(uw::uwreal)
return length(value(uw))
end
Base.length(c::juobs.Corr) = 1
function Base.iterate(uw::uwreal)
return iterate(uw, 1)
end
function Base.iterate(c::juobs.Corr,state::Int64)
if state >length(c)
return nothing
else
return c[state], state+1
end
end
Base.iterate(c::juobs.Corr) = iterate(c,1)
function Base.iterate(uw::uwreal, state::Int64)
if state > length(uw)
return nothing
else
return uw[state], state +1
end
end
function Base.iterate(uw::Vector{uwreal})
return iterate(uw, 1)
end
function Base.iterate(uw::Vector{uwreal}, state::Int64)
if state > length(uw)
return nothing
else
return uw[state], state +1
end
end
function Base.getindex(uw::uwreal, ii)
if ii>length(uw) || ii<=0
throw(BoundsError(uw,[ii]))
end
return uw # uwreal([getindex(value(uw), kwargs...), err(uw)], " ")
end
function Base.getindex(c::juobs.Corr, ii)
if ii>length(c) || ii<=0
throw(BoundsError(c,[ii]))
end
return c
end
"""
Base.abs2(uw::uwreal)
Return the square absolute value.
## Warning
Previously this function returned the absolute value of `uw`, not the square absolute value.
The function was changed to make it consistent with `Base.abs2()` in Julia.
When used, a warning is printed to informe of this change future users, feel free to remove the warning
from your branch!
"""
function Base.abs2(uw::uwreal)
@warn "This function was updated by Antonino to make it consisted with the Julia version of it.
Check the documentation for info and then feel free to comment out this warning in your branch"
return uw^2
end
"""
Base.abs(uw::uwreal)
Return the absolute value of `uw` by taking the square root of `uw^2`.
"""
Base.abs(uw::uwreal) = sqrt(uw^2);
"""
Base.:*(x::uwreal, y::Matrix{uwreal})
Multiplication operator overloading between uwreal variable and matrix of uwreal
"""
function Base.:*(x::uwreal, y::Matrix{uwreal})
n,m = size(y)
res = Matrix{uwreal}(undef, n,m)
for i in 1:n
for j in 1:m
res[i,j] = x * y[i,j]
if res[i,j] !=0 && res[i,j] !=1
end
end
end
return res
end
#=
using ForwardDiff, LinearAlgebra
for op in (:eigvals, :eigvecs)
@eval function LinearAlgebra.$op(a::Matrix{uwreal})
function fvec(x::Matrix)
return LinearAlgebra.$op(x)
end
return fvec(value.(a))
#return uwreal(LinearAlgebra.$op(a.mean), a.prop, ForwardDiff.derivative($op, a.mean)*a.der)
end
end
=#
@doc raw"""
get_matrix(diag::Vector{Array}, upper::Vector{Array} )
This function allows the user to build an array of matrices, where each matrix is a symmetric matrix of correlators at a given timeslice.
It takes as input:
- `diag::Vector{Array}`: vector of correlators. Each correlator will constitute a diagonal element of the matrices A[i] where i runs over the timeslices, i.e.
`A[i][1,1] = diag[1], .... A[i][n,n] = diag[n]`
`Given n=length(diag)`, the matrices will have dimension n*n
- `upper::Vector{Array}`: vector of correlators liying on the upper diagonal position.
`A[i][1,2] = upper[1], .. , A[i][1,n] = upper[n-1], `
`A[i][2,3] = upper[n], .. , A[i][n-1,n] = upper[n(n-1)/2]`
Given n, `length(upper)=n(n-1)/2`
The method returns an array of symmetric matrices of dimension n for each timeslice
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
Julia> matrices[i]
pp[i] ap[i]
pa[i] aa[i]
## where i runs over the timeslices
```
"""
function get_matrix(corr_diag::Vector{Vector{uwreal}}, corr_upper::Vector{Vector{uwreal}})
time = length(corr_diag[1]) # total time slices
n = length(corr_diag) # matrix dimension
d = length(corr_upper) # upper elements
if d != (n*(n-1) / 2)
error("Error get_matrix")
end
res = Vector{Matrix{uwreal}}(undef, time) # array with all the matrices at each time step.
aux = Matrix{uwreal}(undef, n, n)
for t in 1:time
count=0
for i in range(n-1,1, step=-1)
for j in range(n, i+1, step=-1)
aux[i,j] = corr_upper[d-count][t]
aux[j,i] = corr_upper[d-count][t]
count +=1
end
aux[i,i] = corr_diag[i][t]
end
aux[n,n] = corr_diag[n][t]
res[t] = copy(aux)
end
return res
end
get_matrix(corr_diag::Vector{Corr}, corr_upper::Vector{Corr}) = get_matrix(getfield.(corr_diag, :obs), getfield.(corr_upper, :obs))
@doc raw"""
energies(evals::Vector{Array}; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
This method computes the energy level from the eigenvalues according to:
``E_i(t) = \log(λ(t) / λ(t+1))``
where `i=1,..,n` with `n=length(evals[1])` and `t=1,..,T` total time slices.
It returns a vector array en where each entry en[i][t] contains the i-th states energy at time t
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
## solve the GEVP
evals = getall_eigvals(matrices, 5) #where t_0=5
en = energies(evals)
Julia> en[i] # i-th state energy at each timeslice
```
"""
function energies(evals::Union{Vector{Vector{uwreal}},Array{Array{uwreal}} }; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
time = length(evals)
n = length(evals[1])
eff_en = Array{Array{uwreal}}(undef, n)
aux_en = Vector{uwreal}(undef, time-1)
for i in 1:n
for t in 1:time-1
ratio = evals[t][i] / evals[t+1][i]
aux_en[t] = 0.5*log(ratio * ratio)
end
#uwerr.(aux_en)
#isnothing(wpm) ? uwerr.(aux_en) : [uwerr(a, wpm) for a in aux_en]
eff_en[i] = copy(aux_en)
end
return eff_en
end
@doc raw"""
getall_eigvals(a::Vector{Matrix}, t0; iter=30 )
This function solves a GEVP problem, returning the eigenvalues, for a list of matrices, taking as generalised matrix the one
at index t0, i.e:
``C(t_i)v_i = λ_i C(t_0) v_i``, with i=1:lenght(a)
It takes as input:
- `a::Vector{Matrix}` : a vector of matrices
- `t0::Int64` : idex value at which the fixed matrix is taken
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res` = Vector{Vector{uwreal}}
where `res[i]` are the generalised eigenvalues of the i-th matrix of the input array.
Examples:
```@example
## load data
pp_data = read_mesons(path, "G5", "G5")
pa_data = read_mesons(path, "G5", "G0G5")
aa_data = read_mesons(path, "G0G5", "G0G5")
## create Corr struct
corr_pp = corr_obs.(pp_data)
corr_pa = corr_obs.(pa_data)
corr_aa = corr_obs.(aa_data) # array of correlators for different \mu_q combinations
## set up matrices
corr_diag = [corr_pp[1], corr_aa[1]]
corr_upper = [corr_pa[1]]
matrices = [corr_diag, corr_upper]
## solve the GEVP
#evals = getall_eigvals(matrices, 5) #where t_0=5
Julia>
```
"""
function getall_eigvals(a::Vector{Matrix{uwreal}}, t0::Int64; iter=5 )
n = length(a)
res = Vector{Vector{uwreal}}(undef, n)
[res[i] = uweigvals(a[i], a[t0], iter=iter) for i=1:n]
return res
end
@doc raw"""
uweigvals(a::Matrix{uwreal}; iter = 30)
uweigvals(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvalues of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvalues instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30`: optional flag to set the iterations of the qr algorithm used to solve the eigenvalue problem
It returns:
- `res = Vector{uwreal}`: a vector where each elements is an eigenvalue
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
res = uweigvals(a) ##eigenvalues
res1 = uweigvals(a,b) ## generalised eigenvalues
```
"""
function uweigvals(a::Matrix{uwreal}; iter::Int64=5, diag::Bool=true)
n = size(a,1)
if n == 2
return uweigvals_dim_geq3(a, iter=iter, diag=diag) #uweigvals_dim2(a)
else
return uweigvals_dim_geq3(a, iter=iter, diag=diag)
end
end
function uweigvals(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 5, diag::Bool=true)
c = uwdot(invert(b),a)
n = size(c,1)
if n == 2
return uweigvals_dim_geq3(c, iter=iter, diag=diag) #uweigvals_dim2(c)
else
return uweigvals_dim_geq3(c, iter=iter, diag=diag)
end
#for k in 1:iter
# q_k, r_k = qr(c)
# c = uwdot(r_k, q_k)
#end
end
function uweigvals_dim_geq3(a::Matrix{uwreal}; iter = 5, diag::Bool=true)
n = size(a,1)
#a = tridiag_reduction(a)
for k in 1:iter
q_k, r_k = qr(a)
a = uwdot(r_k, q_k)
end
diag == true ? (return get_diag(a)) : (return a)
end
function uweigvals_dim2(a::Matrix{uwreal})
res = Vector{uwreal}(undef,2)
aux_sum = a[1,1] + a[2,2]
delta = ADerrors.sqrt((aux_sum)^2 - 4*(a[1,1]*a[2,2]- a[1,2]*a[2,1]))
res[1] = (aux_sum + delta) / 2
res[2] = (aux_sum - delta) / 2
return res
end
@doc raw"""
getall_eigvecs(a::Vector{Matrix}, delta_t; iter=30 )
This function solves a GEVP problem, returning the eigenvectors, for a list of matrices.
``C(t_i)v_i = λ_i C(t_i-\delta_t) v_i ``, with i=1:lenght(a)
Here `delta_t` is the time shift within the two matrices of the problem, and is kept fixed.
It takes as input:
- `a::Vector{Matrix}` : a vector of matrices
- `delta_t::Int64` : the fixed time shift t-t_0
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res = Vector{Matrix{uwreal}}`
where each `res[i]` is a matrix with the eigenvectors as columns
Examples:
```@example
mat_array = get_matrix(diag, upper_diag)
evecs = getall_eigvecs(mat_array, 5)
```
"""
function getall_eigvecs(a::Vector{Matrix{uwreal}}, delta_t; iter=5)
n = length(a)-delta_t
res = Vector{Matrix{uwreal}}(undef, n)
[res[i] = uweigvecs(a[i+delta_t], a[i]) for i=1:n]
return res
end
@doc raw"""
uweigvecs(a::Matrix{uwreal}; iter = 30)
uweigvecs(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvectors of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvectors instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `res = Matrix{uwreal}`: a matrix where each column is an eigenvector
Examples:
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
res = uweigvecs(a) ##eigenvectors in column
res1 = uweigvecs(a,b) ## generalised eigenvectors in column
```
"""
function uweigvecs(a::Matrix{uwreal}; iter = 10)
n = size(a,1)
# if n > 2
# a, q = tridiag_reduction(a, q_mat=true)
# end
u = idty(n)
for k in 1:iter
q_k, r_k = qr(a)
a = uwdot(r_k, q_k)
u = uwdot(u, q_k)
end
#n > 2 ? (return uwdot(q,u)) : (return u)
return u
end
function uweigvecs(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 10)
c = uwdot(invert(b), a)
#return uweigvecs(c, iter=iter)
n = size(c,1)
u = idty(n)
for k in 1:iter
q_k, r_k = qr(c)
c = uwdot(r_k, q_k)
u = uwdot(u, q_k)
end
return u
end
@doc raw"""
uweigen(a::Matrix{uwreal}; iter = 30)
uweigen(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 30)
This function computes the eigenvalues and the eigenvectors of a matrix of uwreal objects.
If a second matrix b is given as input, it returns the generalised eigenvalues and eigenvectors instead.
It takes as input:
- `a::Matrix{uwreal}` : a matrix of uwreal
- `b::Matrix{uwreal}` : a matrix of uwreal, optional
- `iter=30` : the number of iterations of the qr algorithm used to extract the eigenvalues
It returns:
- `evals = Vector{uwreal}`: a vector where each elements is an eigenvalue
- `evecs = Matrix{uwreal}`: a matrix where the i-th column is the eigenvector of the i-th eigenvalue
Examples:
```@example
a = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
b = Matrix{uwreal}(nothing, n,n) ## n*n matrix of uwreal with nothing entries
eval, evec = uweigen(a)
eval1, evec1 = uweigvecs(a,b)
```
"""
function uweigen(a::Matrix{uwreal}; iter = 5, diag::Bool=true)
return uweigvals(a, iter=iter,diag=diag), uweigvecs(a, iter=iter)
end
function uweigen(a::Matrix{uwreal}, b::Matrix{uwreal}; iter = 5, diag::Bool=true)
return uweigvals(a, b, iter=iter, diag=diag), uweigvecs(a, b, iter=iter)
end
function norm_evec(evec::Matrix{uwreal}, ctnot::Matrix{uwreal})
n=size(evec,1)
res_evec=Matrix{uwreal}(undef, n, n)
for i =1:n
aux_norm = (uwdot(evec[:,i], uwdot(ctnot, evec[:,i])).^2).^0.25
res_evec[:,i] = 1 ./aux_norm .* evec[:,i]
end
return res_evec
end
function get_diag(a::Matrix{uwreal})
n = size(a,1)
res = [a[k, k] for k = 1:n]
return res
end
function get_diag(v::Vector{uwreal})
n = length(v)
res = idty(n)
for i =1:n
res[i,i] = v[i]
end
return res
end
"""
Given an input matrix of uwreals, this function returns the inverse of that matrix using QR decomposition and
backward substitution.
"""
function invert(a::Matrix{uwreal})
q,r = qr(a)
r_inv = upper_inversion(r)
return uwdot(r_inv, transpose(q))
end
"""
Performs a Cholesky decomposition of A, which must
be a symmetric and positive definite matrix. The function
returns the lower variant triangular matrix, L.
"""
function uwcholesky(A::Matrix{uwreal})
n = size(A,1)
L = fill(uwreal(0), n, n)
for i in 1:n
for k in 1:i
tmp_sum = sum(L[i,j] * L[k,j] for j in 1:k)
if i==k
L[i,k] = sqrt(((A[i,i] - tmp_sum)))
else
L[i,k] = (1.0 / L[k,k] * (A[i,k]-tmp_sum))
end
end
end
return L
end
"""
qr(A::Matrix{uwreal})
qr(A::Matrix{Float64})
This methods returns the QR decomposition of a matrix A of either uwreal or Float64 variables
"""
function qr(A::Matrix{uwreal})
m,n = size(A)
Q = idty(m)
for i in 1:(n-(m==n))
H = idty(m)
H[i:m,i:m] = make_householder(A[i:m,i])
Q = uwdot(Q,H)
A = uwdot(H,A)
end
for i in 2:n
for j in 1:i-1
A[i,j] = 0.0
end
end
return Q,A
end
function qr(A::Matrix{Float64})
m,n = size(A)
Q = Matrix{Float64}(I,m,m)
for i in 1:(n-(m==n))
H = Matrix{Float64}(I,m,m)
H[i:m,i:m] = make_householder(A[i:m,i])
X = similar(Q)
Y = similar(A)
Q = mul!(X,Q,H)
A = mul!(Y,H,A)
end
return Q,A
end
"""
make_householder(a::Vector{uwreal})
make_householder(a::Vector{Float64})
This method returns the householder matrix used in qr decomposition to get an upper triangular matrix
It takes as input either a vector of uwreal or a vector of Float64
"""
function make_householder(a::Vector{uwreal})
n = length(a)
v = Vector{uwreal}(undef, n)
for i in 1:n
v[i] = a[i] / (a[1] + uwcopysign(uwnorm(a), a[1]))
end
v[1] = uwreal(1.0)
H = idty(n)
H = H - (2.0 / uwdot(v,v)) * uwdot(reshape(v, :,1), transpose(v)) #this last term is a vector product
return H
end
function make_householder(a::Vector{Float64})
n = length(a)
v = a / (a[1] + copysign(norm(a), a[1]))
v[1] = 1.0
H = Matrix{Float64}(I, n, n)
println(typeof(v))
y = similar(H)
println(typeof(y))
H = H .- (2.0 / dot(v,v)) * mul!(y, v, transpose(v))
return H
end
"""
This function computes a vector u that generates a Householder reflection H = I - uu* satisfying Ha=nu*e1.
Accumulating this transformations any matrix can
be reduced to upper Hessenberg form.
"""
function house_gen(a::Vector{uwreal})
u = deepcopy(a)
nu = uwnorm(a)
if nu == 0
u[1] = sqrt(2)
end
if u[1] != 0
rho = u[1] / sqrt(u[1]*u[1])
else
rho = 1
end
u = uwdot(rho/nu, u)
u[1] += 1
u = uwdot(u , 1/sqrt(u[1]))
nu = -(rho) * nu
return u, nu
end
"""
hess_red(a::Matrix{uwreal})
Given a matrix of uwreal variables, this function returns the Hessenberg form of the matrix by applying householder transformations.
"""
function hess_reduce(a::Matrix{uwreal}; q_mat::Bool=false)
n = size(a,1)
H = deepcopy(a)
Q = idty(n)
for k in 1:n-2
u, H[k+1,k] = house_gen(H[k+1:n,k])
Q[k+1:n,k] = u
v = uwdot(u,H[k+1:n, k+1:n])
H[k+1:n, k+1:n] .= H[k+1:n, k+1:n] .- uwdot(reshape(u,length(u),1),v)
H[k+2:n,k] .= 0.0
v = uwdot(H[1:n,k+1:n], u)
H[1:n,k+1:n] .= H[1:n,k+1:n] .- uwdot(v,reshape(u,1,length(u)))
end
if q_mat
Id = idty(n)
for k in reverse(1:n-2)
u = Q[k+1:n, k]
v = uwdot(u, Q[k+1:n,k+1:n])
Q[k+1:n,k+1:n] .= Q[k+1:n,k+1:n] .- uwdot(reshape(u,length(u),1),v)
Q[:,k] = Id[:,k]
end
return H,Q
else
return H
end
end
"""
"""
function tridiag_reduction(a::Matrix{uwreal}; q_mat::Bool=false)
n = size(a,1)
H = deepcopy(a)
Q = idty(n)
for k in 1:n-2
u, H[k+1,k] = house_gen(H[k+1:n,k])
Q[k+1:n,k] = u
v = uwdot(u,H[k+1:n, k+1:n])
H[k+1:n, k+1:n] .= H[k+1:n, k+1:n] .- uwdot(reshape(u,length(u),1),v)
H[k+2:n,k] .= 0.0
v = uwdot(H[1:n,k+1:n], u)
H[1:n,k+1:n] .= H[1:n,k+1:n] .- uwdot(v,reshape(u,1,length(u)))
H[k,k+2:n] .= 0.0
end
if q_mat
Id = idty(n)
for k in reverse(1:n-2)
u = Q[k+1:n, k]
v = uwdot(u, Q[k+1:n,k+1:n])
Q[k+1:n,k+1:n] .= Q[k+1:n,k+1:n] .- uwdot(reshape(u,length(u),1),v)
Q[:,k] = Id[:,k]
end
return H,Q
else
return H
end
end
"""
upper_inversion(u::Matrix{uwreal})
This method uses backward propagation to compute the inverse of an upper triangular matrix u. Note that the inverse of u must be upper triangular as well.
Tests executed so far always confirmed this property.
"""
function upper_inversion(u::Matrix{uwreal})
n = size(u,1)
u_inv = Matrix{uwreal}(undef, n, n)
for i in 1:n
e_i = canonical_basis(n, i)
x = backward_sub(u, e_i)
u_inv[:,i] = x
end
return u_inv
end
"""
backward_sub(u::Matrix{uwreal}, y::Vector{uwreal})
This method operates backward substitution to solve a liner system Ux = y where U is an upper triangular uwreal matrix
and y is a uwreal vector. It returns a vector of uwreal x that might be used to inverte the upper triangular matrix U.
"""
function backward_sub(u::Matrix{uwreal}, y::Vector{uwreal})
n = length(y)
x = Vector{uwreal}(undef, n)
x[n] = y[n] / u[n,n]
for i in range(n-1,1, step=-1)
temp = 0.0
for j in i+1:n
temp += u[i,j] * x[j]
end
x[i] = (y[i] - temp) /u[i,i]
if x[i] !=0
end
end
return x
end
"""
canonical_basis(n::Int64, k::Int64)
Given the dimension n and the position k, canonical_basis returns a vector of uwreal with all entries set to zero
except for the k-th entry which is set to 1.0.
This method is used as input vector in backward_sub to compute the inverse of an upper triangular matrix.
"""
function canonical_basis(n::Int64, k::Int64)
e = zeros(n)
e[k] = 1.0
return [uwreal(e[i]) for i=1:n]
end
"""
uwdot(a::Vector{uwreal}, b::Vector{uwreal})
uwdot(a::Matrix{uwreal}, b::Matrix{uwreal})
Depending on the arguments, uwdot returns the vector vector or the matrix matrix product
of uwreal data type.
"""
uwdot(a::Vector{uwreal}, b::Vector{uwreal}) = sum(a .* b)
uwdot(a::Vector{uwreal}, b::uwreal) = [a[i] * b for i=1:length(a)]
uwdot(a::uwreal, b::Vector{uwreal}) = uwdot(b,a)
uwdot(a::Matrix{uwreal}, b::Vector{uwreal}) = uwdot(a, reshape(b,(length(b),1)))
uwdot(a::Vector{uwreal}, b::Matrix{uwreal}) = uwdot(reshape(a,(1,length(a))), b)
function uwdot(a::Matrix{uwreal}, b::Matrix{uwreal})
n,m = size(a)
k,p = size(b)
c = Matrix{uwreal}(undef, n, p)
if m != k
error("Error uwdot: in a*b the size of a is ", size(a), " while the size of b is ", size(b) )
end
for i = 1:n
for j = 1:p
c[i, j] = sum(a[i, :] .* b[:, j])
end
end
return c
end
"""
uwnorm(a::Vector{uwreal})
It returns the norm of a vector of uwreal
"""
function uwnorm(a::Vector{uwreal}; wpm::Union{Dict{Int64,Vector{Float64}},Dict{String,Vector{Float64}}, Nothing}=nothing)
norm = sqrt(uwdot(a,a))
#isnothing(wpm) ? uwerr(norm) : uwerr(norm, wpm)
return norm
end
"""
Return the transpose of a matrix or vector of uwreals
"""
function Base.transpose(a::Matrix{uwreal})
res = similar(a)
n = size(a, 1)
if size(a,1) == 1
return reshape(a, :,1)
elseif size(a,2) == 1
return reshape(a, 1,:)
end
for i in 1:n-1
for j in i+1:n
temp = a[i,j]
res[i,j] = a[j,i]
res[j,i] = temp
end
res[i,i] = a[i,i]
end
res[n,n] = a[n,n]
return res
end
function Base.transpose(vec::Vector{uwreal})
res = deepcopy(vec)
return reshape(res,1,:)
end
"""
uwcopysign(a::uwreal, b::uwreal)
It implements the copysign function for uwreal variables
"""
function uwcopysign(a::uwreal, b::uwreal)
c = deepcopy(a)
aux = copysign(value(a), value(b))
c.mean = aux
return c
end
"""
idty(n::Int64)
It returns an indetity matrix of uwreal variables given the size n
"""
function idty(n::Int64)
res = Matrix{uwreal}(undef,n,n)
for i in 1:n
for j in 1:n
if i==j
res[i,j]=1.0
else
res[i,j]=0.0
end
end
end
return res
end
function make_positive_def(A::Matrix; eps::Float64=10^(-14))
vals, vecs = eigen(Symmetric(A))
idx = findall(x-> x<0.0, vals)
vals[idx] .= eps
return Symmetric(vecs * Diagonal(vals) * vecs')
#B = 0.5 * (A + A')
#U, S, V = svd(B)
#H = V * Diagonal(S) * V'
#A_hat = 0.5 * (B + H)
#A_hat = 0.5 * ( A_hat + A_hat')
#k = 0
#while !isposdef(A_hat)
# mineig = minimum(eigvals(A_hat))
# eps = 2.220446049250313e-16
# A_hat = A_hat + (-mineig*k^2 .+ eps*Matrix(I, size(A)))
# k+=1
#end
#return A_hat
end
function invert_covar_matrix(A::Matrix; eps::Float64=10^(-9))
F = svd(A)
s_diag = F.S
for i in 1:length(s_diag)
if s_diag[i] < eps
s_diag[i] = 0.0
else
s_diag[i] = 1 / s_diag[i]
end
end
cov_inv = F.V * Diagonal(s_diag) * F.U'
return cov_inv
end
function more_penrose_inv(A::Matrix)
F = svd(A)
end
########################
# OPERATOR OVERLOADING
########################
function Base.:*(x::uwreal, y::Array{Any})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{Any}, y::uwreal) = Base.:*(y,x)
function Base.:*(x::uwreal, y::Array{Float64})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{Float64}, y::uwreal) = Base.:*(y,x)
function Base.:*(x::uwreal, y::Array{uwreal})
N = size(y, 1)
return fill(x, N) .* y
end
Base.:*(x::Array{uwreal}, y::uwreal) = Base.:*(y,x)
function Base.:+(x::uwreal, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:+(x::Float64, y::Vector{Float64})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:+(x::Float64, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .+ y
end
function Base.:-(x::Float64, y::Vector{Any})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.:-(x::Float64, y::Vector{Float64})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.:-(x::Float64, y::Vector{uwreal})
N = size(y, 1)
return fill(x, N) .- y
end
function Base.length(uw::uwreal)
return length(value(uw))
end
Base.length(c::juobs.Corr) = 1
function Base.iterate(uw::uwreal)
return iterate(uw, 1)
end
function Base.iterate(c::juobs.Corr,state::Int64)
if state >length(c)
return nothing
else
return c[state], state+1
end
end
Base.iterate(c::juobs.Corr) = iterate(c,1)
function Base.iterate(uw::uwreal, state::Int64)
if state > length(uw)
return nothing
else
return uw[state], state +1
end
end
function Base.iterate(uw::Vector{uwreal})
return iterate(uw, 1)
end
function Base.iterate(uw::Vector{uwreal}, state::Int64)
if state > length(uw)
return nothing
else
return uw[state], state +1
end
end
function Base.getindex(uw::uwreal, ii)
if ii>length(uw) || ii<=0
throw(BoundsError(uw,[ii]))
end
return uw # uwreal([getindex(value(uw), kwargs...), err(uw)], " ")
end
function Base.getindex(c::juobs.Corr, ii)
if ii>length(c) || ii<=0
throw(BoundsError(c,[ii]))
end
return c
end
"""
Base.abs2(uw::uwreal)
Return the square absolute value.
## Warning
Previously this function returned the absolute value of `uw`, not the square absolute value.
The function was changed to make it consistent with `Base.abs2()` in Julia.
"""
Base.abs2(uw::uwreal) = uw^2
"""
Base.abs(uw::uwreal)
Return the absolute value of `uw` by taking the square root of `uw^2`.
"""
Base.abs(uw::uwreal) = sqrt(uw^2);
"""
Base.:*(x::uwreal, y::Matrix{uwreal})
Multiplication operator overloading between uwreal variable and matrix of uwreal
"""
function Base.:*(x::uwreal, y::Matrix{uwreal})
n,m = size(y)
res = Matrix{uwreal}(undef, n,m)
for i in 1:n
for j in 1:m
res[i,j] = x * y[i,j]
if res[i,j] !=0 && res[i,j] !=1
end
end
end
return res
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment