From 2d7d3c96fcf245403070bed6223f72d82dc7a319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20F=C3=A4lt?= <mattiasf@control.lth.se> Date: Tue, 1 Dec 2015 18:09:07 +0100 Subject: [PATCH] Initial SQMC commit with own PF --- src/hilbert_curve.jl | 62 ++++++++++++ src/particle_filters/SQMC_test.jl | 161 ++++++++++++++++++++++++++++++ src/particle_filters/pf_SQMC.jl | 108 ++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 src/hilbert_curve.jl create mode 100644 src/particle_filters/SQMC_test.jl create mode 100644 src/particle_filters/pf_SQMC.jl diff --git a/src/hilbert_curve.jl b/src/hilbert_curve.jl new file mode 100644 index 0000000..bde607a --- /dev/null +++ b/src/hilbert_curve.jl @@ -0,0 +1,62 @@ +#Convert number in [0,1)^2 to [0,1) +function xy2d(x::Float64, y:: Float64) + N = 2^30 + xint = floor(Int64, x*N) + yint = floor(Int64, y*N) + # d will be in [0,N^2) + d = xy2d(N, xint, yint)/N^2 +end + +function d2xy(d:: Float64) + N::Int64 = 2^30 + dint = floor(Int64, d*N^2) + x::Float64, y::Float64 = d2xy(N^2, dint) + x/N, y/N +end + +#Converted from wikipedia +#convert (x,y) to d +function xy2d(n::Integer, x::Integer, y::Integer) + rx, ry, s, d = 0, 0, 0, 0 + s = div(n,2) + while s>0 + rx = (x & s) > 0 + ry = (y & s) > 0 + d += s * s * ((3*rx) $ ry) + x, y = rot(s, x, y, rx, ry) + s = div(s,2) + end + return d::Integer +end + +#convert d to (x,y) +function d2xy(n::Integer, d::Integer) + rx, ry, s, t = 0, 0, 0, d + x, y = 0, 0 + s = 1 + while s<n + rx = 1 & (div(t,2)) + ry = 1 & (t $ rx) + x, y = rot(s, x, y, rx, ry) + x += s*rx + y += s*ry + t = div(t,4) + s *= 2 + end + return x::Integer, y::Integer +end + +#rotate/flip a quadrant appropriately +function rot(n::Integer, x::Integer, y::Integer, rx::Integer, ry::Integer) + if ry == 0 + if rx == 1 + x = n-1 - x + y = n-1 - y + end + #Swap x and y + t = x + x = y + y = t + end + return x::Integer, y::Integer +end diff --git a/src/particle_filters/SQMC_test.jl b/src/particle_filters/SQMC_test.jl new file mode 100644 index 0000000..e1597a6 --- /dev/null +++ b/src/particle_filters/SQMC_test.jl @@ -0,0 +1,161 @@ +using Gadfly +using Colors + +function GordonKitagawaUpdate!(x,t, u, xtemp, i) + b₁, b₂, b₃, b₄, σ = .5, 25, 8, 1.2, sqrt(1); + xtemp[1,i] = b₁*x+b₂*x/(t+x^2)+b₃*cos(b₄*t)+σ*erfinv(u[1]*2-1); +end + +function GordonKitagawaOut(x) + σ = sqrt(.1); + a = 20 + x^2/a+σ*randn()[1] +end + +function GordonKitagawaOutNoNoise(x) + a = 20 + x.^2./a +end + +function GordonKitagawapxy(yhat,y) + σ = sqrt(.1); + #w = 1/sqrt(2π)*e^(-(yhat[1]-y[1])^2/2) + w = -(yhat[1]-y[1])^2/(2*σ^2) +end + + + +function generateRealSequence(f!, g, x0, T, N = length(x0)) + x = Array{Float64,2}(length(x0), T) + f!(x0, 1, rand(N),x,1) + y1 = g(x[:,1],1) + y = Array{Float64,2}(length(y1), T) + y[:,1] = y1 + for t = 2:T + f!(x[t-1], t, rand(N),x,t) + y[:,t] = g(x[t], t) + end + x, y +end + +function estMean(x,w,t) + sum(x[:].*exp(w[:])) +end + +function runTest(N, method, debug) + T = 150 + + ##Gordon Kitagawa + #f! = (x,t,u,xtemp,i) -> GordonKitagawaUpdate!(x[1],t,u,xtemp,i) + #g = (x,t) -> GordonKitagawaOutNoNoise(x[1]) + #gn = (x,t) -> GordonKitagawaOut(x[1]) + #ginv = (yhat, y,t) -> GordonKitagawapxy(yhat,y) + + ##LTI System + σ = .5 + f! = (x,t,u,xtemp,i) -> xtemp[1,i] = .8x[1]+4*erfinv(u[1]*2-1) + g = (x,t) -> 2*x[1] + gn = (x,t) -> 2*x[1] + σ*randn()[1] + ginv = (yhat, y, t) -> -(yhat[1]-y[1])^2/(2*σ^2) + + ##Estimator + #est = (x,w,t) -> x[findmax(w)[2]] + est = (x,w,t) -> sum(x[:].*exp(w[:])) + + x0 = .5 + x, y = generateRealSequence(f!, gn, x0, T) + xhat = method(f!, g, ginv, x0, y, est, N, debug=debug, xreal=x) + x, xhat +end + + + +function plotPoints(x, w, y, N, a, τ, t, xreal, xhat) + c = w[:]-minimum(w)+1 + ##Use for GordonK + #p = plot(layer(x=collect(1:N), y=x[:], Geom.point, color=c), + # layer(x=[1,N],y=ones(2).*sqrt(20*max(y[:,t],0)),Geom.line, Theme(default_color=color(colorant"red"))), + # layer(x=[1,N],y=-ones(2).*sqrt(20*max(y[:,t],0)),Geom.line, Theme(default_color=color(colorant"red"))), + # layer(x=[1,N],y=ones(2).*xreal[:,t],Geom.line, Theme(default_color=color(colorant"blue"),line_width=2px)), + # layer(x=[1,N],y=ones(2).*xhat[:,t],Geom.line, Theme(default_color=color(colorant"black"),line_width=4px)), + # Guide.XLabel("Particle "*string(t)), Guide.YLabel("Estimate"), Coord.Cartesian(ymin=-15,ymax=15)) + ##Use for LTI + p = plot(layer(x=collect(1:N), y=x[:], Geom.point, color=c), + layer(x=[1,N],y=ones(2)*1/2.*y[:,t],Geom.line, Theme(default_color=color(colorant"red"))), + layer(x=[1,N],y=ones(2).*xreal[:,t],Geom.line, Theme(default_color=color(colorant"blue"),line_width=2px)), + layer(x=[1,N],y=ones(2).*xhat[:,t],Geom.line, Theme(default_color=color(colorant"black"),line_width=4px)), + Guide.XLabel("Particle "*string(t)), Guide.YLabel("Estimate"), Coord.Cartesian(ymin=-10,ymax=10)) + + display(p) + print("here") + readline(STDIN) +end + + +function rms(x) + sqrt(1/length(x)*sum(x.^2)) +end + +function testSQMC() + debug = false + Ns = 2.^(2:11) + M = 50 + RMS = Array{Float64,2}(length(Ns),M) + largeRMS = Array{Float64}(length(Ns),2) + rmsMean = Array{Float64,2}(length(Ns),2) + rmsVariance = Array{Float64,2}(length(Ns),2) + for (methodidx,method) in enumerate([SQMC,pf]) + xhat, xreal = 0, 0 + @time for (i, N) in enumerate(Ns) + rmslocal = Array{Float64,1}(M) + for j = 1:M + xreal, xhat = runTest(N,method,debug) + RMS[i,j] = rms(xreal-xhat) + end + end + rmsMean[:,methodidx] = mean(RMS,2) + rmsVariance[:,methodidx] = std(RMS,2)./sqrt(M) + for (i, N) in enumerate(Ns) + largeRMS[i,methodidx] = length(find(RMS[i,:].>rmsMean[i,methodidx]+2*rmsVariance[i,methodidx])) + end + end + rmsMean, rmsVariance, Ns, largeRMS, RMS +end + +function testPlot(rmsMean, rmsVariance, Ns) + p = plot( + layer(x=Ns,y=rmsMean[:,1],Geom.line,Theme(default_color=color(colorant"red"))), + layer(x=Ns,y=rmsMean[:,2],Geom.line,Theme(default_color=color(colorant"blue"))), + layer(x=Ns,y=rmsMean[:,1]+rmsVariance[:,1]*2,Geom.line,Theme(default_color=color(colorant"red"))), + layer(x=Ns,y=rmsMean[:,1]-rmsVariance[:,1]*2,Geom.line,Theme(default_color=color(colorant"red"))), + layer(x=Ns,y=rmsMean[:,2]+rmsVariance[:,2]*2,Geom.line,Theme(default_color=color(colorant"blue"))), + layer(x=Ns,y=rmsMean[:,2]-rmsVariance[:,2]*2,Geom.line,Theme(default_color=color(colorant"blue"))), + ) +end + + +function testPlot(N) + for j = 1:N + M = 1000 + vals = Array{Float64,2}(M,2) + ds = linspace(rand()/M,1,M)[1:end] + for i = 1:M + o = d2xy(ds[i]) + vals[i,:] = [o[1], o[2]] + end + plot(vals[:,1], vals[:,2]) + end +end + +function testPlot2(N) + for j = 1:N + M = 1000 + vals = Array{Float64,2}(M,2) + ds = sort(rand(M)) + for i = 1:M + o = d2xy(ds[i]) + vals[i,:] = [o[1], o[2]] + end + plot(vals[:,1], vals[:,2]) + end +end diff --git a/src/particle_filters/pf_SQMC.jl b/src/particle_filters/pf_SQMC.jl new file mode 100644 index 0000000..0fd7940 --- /dev/null +++ b/src/particle_filters/pf_SQMC.jl @@ -0,0 +1,108 @@ +function resample(s, w) + # Samples new particles based on their weights. If you find algorithmic optimizations to this routine, please tell me /Bagge) + N = length(w) + bins = [0.; cumsum(exp(w))] + j = zeros(Int64,N) + bo = 1 + for i = 1:N + for b = bo:N + if bins[b] <= s[i]# < bins[b+1] + j[i] = b + bo = b + break + end + end + end + return j +end + +function resample(w) + bins = [0.; cumsum(exp(w))] + s = collect((rand()/N+0):1/N:bins[end]) + resample(s,w) +end + +@doc """ `xhat = SQMC(f, g, ginv, x0, y, est, N=200, Nu=lenth(x0))` + + Gives estimation of the states `x(:,1),...x(:,T)` given outputs `y(:,1)...y(:,T)` from system: + + x(t) = f(x(t-1),t,u) \n + y = gn(x,v),\n + where `g(x,t)=E[gn(x,t,u)]`, `ginv(yhat, y, t) = p(y=gn(x,u)|yhat=g(x))`, + u is uniform noise on `[0,1)`, with `E[x(0)] = xhat0`, + and some estimator `est(xₚ,w,t)` that ouputs the estimate `xhat(:,t)` + given the particles `xₚ` and weights `w` + """ -> +function SQMC(f!, g, ginv, xhat0, y, est, N=200, Nu = length(xhat0); debug = false, xreal=xreal) + T = size(y,2) + #Estimates at time 1:T + xhat = Array{Float64,2}(Nu, T) + #Current predicted output for each particle + yhat = Array{Float64,2}(size(y,1), N) + x = Array{Float64,2}(length(xhat0), N) + xtemp = similar(x) + w = Array{Float64,1}(N) + #Step (a) (Draw u and x), OBS u will be of size NxNu + u, = sobol(Nu,N) + x[:,1] = xhat0; #Hack to use the pfStep fuction. Works by letting `a` be ones + pfStep!(f!,g,ginv,x,xtemp,u,ones(Int,N),1:N,1,w,yhat,y,N, 1) + xhat[:,1] = est(x,w,1) + u, nextseed, MeM = sobol(Nu+1,N) + for t = 2:T + nextseed = sobol!(u, nextseed, MeM) + τ = sortperm(u[:,1]) + σ = sortperm(ψ(x)[:]) + a = resample(u[τ,1], w[σ]) + # Time update, no sigma here? + pfStep!(f!,g,ginv,x,xtemp,u,a,τ,t,w,yhat,y,N, 2:Nu+1) + xhat[:,t] = est(x,w,1) + if debug + plotPoints(x, w, y, N, a, τ, t, xreal, xhat) + end + end + xhat +end + + +function ψ(x) + x +end + +function pf(f!, g, ginv, xhat0, y, est, N=200, Nu = length(xhat0); debug=false, xreal=xreal) + T = size(y,2) + #Estimates at time 1:T + xhat = Array{Float64,2}(Nu, T) + #Current predicted output for each particle + yhat = Array{Float64,2}(size(y,1), N) + x = Array{Float64,2}(length(xhat0), N) + xtemp = similar(x) + w = Array{Float64,1}(N) + τ = 1:N + u = rand(N,Nu) + x[:,1] = xhat0; #Hack to use the pfStep fuction. Works by letting `a` be ones + pfStep!(f!,g,ginv,x,xtemp,u,ones(Int,N),τ,1,w,yhat,y,N, 1:Nu) + xhat[:,1] = est(x,w,1) + for t = 2:T + u = rand(N,Nu) + a = resample(u[τ,1], w[:]) + # Time update, no sigma here? + pfStep!(f!,g,ginv,x,xtemp,u,a,τ,t,w,yhat,y,N,1:Nu) + xhat[:,t] = est(x,w,1) + end + xhat +end + +#Time update and weighting. Chances `x`, `yhat` and `w` +@inbounds function pfStep!(f,g,ginv,x,xtemp,u,a,τ,t,w,yhat,y,N,uRange) + for i = 1:N + f(x[:,a[i]], t, u[τ[i], uRange], xtemp, i) + yhat[:,i] = g(xtemp[:,i], t) + w[i] = ginv(yhat[:,i], y[:,t], t) + end + x[:] = copy(xtemp) + + offset = maximum(w) + mySum = sum(exp(w-offset)) + normConstant = log(mySum)+offset + w[:] = w[:] - normConstant +end \ No newline at end of file -- GitLab