Borehole NMR processing
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 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. from __future__ import division
  2. import numpy as np
  3. from scipy.sparse.linalg import iterative as iter
  4. import pylab
  5. import pprint
  6. from scipy.optimize import nnls
  7. def PhiB(mux, muy, minVal, maxVal, x):
  8. phib = mux * np.abs( np.sum(np.log( x-minVal)) )
  9. #phib += np.abs( np.log(1. - maxVal / np.sum(x)) )
  10. return phib
  11. #phib += np.log std::log(maxVal - x.segment(ib*block, block).sum());
  12. #template < typename Scalar >
  13. #Scalar PhiB2 (const Scalar& minVal, const Scalar& maxVal, const VectorXr x,
  14. # const int& block, const int &nblocks) {
  15. # Scalar phib = std::abs((x.array() - minVal).log().sum());
  16. # //phib += std::abs((maxVal - x.array()).log().sum()*muy);
  17. # for (int ib=0; ib<nblocks; ++ib) {
  18. # //HiSegments(ib) = x.segment(ib*block, block).sum();
  19. # phib += Scalar(block)*std::log(maxVal - x.segment(ib*block, block).sum());
  20. # }
  21. # return phib;
  22. #}
  23. def logBarrier(A, b, T2Bins, x_0=0, xr=0, alpha=10, mu1=10, mu2=10, smooth=False, MAXITER=70, fignum=1000, sigma=1, callback=None):
  24. """Impliments a log barrier Tikhonov solution to a linear system of equations
  25. Ax = b s.t. x_min < x < x_max. A log-barrier term is used for the constraint
  26. """
  27. # TODO input
  28. minVal = 0.0
  29. maxVal = 1e8
  30. Wd = (np.eye(len(b)) / (sigma)) # Wd = eye( sigma )
  31. WdTWd = (np.eye(len(b)) / (sigma**2)) # Wd = eye( sigma )
  32. #print ("calculating AT WdT.Wd A ") # AT WdTWd A with known data matrix
  33. #print (" A.shape" , np.shape(A), np.shape(b))
  34. ATWdTWdA = np.dot(A.conj().transpose(), np.dot( WdTWd, A )) # TODO, implicit calculation instead?
  35. N = np.shape(A)[1] # number of model
  36. M = np.shape(A)[0] # number of data
  37. SIGMA = .25 #.25 #.125 # .25 #.01#3e-1
  38. EPSILON = 1e-35 # was 35
  39. # reference model
  40. if np.size(xr) == 1:
  41. xr = np.zeros(N)
  42. # initial guess
  43. if np.size(x_0) == 1:
  44. x = 1e-10 + np.zeros(N)
  45. else:
  46. x = 1e-10 + x_0
  47. # Construct model constraint base
  48. #print ("constructing Phim")
  49. Phim_base = np.zeros( [N , N] )
  50. a1 = 1.0 # smallest
  51. # calculate largest term
  52. D1 = 1./abs(T2Bins[1]-T2Bins[0])
  53. D2 = 1./abs(T2Bins[2]-T2Bins[1])
  54. #a2 = 1. #(1./(2.*D1+D2)) # smooth
  55. if smooth == "Both":
  56. #print ("SMOOTH")
  57. # Smooth model
  58. print ("Both small and smooth model")
  59. for ip in range(N):
  60. D1 = 0.
  61. D2 = 0.
  62. DMAX = 2./(T2Bins[1]-T2Bins[0])
  63. if ip > 0:
  64. D1 = 1./abs(T2Bins[ip]-T2Bins[ip-1])
  65. if ip < N-1:
  66. D2 = 1./abs(T2Bins[ip+1]-T2Bins[ip])
  67. if ip > 0:
  68. Phim_base[ip,ip-1] = -(D1) # smooth in log space
  69. if ip == 0:
  70. Phim_base[ip,ip ] = (D1+D2) + a1 # Encourage a little low model, no a1
  71. elif ip == N-1:
  72. Phim_base[ip,ip ] = (D1+D2) + a1 # Penalize long decays
  73. else:
  74. Phim_base[ip,ip ] = (D1+D2) + a1 # Smooth and small
  75. if ip < N-1:
  76. Phim_base[ip,ip+1] = -(D2) # smooth in log space
  77. #Phim_base /= np.max(Phim_base)
  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 ] = 1.0 # Encourage a little low model
  86. elif ip == N-1:
  87. Phim_base[ip,ip ] = 8.0 # Penalize long decays
  88. else:
  89. Phim_base[ip,ip ] = 2.0 # Smooth and small
  90. if ip < N-1:
  91. Phim_base[ip,ip+1] = -1 # smooth in log space
  92. #print(Phim_base)
  93. else:
  94. print ("SMALLEST")
  95. # Smallest model
  96. for ip in range(N):
  97. Phim_base[ip,ip ] = 1.
  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, mu2, 0, 1e8, x)
  105. mu1 = ((phid + alpha*phim) / phib)
  106. #print ("iteration", -1, mu1, mu2, phib, phid, phim, len(b))
  107. for i in range(MAXITER):
  108. b_pre = np.dot(A, x)
  109. phid = np.linalg.norm(np.dot(Wd, (b-b_pre)))**2
  110. # technically phim should not be on WmTWm matrix. So no need to square result.
  111. phim = np.linalg.norm(np.dot(Phim_base, (x-xr)))#**2
  112. phib = PhiB(mu1, mu2, 0, 1e8, x)
  113. Phi_m = alpha*Phim_base
  114. mu1 = ((phid + alpha*phim) / phib)
  115. WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)
  116. #ztilde = x
  117. #print("ztilde", ztilde)
  118. phid_old = phid
  119. inner = 0
  120. First = True
  121. while ( (phib / (phid+alpha*phim)) > EPSILON or First==True ):
  122. First = False
  123. # Log barrier, keep each element above minVal
  124. X1 = np.eye(N) * (x-minVal)**-1
  125. X2 = np.eye(N) * (x-minVal)**-2
  126. # Log barrier, keep sum below maxVal TODO normalize by component. Don't want to push all down
  127. Y1 = np.eye(N) * (maxVal - np.sum(x))**-1
  128. Y2 = np.eye(N) * (maxVal - np.sum(x))**-2
  129. AA = ATWdTWdA + mu1*X2 + mu2*Y2 + Phi_m
  130. M = np.eye( N ) * (1./np.diag(ATWdTWdA + mu1*X2 + mu2*Y2 + Phi_m))
  131. # Solve system (newton step)
  132. b2 = np.dot(A.transpose(), np.dot(WdTWd, b-b_pre) ) + 2.*mu1*np.diag(X1) + 2.*mu2*np.diag(Y1) - alpha*np.dot(WmTWm,(x-xr))
  133. ztilde = iter.cg(AA, b2, M=M) # tol=1e-3*phid, maxiter=200, callback=callback) #, tol=1e-2, maxiter) #, x0=x) #, tol=ttol) #, M=M, x0=x)
  134. h = (ztilde[0])
  135. # Solve system (direct solution)
  136. #b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b)) + 2.*mu1*np.diag(X1) + 2.*mu2*np.diag(Y1) - alpha*np.dot(WmTWm,(x-xr))
  137. #ztilde = iter.cg(AA, b2, x0=x) #, tol=1e-2) #, x0=x) #, tol=ttol) #, M=M, x0=x)
  138. #h = (ztilde[0].real - x)
  139. # step size
  140. d = np.min( (1, 0.95 * np.min(x/np.abs(h+1e-120))) )
  141. ##########################################################
  142. # Update and fix any over/under stepping
  143. x = x+d*h
  144. #x = np.max( ( (minVal+1e-120)*np.ones(N), x+d*h), 0)
  145. # Determine mu steps to take
  146. s1 = mu1 * (np.dot(X2, ztilde[0].real) - 2.*np.diag(X1))
  147. s2 = mu2 * (np.dot(Y2, ztilde[0].real) - 2.*np.diag(Y1))
  148. # determine mu for next step
  149. mu1 = SIGMA/N * np.abs(np.dot(s1, x))
  150. mu2 = SIGMA/N * np.abs(np.dot(s2, x))
  151. b_pre = np.dot(A, x)
  152. phid = np.linalg.norm(np.dot(Wd, (b-b_pre)))**2
  153. phim = np.linalg.norm(np.dot(Phim_base, (x-xr)))#**2
  154. phib = PhiB(mu1, mu2, minVal, maxVal, x)
  155. inner += 1
  156. # determine alpha
  157. scale = (len(b)/phid)
  158. alpha *= scale #**(1/6)
  159. score = np.sqrt(phid/(len(b)+1.)) # unbiased
  160. # check stopping criteria
  161. if score < 1:
  162. print ("*overshot* optimal solution found") #, alpha, score)
  163. break
  164. if score < 1.1: # or np.linalg.norm(x_old-x) < 1e-5 or phid > phid_old:
  165. #print ("overshoot")
  166. #alpha *= 10
  167. print ("optimal solution found") #, alpha, score)
  168. break
  169. if i > 10 and (np.sqrt(phid_old/(len(b)+1.)) - score) < 1e-2: # 1e-2
  170. print ("slow convergence") #, alpha, score, i, scale, score-np.sqrt(phid_old/len(b)))
  171. break
  172. print ( "alpha","phid", "iter", "search", "prior" )
  173. print ( alpha, score, i, scale, np.sqrt(phid_old/(len(b)+1)))
  174. return x