utility_functions.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. # -*- coding: utf-8 -*-
  2. """
  3. These are some useful functions used in CSD methods,
  4. They include CSD source profiles to be used as ground truths,
  5. placement of electrodes in 1D, 2D and 3D., etc
  6. These scripts are based on Grzegorz Parka's,
  7. Google Summer of Code 2014, INFC/pykCSD
  8. This was written by :
  9. Michal Czerwinski, Chaitanya Chintaluri
  10. Laboratory of Neuroinformatics,
  11. Nencki Institute of Experimental Biology, Warsaw.
  12. """
  13. from __future__ import division
  14. import numpy as np
  15. from numpy import exp
  16. import quantities as pq
  17. def patch_quantities():
  18. """patch quantities with the SI unit Siemens if it does not exist"""
  19. for symbol, prefix, definition, u_symbol in zip(
  20. ['siemens', 'S', 'mS', 'uS', 'nS', 'pS'],
  21. ['', '', 'milli', 'micro', 'nano', 'pico'],
  22. [pq.A / pq.V, pq.A / pq.V, 'S', 'mS', 'uS', 'nS'],
  23. [None, None, None, None, u'µS', None]):
  24. if type(definition) is str:
  25. definition = lastdefinition / 1000
  26. if not hasattr(pq, symbol):
  27. setattr(pq, symbol, pq.UnitQuantity(
  28. prefix + 'siemens',
  29. definition,
  30. symbol=symbol,
  31. u_symbol=u_symbol))
  32. lastdefinition = definition
  33. return
  34. def check_for_duplicated_electrodes(elec_pos):
  35. """Checks for duplicate electrodes
  36. Parameters
  37. ----------
  38. elec_pos : np.array
  39. Returns
  40. -------
  41. has_duplicated_elec : Boolean
  42. """
  43. unique_elec_pos = np.vstack({tuple(row) for row in elec_pos})
  44. has_duplicated_elec = unique_elec_pos.shape == elec_pos.shape
  45. return has_duplicated_elec
  46. def distribute_srcs_1D(X, n_src, ext_x, R_init):
  47. """Distribute sources in 1D equally spaced
  48. Parameters
  49. ----------
  50. X : np.arrays
  51. points at which CSD will be estimated
  52. n_src : int
  53. number of sources to be included in the model
  54. ext_x : floats
  55. how much should the sources extend the area X
  56. R_init : float
  57. Same as R in 1D case
  58. Returns
  59. -------
  60. X_src : np.arrays
  61. positions of the sources
  62. R : float
  63. effective radius of the basis element
  64. """
  65. X_src = np.mgrid[(np.min(X) - ext_x):(np.max(X) + ext_x):
  66. np.complex(0, n_src)]
  67. R = R_init
  68. return X_src, R
  69. def distribute_srcs_2D(X, Y, n_src, ext_x, ext_y, R_init):
  70. """Distribute n_src's in the given area evenly
  71. Parameters
  72. ----------
  73. X, Y : np.arrays
  74. points at which CSD will be estimated
  75. n_src : int
  76. demanded number of sources to be included in the model
  77. ext_x, ext_y : floats
  78. how should the sources extend the area X, Y
  79. R_init : float
  80. demanded radius of the basis element
  81. Returns
  82. -------
  83. X_src, Y_src : np.arrays
  84. positions of the sources
  85. nx, ny : ints
  86. number of sources in directions x,y
  87. new n_src = nx * ny may not be equal to the demanded number of sources
  88. R : float
  89. effective radius of the basis element
  90. """
  91. Lx = np.max(X) - np.min(X)
  92. Ly = np.max(Y) - np.min(Y)
  93. Lx_n = Lx + (2 * ext_x)
  94. Ly_n = Ly + (2 * ext_y)
  95. [nx, ny, Lx_nn, Ly_nn, ds] = get_src_params_2D(Lx_n, Ly_n, n_src)
  96. ext_x_n = (Lx_nn - Lx) / 2
  97. ext_y_n = (Ly_nn - Ly) / 2
  98. X_src, Y_src = np.mgrid[(np.min(X) - ext_x_n):(np.max(X) + ext_x_n):
  99. np.complex(0, nx),
  100. (np.min(Y) - ext_y_n):(np.max(Y) + ext_y_n):
  101. np.complex(0, ny)]
  102. # d = round(R_init / ds)
  103. R = R_init # R = d * ds
  104. return X_src, Y_src, R
  105. def get_src_params_2D(Lx, Ly, n_src):
  106. """Distribute n_src sources evenly in a rectangle of size Lx * Ly
  107. Parameters
  108. ----------
  109. Lx, Ly : floats
  110. lengths in the directions x, y of the area,
  111. the sources should be placed
  112. n_src : int
  113. demanded number of sources
  114. Returns
  115. -------
  116. nx, ny : ints
  117. number of sources in directions x, y
  118. new n_src = nx * ny may not be equal to the demanded number of sources
  119. Lx_n, Ly_n : floats
  120. updated lengths in the directions x, y
  121. ds : float
  122. spacing between the sources
  123. """
  124. coeff = [Ly, Lx - Ly, -Lx * n_src]
  125. rts = np.roots(coeff)
  126. r = [r for r in rts if type(r) is not complex and r > 0]
  127. nx = r[0]
  128. ny = n_src / nx
  129. ds = Lx / (nx - 1)
  130. nx = np.floor(nx) + 1
  131. ny = np.floor(ny) + 1
  132. Lx_n = (nx - 1) * ds
  133. Ly_n = (ny - 1) * ds
  134. return (nx, ny, Lx_n, Ly_n, ds)
  135. def distribute_srcs_3D(X, Y, Z, n_src, ext_x, ext_y, ext_z, R_init):
  136. """Distribute n_src sources evenly in a rectangle of size Lx * Ly * Lz
  137. Parameters
  138. ----------
  139. X, Y, Z : np.arrays
  140. points at which CSD will be estimated
  141. n_src : int
  142. desired number of sources we want to include in the model
  143. ext_x, ext_y, ext_z : floats
  144. how should the sources extend over the area X,Y,Z
  145. R_init : float
  146. demanded radius of the basis element
  147. Returns
  148. -------
  149. X_src, Y_src, Z_src : np.arrays
  150. positions of the sources in 3D space
  151. nx, ny, nz : ints
  152. number of sources in directions x,y,z
  153. new n_src = nx * ny * nz may not be equal to the demanded number of
  154. sources
  155. R : float
  156. updated radius of the basis element
  157. """
  158. Lx = np.max(X) - np.min(X)
  159. Ly = np.max(Y) - np.min(Y)
  160. Lz = np.max(Z) - np.min(Z)
  161. Lx_n = Lx + 2 * ext_x
  162. Ly_n = Ly + 2 * ext_y
  163. Lz_n = Lz + 2 * ext_z
  164. (nx, ny, nz, Lx_nn, Ly_nn, Lz_nn, ds) = get_src_params_3D(Lx_n,
  165. Ly_n,
  166. Lz_n,
  167. n_src)
  168. ext_x_n = (Lx_nn - Lx) / 2
  169. ext_y_n = (Ly_nn - Ly) / 2
  170. ext_z_n = (Lz_nn - Lz) / 2
  171. X_src, Y_src, Z_src = np.mgrid[(np.min(X) - ext_x_n):(np.max(X) + ext_x_n):
  172. np.complex(0, nx),
  173. (np.min(Y) - ext_y_n):(np.max(Y) + ext_y_n):
  174. np.complex(0, ny),
  175. (np.min(Z) - ext_z_n):(np.max(Z) + ext_z_n):
  176. np.complex(0, nz)]
  177. # d = np.round(R_init / ds)
  178. R = R_init
  179. return (X_src, Y_src, Z_src, R)
  180. def get_src_params_3D(Lx, Ly, Lz, n_src):
  181. """Helps to evenly distribute n_src sources in a cuboid of size Lx * Ly * Lz
  182. Parameters
  183. ----------
  184. Lx, Ly, Lz : floats
  185. lengths in the directions x, y, z of the area,
  186. the sources should be placed
  187. n_src : int
  188. demanded number of sources to be included in the model
  189. Returns
  190. -------
  191. nx, ny, nz : ints
  192. number of sources in directions x, y, z
  193. new n_src = nx * ny * nz may not be equal to the demanded number of
  194. sources
  195. Lx_n, Ly_n, Lz_n : floats
  196. updated lengths in the directions x, y, z
  197. ds : float
  198. spacing between the sources (grid nodes)
  199. """
  200. V = Lx * Ly * Lz
  201. V_unit = V / n_src
  202. L_unit = V_unit**(1. / 3.)
  203. nx = np.ceil(Lx / L_unit)
  204. ny = np.ceil(Ly / L_unit)
  205. nz = np.ceil(Lz / L_unit)
  206. ds = Lx / (nx - 1)
  207. Lx_n = (nx - 1) * ds
  208. Ly_n = (ny - 1) * ds
  209. Lz_n = (nz - 1) * ds
  210. return (nx, ny, nz, Lx_n, Ly_n, Lz_n, ds)
  211. def generate_electrodes(dim, xlims=[0.1, 0.9], ylims=[0.1, 0.9],
  212. zlims=[0.1, 0.9], res=5):
  213. """Generates electrodes, helpful for FWD funtion.
  214. Parameters
  215. ----------
  216. dim : int
  217. Dimensionality of the electrodes, 1,2 or 3
  218. xlims : [start, end]
  219. Spatial limits of the electrodes
  220. ylims : [start, end]
  221. Spatial limits of the electrodes
  222. zlims : [start, end]
  223. Spatial limits of the electrodes
  224. res : int
  225. How many electrodes in each dimension
  226. Returns
  227. -------
  228. ele_x, ele_y, ele_z : flattened np.array of the electrode pos
  229. """
  230. if dim == 1:
  231. ele_x = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res)]
  232. ele_x = ele_x.flatten()
  233. return ele_x
  234. elif dim == 2:
  235. ele_x, ele_y = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res),
  236. ylims[0]: ylims[1]: np.complex(0, res)]
  237. ele_x = ele_x.flatten()
  238. ele_y = ele_y.flatten()
  239. return ele_x, ele_y
  240. elif dim == 3:
  241. ele_x, ele_y, ele_z = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res),
  242. ylims[0]: ylims[1]: np.complex(0, res),
  243. zlims[0]: zlims[1]: np.complex(0, res)]
  244. ele_x = ele_x.flatten()
  245. ele_y = ele_y.flatten()
  246. ele_z = ele_z.flatten()
  247. return ele_x, ele_y, ele_z
  248. def gauss_1d_dipole(x):
  249. """1D Gaussian dipole source is placed between 0 and 1
  250. to be used to test the CSD
  251. Parameters
  252. ----------
  253. x : np.array
  254. Spatial pts. at which the true csd is evaluated
  255. Returns
  256. -------
  257. f : np.array
  258. The value of the csd at the requested points
  259. """
  260. src = 0.5*exp(-((x-0.7)**2)/(2.*0.3))*(2*np.pi*0.3)**-0.5
  261. snk = -0.5*exp(-((x-0.3)**2)/(2.*0.3))*(2*np.pi*0.3)**-0.5
  262. f = src+snk
  263. return f
  264. def large_source_2D(x, y):
  265. """2D Gaussian large source profile - to use to test csd
  266. Parameters
  267. ----------
  268. x : np.array
  269. Spatial x pts. at which the true csd is evaluated
  270. y : np.array
  271. Spatial y pts. at which the true csd is evaluated
  272. Returns
  273. -------
  274. f : np.array
  275. The value of the csd at the requested points
  276. """
  277. zz = [0.4, -0.3, -0.1, 0.6]
  278. zs = [0.2, 0.3, 0.4, 0.2]
  279. f1 = 0.5965*exp( (-1*(x-0.1350)**2 - (y-0.8628)**2) /0.4464)* exp(-(-zz[0])**2 / zs[0]) /exp(-(zz[0])**2/zs[0])
  280. f2 = -0.9269*exp( (-2*(x-0.1848)**2 - (y-0.0897)**2) /0.2046)* exp(-(-zz[1])**2 / zs[1]) /exp(-(zz[1])**2/zs[1]);
  281. f3 = 0.5910*exp( (-3*(x-1.3189)**2 - (y-0.3522)**2) /0.2129)* exp(-(-zz[2])**2 / zs[2]) /exp(-(zz[2])**2/zs[2]);
  282. f4 = -0.1963*exp( (-4*(x-1.3386)**2 - (y-0.5297)**2) /0.2507)* exp(-(-zz[3])**2 / zs[3]) /exp(-(zz[3])**2/zs[3]);
  283. f = f1+f2+f3+f4
  284. return f
  285. def small_source_2D(x, y):
  286. """2D Gaussian small source profile - to be used to test csd
  287. Parameters
  288. ----------
  289. x : np.array
  290. Spatial x pts. at which the true csd is evaluated
  291. y : np.array
  292. Spatial y pts. at which the true csd is evaluated
  293. Returns
  294. -------
  295. f : np.array
  296. The value of the csd at the requested points
  297. """
  298. def gauss2d(x,y,p):
  299. rcen_x = p[0] * np.cos(p[5]) - p[1] * np.sin(p[5])
  300. rcen_y = p[0] * np.sin(p[5]) + p[1] * np.cos(p[5])
  301. xp = x * np.cos(p[5]) - y * np.sin(p[5])
  302. yp = x * np.sin(p[5]) + y * np.cos(p[5])
  303. g = p[4]*exp(-(((rcen_x-xp)/p[2])**2+
  304. ((rcen_y-yp)/p[3])**2)/2.)
  305. return g
  306. f1 = gauss2d(x,y,[0.3,0.7,0.038,0.058,0.5,0.])
  307. f2 = gauss2d(x,y,[0.3,0.6,0.038,0.058,-0.5,0.])
  308. f3 = gauss2d(x,y,[0.45,0.7,0.038,0.058,0.5,0.])
  309. f4 = gauss2d(x,y,[0.45,0.6,0.038,0.058,-0.5,0.])
  310. f = f1+f2+f3+f4
  311. return f
  312. def gauss_3d_dipole(x, y, z):
  313. """3D Gaussian dipole profile - to be used to test csd.
  314. Parameters
  315. ----------
  316. x : np.array
  317. Spatial x pts. at which the true csd is evaluated
  318. y : np.array
  319. Spatial y pts. at which the true csd is evaluated
  320. z : np.array
  321. Spatial z pts. at which the true csd is evaluated
  322. Returns
  323. -------
  324. f : np.array
  325. The value of the csd at the requested points
  326. """
  327. x0, y0, z0 = 0.3, 0.7, 0.3
  328. x1, y1, z1 = 0.6, 0.5, 0.7
  329. sig_2 = 0.023
  330. A = (2*np.pi*sig_2)**-1
  331. f1 = A*exp( (-(x-x0)**2 -(y-y0)**2 -(z-z0)**2) / (2*sig_2) )
  332. f2 = -1*A*exp( (-(x-x1)**2 -(y-y1)**2 -(z-z1)**2) / (2*sig_2) )
  333. f = f1+f2
  334. return f