Sciences Po, Spring 2019
where
We will construct a grid of $M\geq J$ points ${x_1,\dots,x_M}$ within the domain $\mathbb{R}^d$, and we will denote the residuals at each grid point by $\epsilon = {\epsilon_1,\dots,\epsilon_M}$:
$$ \left[\begin{array}{c} \epsilon_1 \\ \vdots \\ \epsilon_M \\ \end{array} \right] = \left[\begin{array}{c} f(x_1) \\ \vdots \\ f(x_M) \end{array} \right] - \left[\begin{array}{ccc} \phi_1(x_1) & \dots & \phi_J(x_1) \\ \vdots & \ddots & \vdots \\ \phi_1(x_M) & \dots & \phi_J(x_M) \end{array} \right] \cdot \left[\begin{array}{c} c_1 \\ \vdots \\ c_J \end{array} \right] \\ \mathbf{\epsilon} = \mathbf{y} - \mathbf{\Phi c} $$where the second line uses vector notation, and $\mathbf{y}$ has all values of $f$.
for the case with $m$ evaluation nodes for $x$, and $n$ basis functions for each $x_i$.
0 & n\ne m \\ \pi & n=m=0 \\ \frac{\pi}{2} & n=m\ne 0
\end{cases}
$$ T_0(x) & = 1 \\
T_1(x) & = x \\
T_{n+1}(x) & = 2xT_n(x) - T_{n-1}(x).
\end{align}function T(x,n)
@assert (x >= -1) & (x <= 1)
if n == 0
return 1.0
elseif n==1
return x
else
2*x*T(x,n-1) - T(x,n-2)
end
end
T2(x,n) = cos(n* acos(x))
@show T(0.5,3)
using Test
@assert T2(0.5,3) == T(0.5,3)
# weighting function
w(x) = 1.0 / sqrt(1-x^2)
using Statistics
using Test
# simple integration works for n not equal m
@assert isapprox(mean(T(x,0) * T(x,1) * w(x) for x in range(-0.99,stop = 0.99, length=100)), 0.0, atol = 1e-16)
@assert isapprox(mean(T(x,4) * T(x,5) * w(x) for x in range(-0.99,stop = 0.99, length=100)), 0.0, atol = 1e-16)
using Plots
using FastGaussQuadrature
gcnodes = gausschebyshev(21) # generate 11 Chebyshev Nodes
gr()
scatter(gcnodes,ones(21),ylims=(0.9,1.1),m=(:red,:+),legend=false,size=(600,100),yticks=nothing)
using BasisMatrices
x = range(-4, stop = 4 ,length = 10)
ϕ = Basis(ChebParams(length(x),-4,4))
ϕ
S, y = nodes(ϕ)
Φ = BasisMatrix(ϕ, Expanded(), S, 0)
Φ.vals[1]' * Φ.vals[1]
Julia
: ApproxFun.jl
¶ApproxFun.jl
is a Julia package based on the Matlab package chebfun
. It is quite amazing.using LinearAlgebra, SpecialFunctions, Plots, ApproxFun
x = Fun(identity,0..10)
f = sin(x^2)
g = cos(x)
h = f + g^2
r = roots(h)
rp = roots(h')
using Plots
plot(h,labels = "h")
scatter!(r,h.(r),labels="roots h")
scatter!(rp,h.(rp),labels = "roots h'")
# works even with discontinuities!
ff = x->sign(x-0.1)/2 + cos(4*x); # sign introduces a jump at 0.1
x = Fun(identity) # set up a function space
space(x)
f = ff(x) # define ff on that space
plot(f) # plot
# whats the first deriv of that function at at 0.785?
println(f'(0.785))
# and close to the jump, at 0.100001?
println(f'(0.1000001))
# integral of f?
g1 = cumsum(f)
g = g1 + f(-1)
integral = norm(f-g)
integral
S = Chebyshev(1..2);
n = 100; m = 50;
p = range(1,stop=2,length=n); # a non-default grid
v = exp.(p); # values at the non-default grid
V = Array{Float64}(undef,n,m); # Create a Vandermonde matrix by evaluating the basis at the grid
for k = 1:m
V[:,k] = Fun(S,[zeros(k-1);1]).(p)
end
f = Fun(S,V\v);
@show f(1.1)
@show exp(1.1)
ApproXD
):= length(z)
)We can define $B_j^{k,\mathbf{z}} (x)$ recursively like this:
$$ B_j^{k,\mathbf{z}} (x) = \frac{x-z_{j-k}}{z_j - z_{j-k}} B_{j-1}^{k-1,\mathbf{z}} (x) + \frac{z_{j+1}-x}{z_{j+1} - z_{j+1-k}} B_{j}^{k-1,\mathbf{z}} (x), j=1,\dots,n $$
The derivative wrt to it's argument $x$ is
$$ \frac{d B_j^{k,\mathbf{z}} (x)}{dx} = \frac{k}{z_j - z_{j-k}} B_{j-1}^{k-1,\mathbf{z}} (x) + \frac{k}{z_{j+1} - z_{j+1-k}} B_{j}^{k-1,\mathbf{z}} (x), j=1,\dots,n $$
Similarly, the Integral is just the sum over the basis functions: $$ \int_a^x B_j^{k,\mathbf{z}} (y) dy = \sum_{i=j}^n \frac{z_i - z_{i-k}}{k} B_{i+1}^{k+1,\mathbf{z}} (x) $$
using ApproXD
# ] dev https://github.com/floswald/ApproXD.jl
bs = BSpline(7,3,0,1) #7 knots, degree 3 in [0,1]
# how many basis functions? (go back 1 slide.)
# getNumCoefs(bs)
x = range(0,stop =1.0, length = 500)
B = Array(getBasis(collect(x),bs))
plot(x,B,layout=(3,3),grid=false,ylim=(-0.1,1.1),legend=false,linewidth=2,linecolor=:black)
# Notice that placing each of those panels on top of each other generates a sparse matrix!
plot(x,B,grid=false,ylim=(-0.1,1.1),legend=false)
bs = BSpline(9,1,0,1) #9 knots, degree 1 in [0,1]
x = range(0,stop = 1.0,length = 500)
B = Array(getBasis(collect(x),bs))
plot(x,B,layout=(3,3),grid=false,ylim=(-0.1,1.1),legend=false,linewidth=2,linecolor=:black)
julia
implements searchsortedlast
. gg(x) = (1+25x^2)^(-1)
plot(gg)
Interpolations.jl
¶1:N
1:N
to different domains# interpolation
# restart kernel!
using Interpolations
A = rand(10,5)
itp = interpolate(A, Interpolations.BSpline(Quadratic(Reflect(OnCell()))))
itp(1.9,4.9)
A
A[2,5]
A_x = 1.:2.:40. # x in [1,40]
A = [log(x) for x in A_x] # f(x)
itp = interpolate(A, Interpolations.BSpline(Cubic(Interpolations.Line(OnGrid()))))
sitp = scale(itp, A_x) # scale x-axis of interpolator
@show sitp(3.) # exactly log(3.)
@show sitp(3.5) # approximately log(3.5)
@show itp(3); # is the 3rd index(!) in A, not the *value* 3
# Same for 2D
A_x1 = 1:.1:10
A_x2 = 1:.5:20
fff(x1, x2) = log(x1+x2)
A = [fff(x1,x2) for x1 in A_x1, x2 in A_x2]
itp = interpolate(A, Interpolations.BSpline(Cubic(Interpolations.Line(OnGrid()))))
sitp = scale(itp, A_x1, A_x2)
sitp(5., 10.) # exactly log(5 + 10)
sitp(5.6, 7.1) # approximately log(5.6 + 7.1)
GriddedInterpolation
type is useful.A = rand(20)
A_x = collect(1.0:2.0:40.0)
knots = (A_x,)
itp = interpolate(knots, A, Gridded(Linear()))
itp(2.0)
# 2D
A = rand(8,20)
knots = ([x^2 for x = 1:8], [0.2y for y = 1:20])
itp = interpolate(knots, A, Gridded(Linear()))
itp(4,1.2) # approximately A[2,6]
# we can mix modes across dimensions!
itp = interpolate(knots, A, (Gridded(Linear()),Gridded(Constant())))
itp(4,1.2)
using StaticArrays
x = range(1,stop = 3, length = 200) # cash on hand
a = Float64[log(1+j)*i for j in x, i in 1:2] # cons and save function
b = reinterpret(SVector{2,Float64}, a')[:]
itp = interpolate(b, Interpolations.BSpline(Quadratic(Reflect(OnCell()))))
@show itp(3)
sitp = scale(itp,x)
@show sitp(3);
v = sitp(1.75) # get interpolated values for both function
plot(x,a,labels=["save","cons"],yticks=convert(Array{Float64},v),xlabel="current cash")
vline!([1.75],lab="")
hline!([v[1],v[2]],lab="")
\Phi =& \Phi_D \otimes \Phi_{D-1} \otimes \dots \otimes \Phi_{1} \end{aligned} $$
using ApproXD
gr()
bs = ApproXD.BSpline(7,3,0,1) #7 knots, degree 3 in [0,1]
n = 500
eval_points = collect(range(0,stop = 1.0,length = n))
B = Array(ApproXD.getBasis(eval_points,bs))
ys = [string("Basis",i) for i = size(B)[2]-1:-1:0]
xs = reverse(eval_points)
heatmap(xs,ys,reverse(B',dims=1))
heatmap(xs,ys,reverse(B',dims=1) .> 0, cbar = false)
B
by typing B
and typeof(B)
ApproXD.jl
for example viafunction evalTensor2{T}(mat1::SparseMatrixCSC{T,Int64},
mat2::SparseMatrixCSC{T,Int64},
c::Vector{T})
This proceeds as in the previouses cases:
BasisMatrices
¶BasisMatrices
provides general support for all kinds of basis matrices.Cheb
for chebyshev basis matrixSpline
for splineLin
for linearParams
for each type: bounds, grid points, degrees, etcbasis
nodes(basis)
BasisMatrix(basis)
using BasisMatrices
grid1 = range(1,stop = 3,length = 10)
lp = LinParams(grid1)
sp = SplineParams(collect(grid1),0,3) # nodes, whether only 2 nodes, degree of spline
sm = SmolyakParams(2,1,[3],[2]) # dims,mu,lower bound(s), upper bound(s)
sm2 = SmolyakParams(2,[1,2],[0,-0.5],[3,4.4]) # dims,mu,lower bound(s), upper bound(s)
sm3 = SmolyakParams(3,[1,2,2],[0,-0.5,1],[3,4.4,5]) # dims,mu,lower bound(s), upper bound(s)
# simple interpolation
fi(x) = x.^2 - 0.5*x.^3
sp = SplineParams(collect(grid1),0,3) # nodes, whether only 2 nodes, degree of spline
sb = Basis(sp)
s,n = BasisMatrices.nodes(sb)
sv =fi(s)
coef, bs_direct = funfitxy(sb, s, sv) # compute coefficients
plot(fi,1,3,label="truth")
newx = 1 .+ rand(10)*2
newy = funeval(coef,sb,newx) # evaluate basis at new points
scatter!(newx,newy,label="approx")
# multiple dimensions
basis = Basis(SplineParams(25, -1, 1, 2),SplineParams(20, -1, 2, 1)) # quadratic and linear spline
# get nodes
X, x12 = BasisMatrices.nodes(basis)
# function to interpolate
f2(x1, x2) = cos.(x2) ./ exp.(x1)
f2(X::Matrix) = f2.(X[:, 1], X[:, 2])
# f at nodes
y = f2(X)
Ymat = [f2(i,j) for i in x12[1], j in x12[2]]
coefs, bbs = funfitxy(basis, X, y)
ynew = funeval(coefs, basis, X[5:5, :])[1]
using Test
@test maximum(abs, ynew - y[5]) <= 1e-12
# plotlyjs()
surface(x12[1],x12[2],Ymat',alpha=0.7,cbar=false)
xnew=hcat(-1 .+ rand(10)*2 , -1 .+ rand(10)*3)
ynew = [funeval(coefs, basis, xnew[i,:]) for i in 1:size(xnew,1)]
scatter!(xnew[:,1],xnew[:,2],ynew,markersize=2,markershape=:rect)
# same with the smolyak grid
# multiple dimensions
using LinearAlgebra
sp = SmolyakParams(2,3, [-1,-1],[1,2])
basis = Basis(sp)
# get nodes
X, x12 = BasisMatrices.nodes(basis)
# # f at nodes
y = f2(X)
Ymat = [f2(i,j) for i in unique(X[:,1]), j in unique(X[:,2])]
# map domain into unit hypercube
cube = BasisMatrices.dom2cube(X, basis.params[1])
# evaluate basis matrix
eb = BasisMatrices.build_B(sp.d, sp.mu, cube, sp.pinds)
coef = pinv(eb) * y
xnew = hcat(-1 .+ rand(30)*2 , -1 .+ rand(30)*3)
cube = BasisMatrices.dom2cube(xnew, sp)
eb = BasisMatrices.build_B(sp.d, sp.mu, cube, sp.pinds)
ynew = eb * coef
scatter(X[:,1],X[:,2],y,markersize=2)
scatter!(xnew[:,1],xnew[:,2],ynew,markersize=2,markershape=:rect)
using Tasmanian
Tasmanian.ex2()