method-of-moments-Legendre-final.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #!/usr/bin/env python3
  2. # -*- coding: UTF-8 -*-
  3. #from scipy.special import gamma, binom
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. import scipy.sparse as sparse
  7. from scipy.sparse.linalg import eigsh
  8. from numpy.linalg import eig
  9. from mpmath import mp, mpf
  10. mp.dps = 2000
  11. voxel_num = 5
  12. phase_range = np.pi/2 *0.0
  13. phase_init = np.pi/2 #(0.0)
  14. U_points = voxel_num * 3000
  15. noise_ratio = 0.0*(1.2e-16) #(1.2e-16) #1e8
  16. total_periods=10#DO NOT CHANGE to fit T2s_scale autoscale
  17. rf_samples_per_period = 20
  18. # max polynomial order equals rf_samples_per_period * total_periods
  19. # B0=1.5T freq=64Mhz, period = 15.6 ns
  20. period = (10.0) #ms
  21. omega = 2.0*np.pi/period
  22. #T2s_scale = 0.01 #ms # need to be 10ms
  23. T2s_scale = total_periods*period/rf_samples_per_period/voxel_num*8 #ms # need to be 10ms
  24. T2s_min = T2s_scale/20.0
  25. #ms
  26. total_steps = rf_samples_per_period*total_periods
  27. if total_steps % 2 != 0: total_steps -= 1
  28. time_steps = np.array(mp.linspace((0), (period*total_periods), total_steps))
  29. tmp = [np.random.rand()*0.0+(1.0) for n in range(voxel_num)]
  30. voxel_amplitudes = np.array(tmp)
  31. tmp = [np.random.rand() for n in range(voxel_num)]
  32. voxel_T2s_decay = np.sort(np.array(tmp))*(T2s_scale-2*T2s_min) + T2s_min
  33. voxel_all = np.append(voxel_amplitudes,voxel_T2s_decay/T2s_scale)
  34. if voxel_num == 5:
  35. # voxel_all = np.array([mpf(0.2),(0.6),(0.02),(0.1)])
  36. voxel_all = np.array([(0.822628),(0.691376),(0.282906),(0.226013),(0.90703),(0.144985),(0.228563),(0.340353),(0.462462),(0.720518)])
  37. #voxel_all = np.array([(0.592606),(0.135168),(0.365712),(0.667536),(0.437378),(0.918822),(0.943879),(0.590338),(0.685997),(0.658839)])
  38. voxel_amplitudes = voxel_all[:voxel_num]
  39. voxel_T2s_decay = voxel_all[voxel_num:]*T2s_scale
  40. # a_i = np.array([(0.3),(0.1),(0.15),(0.1)])
  41. # d_i = np.array([(0.7),(0.9),(0.2),(0.67)])
  42. # voxel_num = len(a_i)
  43. voxel_phases = np.array(np.linspace(0,phase_range, voxel_num))
  44. # if len(voxel_amplitudes) != len(voxel_phases):
  45. # print("ERROR! Size of amplitude and phase arrays do not match!")
  46. # raise
  47. ampl = []
  48. def gen_rf_signal(time):
  49. '''Generates demodulated signal at radio frequence using voxels
  50. amplitudes, T2s decays, and phases.
  51. '''
  52. tmp = [mpf(0.0) for n in range(len(time))]
  53. mag_sin = np.array(tmp)
  54. mag_cos = np.array(tmp)
  55. for t in range(len(time)):
  56. for i in range(voxel_num):
  57. # mag_sin[t] += a_i[i]*(
  58. # (d_i[i] + np.random.rand()*noise_ratio)**time[t]
  59. # )
  60. amp = voxel_amplitudes[i] * (
  61. mp.exp(-time[t]/voxel_T2s_decay[i])
  62. ) + ( 0.0
  63. # + np.random.rand()*noise_ratio
  64. )
  65. # if t == 0:
  66. #print("a_{:d}".format(i),float(voxel_amplitudes[i]* np.sin(voxel_phases[i] + phase_init)))
  67. #print("d_{:d}".format(i),float( np.exp(-(period/rf_samples_per_period)/voxel_T2s_decay[i]) ))
  68. mag_sin[t] += amp * np.sin((voxel_phases[i] + phase_init))
  69. mag_cos[t] += amp * np.cos((voxel_phases[i] + phase_init))
  70. mag_sin[t] *= (1 + (np.random.rand()-0.5)*noise_ratio)
  71. #mag_cos[t] += np.random.rand()*noise_ratio
  72. return mag_sin, mag_cos
  73. def factorial(n):
  74. return mp.gamma(n+1)
  75. def binom(n,k):
  76. return factorial(n)/(factorial(k)*factorial(n-k))
  77. def K ( i, j):
  78. polyL = L[i] #precomputed shiftedLegendre(i)
  79. return polyL.coeffs[-j-1]
  80. def GetU (lambdas):
  81. n_max = len(lambdas)
  82. P = np.ones((n_max+1,U_points))
  83. x = np.linspace(0,1, U_points)
  84. P[1] = 2*x-1
  85. for n in range(1,n_max):
  86. P[n+1] = ((2*n+1)/(n+1)) * (2*x-1) * P[n] - (n/(n+1))*P[n-1]
  87. U = np.zeros(U_points)
  88. for i in range (len(lambdas)):
  89. # if i%10 == 0: print(i)
  90. U += lambdas[i]*P[i]*np.sqrt(2*i+1)
  91. #polyL = L[i] #shiftedLegendre(i)
  92. #U += lambdas[i]*polyL(x)
  93. return U
  94. def GetLambda(mag_rf):
  95. M_cutoff = len(mag_rf)
  96. all_lambda = []
  97. for i in range(M_cutoff):
  98. lambd = mpf(0.0)
  99. for j in range(i+1):
  100. lambd += K(i,j)*mpf(mag_rf[j])
  101. # print("K({:d},{:d}) =".format(i,j), float(K(i,j)))
  102. all_lambda.append(float(lambd))
  103. # tmp = [mpf(0.0) for n in range(M_cutoff)]
  104. # all_lambda = np.array(tmp)
  105. # all_lambda[10] = mpf(1.0)
  106. return all_lambda
  107. def GetAbsLastLambda(mag_rf):
  108. M_cutoff = len(mag_rf)
  109. all_lambda = []
  110. i = M_cutoff - 1
  111. lambd = mpf(0.0)
  112. for j in range(i+1):
  113. lambd += K(i,j)*mpf(mag_rf[j])
  114. # print("K({:d},{:d}) =".format(i,j), float(K(i,j)))
  115. all_lambda.append(float(lambd))
  116. # tmp = [mpf(0.0) for n in range(M_cutoff)]
  117. # all_lambda = np.array(tmp)
  118. # all_lambda[10] = mpf(1.0)
  119. return np.abs(all_lambda[0])
  120. def shiftedLegendre(n):
  121. coeffs = []
  122. for k in range(n+1):
  123. val = mpf(-1)**n * binom(mpf(n),mpf(k)) * binom(n+k,k) * (-1)**k
  124. coeffs.insert(0,val*mp.sqrt(2*n+1))
  125. return np.poly1d(coeffs)
  126. def GetGnk(c,n,k):
  127. Gnk = mpf(0.0)
  128. for m in range(k+1):
  129. Gnk+=(-1)**m * binom(mpf(k),mpf(m)) * c[n+m]
  130. return Gnk
  131. def GetGnkSum(c):
  132. total = 0
  133. N = len(c)
  134. for n in range(2):
  135. for k in range (N-n-2,N-n):
  136. Gnk = float(GetGnk(c , n, k))
  137. if Gnk <0:
  138. total += Gnk
  139. return total
  140. def FixGnk(mag_sin):
  141. total = GetGnkSum(mag_sin)
  142. print(total)
  143. def mat1mp (mag_sin):
  144. N = len(mag_sin)
  145. NN = int((N-1)/2)
  146. tmp = np.array([mpf(0.0) for n in range((NN+1)**2)])
  147. tmp2 = np.reshape(tmp,( NN+1 , NN+1 ))
  148. mat = mp.matrix(tmp2)
  149. # mat = np.zeros(( NN+1 , NN+1 ))
  150. for i in range(NN+1):
  151. for j in range(NN+1):
  152. mat[i,j] = mag_sin[i+j+1]
  153. # print(mat)
  154. return mat
  155. def mat1 (mag_sin):
  156. N = len(mag_sin)
  157. NN = int((N-1)/2)
  158. mat = np.zeros(( NN+1 , NN+1 ))
  159. for i in range(NN+1):
  160. for j in range(NN+1):
  161. mat[i,j] = mag_sin[i+j+1]
  162. # print(mat)
  163. return mat
  164. def mat2 (mag_sin):
  165. N = len(mag_sin)
  166. NN = int((N-1)/2)
  167. mat = np.zeros(( NN+1 , NN+1 ))
  168. for i in range(NN+1):
  169. for j in range(NN+1):
  170. mat[i,j] = mag_sin[i+j] - mag_sin[i+j+1]
  171. return mat
  172. def fitness(corrector):
  173. mag_sin = mag_sin_0*(1 + mpf(1.1)*(corrector-0.5)*noise_ratio)
  174. res = GetAbsLastLambda(mag_sin)
  175. global globi
  176. globi = globi + 1
  177. if globi % 100 == 0: print("REsult", res)
  178. return res
  179. mag_sin_0, mag_cos_0 = gen_rf_signal(time_steps)
  180. mag_sin = np.copy(mag_sin_0)
  181. mag_cos = np.copy(mag_cos_0)
  182. L = [] # Shifted Legendre polinomials
  183. for i in range(len(mag_sin)):
  184. polyL = shiftedLegendre(i)
  185. L += [polyL]
  186. globi = 1
  187. # import time
  188. # time1 = time.time()
  189. # corr = np.zeros(len(mag_sin))
  190. # fitness(corr)
  191. # from scipy.optimize import minimize
  192. # # = minimize(fitness,corr,method='L-BFGS-B')
  193. # v = minimize(fitness,corr,method='TNC',options={'disp': False, 'minfev': 0, 'scale': None, 'rescale': -1, 'offset': None, 'gtol': -1, 'eps': 1e-08, 'eta': -1, 'maxiter': None, 'maxCGit': -1, 'mesg_num': None, 'ftol': -1, 'xtol': -1, 'stepmx': 0, 'accuracy': 0})
  194. # # from scipy.optimize import differential_evolution
  195. # # bounds = []
  196. # # for i in range(len(mag_sin)):
  197. # # bounds.append((0,1))
  198. # # v = differential_evolution(fitness,bounds)
  199. # fitness(v.x)
  200. # print(v.fun, "at", v.x)
  201. # time2 = time.time()
  202. # print( 'it took %0.5f s' % ((time2-time1)))
  203. print("N = ",len(mag_sin))
  204. print("Noise = ", noise_ratio )
  205. # print("c_n =",["%0.8f"%(x) for x in mag_sin])
  206. # print("mat1 =",mat1(mag_sin))
  207. # print("mat2 =",mat2(mag_sin))
  208. #print( 'eigsh took %0.5f s' % ((time2-time1)))
  209. #print("eigsh1 = ",v1,"eigsh2 = ",v2)
  210. # v1f = eig(mat1(mag_sin))
  211. # v2f = eig(mat2(mag_sin))
  212. # print("eig1 = ",np.sort(v1f[0]))
  213. # print("eig2 = ",np.sort(v2f[0]))
  214. # v = eigsh(mat2(mag_sin), k=6, return_eigenvectors=False, which='LA')
  215. # print(v)
  216. # N = len(mag_sin)
  217. # print("N = ", N)
  218. # for n in range(2):
  219. # for k in range (N-n-2,N-n):
  220. # Gnk = float(GetGnk(mag_sin, n, k))
  221. # if Gnk <0:
  222. # print("G",n,k," = ", Gnk)
  223. #FixGnk(mag_sin)
  224. sign = ""
  225. # for i in range(voxel_num):
  226. # if i%5 == 0:
  227. # sign+="\n"
  228. # sign = sign + '{:3.2g}'.format(float(a_i[i]))+"/"+'{:3.2g}'.format(float(d_i[i]))+", "
  229. #
  230. # # print(mp.exp(-1.0/voxel_T2s_decay[i]))
  231. plt.plot(mag_sin, ls=' ', marker='o')
  232. plt.title("Signal to restore amp/decay_T:"+sign)
  233. plt.savefig("signal.pdf")
  234. plt.clf()
  235. recL = []
  236. x = np.linspace(0,1, U_points)
  237. # polyL_val = np.array([float(L[-1](x[n])) for n in range(U_points)])
  238. # plt.plot(x,polyL_val)
  239. # plt.title("Legendre polynom of order "+str(len(L)))
  240. # plt.savefig("polyL.pdf")
  241. # plt.clf()
  242. #print("Before lambda.")
  243. import time
  244. time1 = time.time()
  245. lambdas = GetLambda(mag_sin)
  246. time2 = time.time()
  247. print( 'GetLambda took %0.5f s' % ((time2-time1)))
  248. print((lambdas))
  249. U = GetU(lambdas)
  250. sign =""
  251. for i in range(voxel_num):
  252. sign+="{:4.3g}, ".format(float( mp.exp(-(period/rf_samples_per_period)/voxel_T2s_decay[i]) ))
  253. #print(sign)
  254. x = np.linspace(0,1, U_points)
  255. mag_x = np.linspace(0,1, len(mag_sin))
  256. import matplotlib as matplotlib
  257. matplotlib.rcParams.update({'font.size': 28})
  258. plt.figure(figsize=(30, 20))
  259. plt.plot(x,U, lw=0.2)
  260. plt.title(r"$\sum^M \lambda_i L_i(x)$", y=1.02)
  261. plt.xlim(0,1)
  262. plt.xlabel(sign, fontsize=28)
  263. plt.savefig("plt.pdf")
  264. plt.clf()
  265. # rms = np.sqrt(np.mean(U**2))/np.max(U)
  266. # #print(rms)
  267. # status = "OK"
  268. # if rms < 0.2: status = "BAD"
  269. # print("rms =", rms, status)