From ed7c34aa7ed9389ede9ebcd1ff8f42ea7a27755e Mon Sep 17 00:00:00 2001 From: Xiao-Yong Jin Date: Fri, 20 Dec 2024 17:46:49 -0600 Subject: [PATCH] add cspline, jackknife resample, and test utils --- src/algorithms/cspline.nim | 295 +++++++++++++++++++++++++++++++++++++ src/base/qexInternal.nim | 6 +- src/utils/resample.nim | 150 +++++++++++++++++++ src/utils/test.nim | 120 +++++++++++++++ 4 files changed, 569 insertions(+), 2 deletions(-) create mode 100644 src/algorithms/cspline.nim create mode 100644 src/utils/resample.nim create mode 100644 src/utils/test.nim diff --git a/src/algorithms/cspline.nim b/src/algorithms/cspline.nim new file mode 100644 index 0000000..d378fcd --- /dev/null +++ b/src/algorithms/cspline.nim @@ -0,0 +1,295 @@ +## cubic spline and related routines + +import std/algorithm +import base + +proc estimateDerivative*[N:static[int],T](dx,dy:array[N,T]):T = + ## estimate the derivative given a list of dx and dy, using Taylor series + ## dx and dy must be the difference away from a single point. + when N==1: + return dy[0]/dx[0] + elif N==2: + let h10 = dx[1]-dx[0] + return dy[0]*dx[1]/(dx[0]*h10) - dx[0]*dy[1]/(h10*dx[1]) + elif N==3: + let + h10 = dx[1]-dx[0] + h02 = dx[0]-dx[2] + h21 = dx[2]-dx[1] + a = dy[0]*dx[1]*dx[2]/(dx[0]*h10*h02) + b = dx[0]*dy[1]*dx[2]/(dx[1]*h10*h21) + c = dx[0]*dx[1]*dy[2]/(dx[2]*h02*h21) + return -(a+b+c) + else: + error("estimateDerivative: Unimplemented for N = " & $N) + +type CSpline*[T] = object + x: seq[T] + ys: seq[array[2,T]] ## y and computed second derivatives of y + +type + CSplineBoundDyKind = enum + CSBEstimateDy, CSBZeroD2y, CSBSetDy + CSplineBoundDy = object + case kind: CSplineBoundDyKind + of CSBEstimateDy, CSBZeroD2y: + discard + of CSBSetDy: + dy: float + CSplineBounds = object + lo,hi: CSplineBoundDy + +const CSplineBoundEstimateDy* = CSplineBoundDy(kind:CSBEstimateDy) +const CSplineBoundZeroD2y* = CSplineBoundDy(kind:CSBZeroD2y) +converter toCSplineBoundDy*(dy:float):CSplineBoundDy = CSplineBoundDy(kind:CSBSetDy,dy:dy) + +proc csplineBounds*(lo=CSplineBoundEstimateDy, hi=CSplineBoundEstimateDy):CSplineBounds = + CSplineBounds(lo:lo, hi:hi) + +proc newCSpline*[T](x,y:openarray[T], bounds=csplineBounds()):CSpline[T] = + let n = x.len + if y.len != n: + qexError "different length in x and y: ",n," != ",y.len + var r = CSpline[T](x:newseq[T](n), ys:newseq[array[2,T]](n)) + for i in 0.. int: + cmp(a[1], b[1]) # sort by x + for i in 0..3: estimateDerivative([x(1)-x(0), x(2)-x(0), x(3)-x(0)], [y(1)-y(0), y(2)-y(0), y(3)-y(0)]) + elif n==3: estimateDerivative([x(1)-x(0), x(2)-x(0)], [y(1)-y(0), y(2)-y(0)]) + elif n==2: estimateDerivative([x(1)-x(0)], [y(1)-y(0)]) + else: T(0.0) + else: + bounds.lo.dy + d = y(1)-y(0) + h = x(1)-x(0) + d2y(0) = T(3.0)*(d/h-dy)/h + g[0] = T(0.5) + + # solve the tridiagonal system + for j in 1..3: estimateDerivative([x(n-2)-x(n-1), x(n-3)-x(n-1), x(n-4)-x(n-1)], [y(n-2)-y(n-1), y(n-3)-y(n-1), y(n-4)-y(n-1)]) + elif n==3: estimateDerivative([x(n-2)-x(n-1), x(n-3)-x(n-1)], [y(n-2)-y(n-1), y(n-3)-y(n-1)]) + elif n==2: estimateDerivative([x(n-2)-x(n-1)], [y(n-2)-y(n-1)]) + else: T(0.0) + else: + bounds.hi.dy + d = y(n-1)-y(n-2) + h = x(n-1)-x(n-2) + d2y(n-1) = (T(6.0)*(dy-d/h)/h-d2y(n-2))/(T(2.0)-g[n-2]) + + # back substitute + for j in countdown(n-2,0): + d2y(j) -= g[j]*d2y(j+1) + #echo "back: d2y(",j,") ",d2y(j) + + return r + +func bisect*[T](xs:openarray[T], x:T): int = + ## assuming ascending xs + ## no boundary check + var + tot = xs.len + mid = tot div 2 + while tot>1: + if x