Surface NMR processing and inversion GUI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

logbarrier.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. from __future__ import division
  2. import numpy as np
  3. from scipy.sparse.linalg import iterative as iter
  4. from scipy.sparse import eye as seye
  5. import pylab
  6. import pprint
  7. from scipy.optimize import nnls
  8. import matplotlib.pyplot as plt
  9. from akvo.tressel.SlidesPlot import *
  10. def PhiB(mux, minVal, x):
  11. phib = mux * np.abs( np.sum(np.log( x-minVal)) )
  12. return phib
  13. def curvaturefd(x, y, t):
  14. x1 = np.gradient(x,t)
  15. x2 = np.gradient(x1,t)
  16. y1 = np.gradient(y,t)
  17. y2 = np.gradient(y1,t)
  18. return np.abs(x1*y2 - y1*x2) / np.power(x1**2 + y1**2, 3./2)
  19. def curvatureg(x, y):
  20. from scipy.ndimage import gaussian_filter1d
  21. #first and second derivative
  22. x1 = gaussian_filter1d(x, sigma=1, order=1)#, mode='constant', cval=x[-1])
  23. x2 = gaussian_filter1d(x1, sigma=1, order=1)#, mode='constant', cval=y[-1])
  24. y1 = gaussian_filter1d(y, sigma=1, order=1)#, mode='constant', cval=x1[-1])
  25. y2 = gaussian_filter1d(y1, sigma=1, order=1)#, mode='constant', cval=y1[-1])
  26. return np.abs(x1*y2 - y1*x2) / np.power(x1**2 + y1**2, 3./2)
  27. def logBarrier(A, b, T2Bins, lambdastar, x_0=0, xr=0, alpha=10, mu1=10, mu2=10, smooth=False, MAXITER=70, fignum=1000, sigma=1, callback=None):
  28. """Impliments a log barrier Tikhonov solution to a linear system of equations
  29. Ax = b s.t. x_min < x < x_max. A log-barrier term is used for the constraint
  30. """
  31. # TODO input
  32. minVal = 0.0
  33. #maxVal = 1e8
  34. Wd = (np.eye(len(b)) / (sigma)) # Wd = eye( sigma )
  35. WdTWd = (np.eye(len(b)) / (sigma**2)) # Wd = eye( sigma )
  36. ATWdTWdA = np.dot(A.conj().transpose(), np.dot( WdTWd, A )) # TODO, implicit calculation instead?
  37. N = np.shape(A)[1] # number of model
  38. M = np.shape(A)[0] # number of data
  39. SIGMA = .25 # .25 # lower is more aggresive relaxation of log barrier
  40. EPSILON = 1e-25 #1e-35
  41. # reference model
  42. if np.size(xr) == 1:
  43. xr = np.zeros(N)
  44. # initial guess
  45. if np.size(x_0) == 1:
  46. x = 1e-10 + np.zeros(N)
  47. else:
  48. x = 1e-10 + x_0
  49. # Construct model constraint base
  50. Phim_base = np.zeros( [N , N] )
  51. a1 = .05 # smallest too
  52. # calculate largest term
  53. D1 = 1./abs(T2Bins[1]-T2Bins[0])
  54. D2 = 1./abs(T2Bins[2]-T2Bins[1])
  55. #a2 = 1. #(1./(2.*D1+D2)) # smooth
  56. if smooth == "Both":
  57. #print ("Both small and smooth model")
  58. for ip in range(N):
  59. D1 = 0.
  60. D2 = 0.
  61. if ip > 0:
  62. #D1 = np.sqrt(1./abs(T2Bins[ip]-T2Bins[ip-1]))**.5
  63. D1 = (1./abs(T2Bins[ip]-T2Bins[ip-1])) #**2
  64. if ip < N-1:
  65. #D2 = np.sqrt(1./abs(T2Bins[ip+1]-T2Bins[ip]))**.5
  66. D2 = (1./abs(T2Bins[ip+1]-T2Bins[ip])) #**2
  67. if ip > 0:
  68. Phim_base[ip,ip-1] = -(D1)
  69. if ip == 0:
  70. Phim_base[ip,ip ] = 2.*(D1+D2)
  71. elif ip == N-1:
  72. Phim_base[ip,ip ] = 2.*(D1+D2)
  73. else:
  74. Phim_base[ip,ip ] = 2.*(D1+D2)
  75. if ip < N-1:
  76. Phim_base[ip,ip+1] = -(D2)
  77. Phim_base /= np.max(Phim_base) # normalize
  78. Phim_base += a1*np.eye(N)
  79. elif smooth == "Smooth":
  80. #print ("Smooth model")
  81. for ip in range(N):
  82. if ip > 0:
  83. Phim_base[ip,ip-1] = -1 # smooth in log space
  84. if ip == 0:
  85. Phim_base[ip,ip ] = 2.05 # Encourage a little low model
  86. elif ip == N-1:
  87. Phim_base[ip,ip ] = 2.5 # Penalize long decays
  88. else:
  89. Phim_base[ip,ip ] = 2.1 # Smooth and small
  90. if ip < N-1:
  91. Phim_base[ip,ip+1] = -1 # smooth in log space
  92. elif smooth == "Smallest":
  93. for ip in range(N):
  94. Phim_base[ip,ip ] = 1.
  95. else:
  96. print("non valid model constraint:", smooth)
  97. exit()
  98. Phi_m = alpha*Phim_base
  99. WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)
  100. b_pre = np.dot(A, x)
  101. phid = np.linalg.norm( np.dot(Wd, (b-b_pre)) )**2
  102. phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
  103. mu2 = phim
  104. phib = PhiB(mu1, 0, x)
  105. mu1 = ((phid + alpha*phim) / phib)
  106. PHIM = []
  107. PHID = []
  108. MOD = []
  109. ALPHA = []
  110. ALPHA.append(alpha)
  111. #ALPHA = np.linspace( alpha, 1, MAXITER )
  112. for i in range(MAXITER):
  113. #alpha = ALPHA[i]
  114. Phi_m = alpha*Phim_base
  115. # reset mu1 at each iteration
  116. # Calvetti -> No ; Li -> Yes
  117. # without this, non monotonic convergence occurs...which is OK if you really trust your noise
  118. mu1 = ((phid + alpha*phim) / phib)
  119. WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)
  120. phid_old = phid
  121. inner = 0
  122. First = True # guarantee entry
  123. xp = np.copy(x) # prior step x
  124. while ( (phib / (phid+alpha*phim)) > EPSILON or First==True ):
  125. #while ( First==True ):
  126. First = False
  127. # Log barrier, keep each element above minVal
  128. X1 = np.eye(N) * (x-minVal)**-1
  129. X2 = np.eye(N) * (x-minVal)**-2
  130. # Log barrier, keep sum below maxVal TODO normalize by component. Don't want to push all down
  131. #Y1 = np.eye(N) * (maxVal - np.sum(x))**-1
  132. #Y2 = np.eye(N) * (maxVal - np.sum(x))**-2
  133. AA = ATWdTWdA + mu1*X2 + Phi_m
  134. M = np.eye( N ) * (1./np.diag(ATWdTWdA + mu1*X2 + Phi_m))
  135. #M = seye( N ).dot(1./np.diag(ATWdTWdA + mu1*X2 + Phi_m))
  136. # Solve system (newton step) (Li)
  137. b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b-b_pre) ) + 2.*mu1*np.diag(X1) - alpha*np.dot(WmTWm,(x-xr))
  138. ztilde = iter.cg(AA, b2, M = M)
  139. h = (ztilde[0].real)
  140. # Solve system (direct solution) (Calvetti)
  141. #b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b)) + 2.*mu1*np.diag(X1) - alpha*np.dot(WmTWm,(x-xr))
  142. #ztilde = iter.cg(AA, b2, M=M, x0=x)
  143. #h = (ztilde[0].real - x)
  144. # step size
  145. d = np.min( (1, 0.95 * np.min(x/np.abs(h+1e-120))) )
  146. ##########################################################
  147. # Update and fix any over/under stepping
  148. x += d*h
  149. # Determine mu steps to take
  150. s1 = mu1 * (np.dot(X2, ztilde[0].real) - 2.*np.diag(X1))
  151. #s2 = mu2 * (np.dot(Y2, ztilde[0].real) - 2.*np.diag(Y1))
  152. # determine mu for next step
  153. mu1 = SIGMA/N * np.abs(np.dot(s1, x))
  154. #mu2 = SIGMA/N * np.abs(np.dot(s2, x))
  155. b_pre = np.dot(A, x)
  156. phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
  157. phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
  158. phib = PhiB(mu1, minVal, x)
  159. inner += 1
  160. PHIM.append(phim)
  161. PHID.append(phid)
  162. MOD.append(np.copy(x))
  163. # determine alpha
  164. scale = 1.5*(len(b)/phid)
  165. #alpha *= np.sqrt(scale)
  166. alpha *= min(scale, .95) # was .85...
  167. #print("alpha", min(scale, 0.99))
  168. #alpha *= .99 # was .85...
  169. ALPHA.append(alpha)
  170. #alpha = ALPHA[i+1]
  171. print("inversion progress", i, alpha, np.sqrt(phid/len(b)), phim, flush=True)
  172. # if np.sqrt(phid/len(b)) < 0.97:
  173. # ibreak = -1
  174. # print ("------------overshot--------------------", alpha, np.sqrt(phid/len(b)), ibreak)
  175. # alpha *= 2. #0
  176. # x -= d*h
  177. # b_pre = np.dot(A, x)
  178. # phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
  179. # phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )#**2
  180. # mu1 = ((phid + alpha*phim) / phib)
  181. if lambdastar == "discrepency":
  182. if np.sqrt(phid/len(b)) < 1.00 or alpha < 1e-5:
  183. ibreak = 1
  184. print ("optimal solution found", alpha, np.sqrt(phid/len(b)), ibreak)
  185. break
  186. # slow convergence, bail and use L-curve
  187. # TI- only use L-curve. Otherwise results for perlin noise are too spurious for paper.
  188. if lambdastar == "lcurve":
  189. if i > 4:
  190. kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:i+1])#ALPHA[0:-1])
  191. #kappa = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
  192. print("max kappa", np.argmax(kappa), "distance from", i-np.argmax(kappa))
  193. if i > 4 and (i-np.argmax(kappa)) > 4: # ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-4) :
  194. #if np.sqrt(phid/len(b)) < 3.0 and ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-3):
  195. ibreak = 1
  196. MOD = np.array(MOD)
  197. print ("###########################") #slow convergence", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
  198. print ("Using L-curve criteria")
  199. #kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
  200. #kappa2 = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
  201. #kappa = curvature( np.array(PHIM), np.array(PHID))
  202. x = MOD[ np.argmax(kappa) ]
  203. b_pre = np.dot(A, x)
  204. phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
  205. phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
  206. mu1 = ((phid + alpha*phim) / phib)
  207. print ("L-curve selected", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
  208. print ("###########################")
  209. if np.sqrt(phid/len(b)) <= 1:
  210. ibreak=0
  211. fig = plt.figure( figsize=(pc2in(20.0),pc2in(22.)) )
  212. ax1 = fig.add_axes( [.2,.15,.6,.7] )
  213. #plt.plot( (np.array(PHIM)), np.log(np.array(PHID)/len(b)), '.-')
  214. #plt.plot( ((np.array(PHIM))[np.argmax(kappa)]) , np.log( (np.array(PHID)/len(b))[np.argmax(kappa)] ), '.', markersize=12)
  215. #plt.axhline()
  216. lns1 = plt.plot( np.log(np.array(PHIM)), np.log(np.sqrt(np.array(PHID)/len(b))), '.-', label="L curve")
  217. lns2 = plt.plot( np.log(np.array(PHIM))[np.argmax(kappa)], np.log(np.sqrt(np.array(PHID)/len(b))[np.argmax(kappa)]), '.', markersize=12, label="$\lambda^*$")
  218. ax2 = plt.twinx()
  219. lns3 = ax2.plot( np.log(np.array(PHIM)), kappa, color='orange', label="curvature" )
  220. # Single legend
  221. lns = lns1+lns3
  222. labs = [l.get_label() for l in lns]
  223. ax2.legend(lns, labs, loc=0)
  224. ax1.set_xlabel("$\phi_m$")
  225. ax1.set_ylabel("$\phi_d$")
  226. ax2.set_ylabel("curvature")
  227. plt.savefig('lcurve.pdf')
  228. break
  229. PHIM = np.array(PHIM)
  230. PHID = np.array(PHID)
  231. if (i == MAXITER-1 ):
  232. ibreak = 2
  233. print("Reached max iterations!!", alpha, np.sqrt(phid/len(b)), ibreak)
  234. kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
  235. x = MOD[ np.argmax(kappa) ]
  236. b_pre = np.dot(A, x)
  237. phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
  238. phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
  239. mu1 = ((phid + alpha*phim) / phib)
  240. if lambdastar == "lcurve":
  241. return x, ibreak, np.sqrt(phid/len(b)), PHIM, PHID/len(b), np.argmax(kappa)
  242. else:
  243. return x, ibreak, np.sqrt(phid/len(b))
  244. if __name__ == "__main__":
  245. print("Test")