OSCR

Spatial richness of neural magnetic fields.

Code ↔ Paper

5 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 5 matches
  1. [1] § 4 Methods › 4.5 Morphology reconstruction modified algorithm ↔ Ziad_MorphologyReconstruction4.ipynb, lines 330–403 · score 0.72 · axon hillock, best fit, outliers, closest, perpendicular, opposite
  2. [2] § 4 Methods › 4.5 Morphology reconstruction modified algorithm ↔ Ziad_MorphologyReconstruction5.ipynb, lines 333–406 · score 0.72 · axon hillock, best fit, outliers, closest, perpendicular, opposite
  3. [3] § 2 Results › 2.2 Neural signal scaling ↔ Ada_09_09_24_main.ipynb, lines 184–269 · score 0.67 · ball stick, soma diameter, axon diameter, axon length, segment, threshold
  4. [4] § 4 Methods › 4.2 Similarity calculation and template creation ↔ ZIAD_PointSpread.ipynb, lines 199–259 · score 0.57 · nearest neighbors, rotated coordinates
  5. [5] § 4 Methods › 4.3 Spike sorting recording generation ↔ MEArecTemplate/generators/spiketraingenerator.py, lines 125–167 · score 0.53 · refractory period, spike train, Poisson, neurons, cell

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 935 lines · 31 KB · CC-BY-4.0 · 1 match

  1. # %%
  2. from sklearn import svm, datasets
  3. from sklearn.linear_model import HuberRegressor
  4. from sklearn.neighbors import NearestNeighbors
  5. from scipy.spatial import distance
  6. import time
  7. import numpy as np
  8. import MEArecTemplate as mr
  9. from pathlib import Path
  10. import MEAutility as mu
  11. import LFPy
  12. import matplotlib.pyplot as plt
  13. # %%
  14. %run ZIAD_MEARecHelperFunctions.ipynb
  15. # %%
  16. # Get all signals from the grid of electrodes that exceed a certain threshold
  17. def get_strong_signals(data, elec_x, elec_y, thresh):
  18. data = data/np.max(abs(data))
  19. coords = []
  20. targets = []
  21. for i in range(len(data)):
  22. if np.max(abs(data[i])) >= thresh:
  23. coords.append([elec_x[i], elec_y[i]])
  24. # Target is 1 if signal is positive, 0 if negative
  25. targets.append(np.max(data[i]) > abs(np.min(data[i])))
  26. return np.array(coords), np.array(targets)
  27. def get_electrodes(mea_name):
  28. mea_cells_folder = '/Users/Ziad/.config/mearec/1.7.2/cell_models/MEArecLinearCells/'
  29. cell_name = 'L5_TTPC1_cADpyr232_1'
  30. cell_model_folder = Path(Path(mea_cells_folder) / cell_name)
  31. cell = mr.return_bbp_cell(cell_model_folder, end_T=1000, dt=0.03125, start_T=0)
  32. mea = mu.return_mea(mea_name)
  33. electrodes = LFPy.RecExtElectrode(cell, probe=mea)
  34. return electrodes
  35. def get_electrodes2(mea_name):
  36. m_idx = mea_name.find('MEA')
  37. count = int(np.sqrt(int(mea_name[:m_idx])))
  38. pitch = int(mea_name[m_idx+3:])
  39. max_coord = (count - 1)*pitch/2.0
  40. coords = np.arange(-1*max_coord, max_coord+1, pitch)
  41. elec_x = []
  42. elec_y = []
  43. for xcoord in coords:
  44. for ycoord in coords:
  45. elec_x.append(xcoord)
  46. elec_y.append(ycoord)
  47. return np.array(elec_x), np.array(elec_y)
  48. def make_meshgrid(x, y, h=.5):
  49. x_min, x_max = x.min() - 1, x.max() + 1
  50. y_min, y_max = y.min() - 1, y.max() + 1
  51. xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
  52. return xx, yy
  53. def plot_contours(ax, clf, xx, yy, **params):
  54. Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
  55. Z = Z.reshape(xx.shape)
  56. out = ax.contourf(xx, yy, Z, **params)
  57. return out, Z
  58. def load_cell(template_id, tempgen):
  59. # Load cell and position and rotation info
  60. mea_cells_folder = '/Users/Ziad/.config/mearec/1.7.2/cell_models/MEArecLinearCells/'
  61. cell_name = 'L5_TTPC1_cADpyr232_1'
  62. cell_model_folder = Path(Path(mea_cells_folder) / cell_name)
  63. T = 1000
  64. dt = 0.03125
  65. cell = mr.return_bbp_cell(cell_model_folder, end_T=T, dt=dt, start_T=0)
  66. pos = tempgen.locations[template_id]
  67. rot = tempgen.rotations[template_id]
  68. cell = mr.ziad_flatten_geometry(cell, pos, rot, 10)
  69. cell.set_pos(pos[0], pos[1], pos[2])
  70. cell.set_rotation(rot[0], rot[1], rot[2])
  71. return cell
  72. def get_apic_dist(template_id, all_y, all_z, clf, verbose=False):
  73. coords = np.zeros((2, np.shape(all_y)[1]))
  74. coords[0] = all_y[template_id]
  75. coords[1] = all_z[template_id]
  76. dists = abs(clf.decision_function(coords.T))/np.linalg.norm(clf.coef_)
  77. if verbose:
  78. print(dists)
  79. return np.mean(dists), np.std(dists)
  80. def get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = [], verbose=False):
  81. coords = np.zeros((np.shape(all_y)[1], 2))
  82. coords[:, 0] = all_y[template_id]
  83. coords[:, 1] = all_z[template_id]
  84. if len(boundary) == 0:
  85. dists = distance.cdist(coords, get_boundary_coords(xx, yy, clf), 'euclidean')
  86. else:
  87. dists = distance.cdist(coords, boundary, 'euclidean')
  88. dists = np.min(dists, axis=1)
  89. # if verbose:
  90. # print(dists)
  91. # plt.hist(dists, bins=30)
  92. return np.mean(dists), np.std(dists), dists
  93. def get_boundary_coords(xx, yy, clf):
  94. Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
  95. Z = Z.reshape(xx.shape)
  96. boundary = []
  97. for i in range(len(xx)):
  98. for j in range(1, len(xx[0])):
  99. if j > 0:
  100. if Z[i, j] != Z[i, j-1]:
  101. #print("hit1")
  102. midx = (xx[i, j] + xx[i, j-1]) / 2
  103. midy = yy[i, j]
  104. boundary.append([midx, midy])
  105. if i > 0:
  106. if Z[i, j] != Z[i-1, j]:
  107. #print("hit2")
  108. midx = xx[i, j]
  109. midy = (yy[i, j] + yy[i-1, j]) / 2
  110. boundary.append([midx, midy])
  111. #print(boundary)
  112. boundary = np.array(boundary)
  113. return boundary
  114. def generate_noise(snr, sig, shape):
  115. noise = np.random.normal(size=shape)
  116. sig_pwr = np.sum(sig**2)
  117. noise_pwr = sig_pwr/(10**(snr/10))
  118. noise_coeff = np.sqrt(noise_pwr/np.sum(noise**2))
  119. noise = noise*noise_coeff
  120. new_noise_pwr = np.sum(noise**2)
  121. #print("SNR: ", 10*np.log10(sig_pwr/noise_pwr))
  122. return noise
  123. def estimate_axon_hillock(signals, elec_x, elec_y, return_med = False):
  124. mags_pwr = np.sqrt(np.sum(signals**2, axis=1))
  125. mags_strong_indices = np.argsort(-1*mags_pwr)[:4]
  126. mags_relative_pwr = mags_pwr[mags_strong_indices] / np.sum(mags_pwr[mags_strong_indices])
  127. new_x = np.sum(elec_x[mags_strong_indices] * mags_relative_pwr)
  128. new_y = np.sum(elec_y[mags_strong_indices] * mags_relative_pwr)
  129. # Returns coordinates of other strong points
  130. if return_med:
  131. new_coords = []
  132. indices = np.arange(10, 100, 10)
  133. #indices = np.arange(4, 17, 4)
  134. for index in indices:
  135. mags_med_indices = np.argsort(-1*mags_pwr)[index:index+4]
  136. mags_relative_pwr_med = mags_pwr[mags_med_indices] / np.sum(mags_pwr[mags_med_indices])
  137. med_x = np.sum(elec_x[mags_med_indices] * mags_relative_pwr_med)
  138. med_y = np.sum(elec_y[mags_med_indices] * mags_relative_pwr_med)
  139. new_coords.append([med_x, med_y])
  140. return new_x, new_y, np.array(new_coords)
  141. return new_x, new_y
  142. def get_lbf_points(signals, indices, elec_x, elec_y):
  143. mags_pwr = np.sqrt(np.sum(signals**2, axis=1))
  144. new_coords = []
  145. #indices = np.arange(10, 100, 10)
  146. #indices = np.arange(4, 17, 4)
  147. for index in indices:
  148. mags_med_indices = np.argsort(-1*mags_pwr)[index:index+4]
  149. mags_relative_pwr_med = mags_pwr[mags_med_indices] / np.sum(mags_pwr[mags_med_indices])
  150. med_x = np.sum(elec_x[mags_med_indices] * mags_relative_pwr_med)
  151. med_y = np.sum(elec_y[mags_med_indices] * mags_relative_pwr_med)
  152. new_coords.append([med_x, med_y])
  153. return np.array(new_coords)
  154. def cast_lbf_to_boundary(lbf_coords, boundary):
  155. dists = distance.cdist(lbf_coords, boundary, 'euclidean')
  156. min_coord_idxs = np.argmin(dists, axis=1)
  157. cast_coords = boundary[min_coord_idxs]
  158. return cast_coords
  159. def calc_lbf(ah_x, ah_y, point_coords, loss_func, weight, eps = 1.35):
  160. x = np.zeros((len(point_coords[:, 0]) + 1,))
  161. y = np.zeros((len(point_coords[:, 1]) + 1,))
  162. x[0] = ah_x
  163. y[0] = ah_y
  164. x[1:] = point_coords[:, 0]
  165. y[1:] = point_coords[:, 1]
  166. weights = np.ones((len(x),))
  167. weights[0] = weight
  168. if loss_func == 'Huber':
  169. huber = HuberRegressor(epsilon = eps)
  170. try:
  171. huber.fit(x[:, None], y, sample_weight = weights)
  172. except ValueError as e:
  173. print(f"Value error with eps = {eps} - retrying with different epsilon")
  174. return -1e8, -1e8
  175. y1 = huber.predict([[0]])
  176. y2 = huber.predict([[1]])
  177. m = (y2 - y1)
  178. b = y1
  179. elif loss_func == 'Normal':
  180. m, b = np.polyfit(x, y, 1, w = weights)
  181. return m, b
  182. def load_cell(template_id, tempgen):
  183. # Load cell and position and rotation info
  184. mea_cells_folder = '/Users/Ziad/.config/mearec/1.7.2/cell_models/MEArecLinearCells/'
  185. cell_name = 'L5_TTPC1_cADpyr232_1'
  186. cell_model_folder = Path(Path(mea_cells_folder) / cell_name)
  187. T = 1000
  188. dt = 0.03125
  189. cell = mr.return_bbp_cell(cell_model_folder, end_T=T, dt=dt, start_T=0)
  190. pos = tempgen.locations[template_id]
  191. rot = tempgen.rotations[template_id]
  192. cell = mr.ziad_flatten_geometry(cell, pos, rot, 10)
  193. cell.set_pos(pos[0], pos[1], pos[2])
  194. cell.set_rotation(rot[0], rot[1], rot[2])
  195. return cell
  196. def remove_outliers(n_neighbors, cast_coords, max_range):
  197. # Remove outliers
  198. nbrs = NearestNeighbors(n_neighbors=n_neighbors, algorithm='ball_tree').fit(cast_coords)
  199. distances, indices = nbrs.kneighbors(cast_coords)
  200. avg_dists = np.mean(distances[:, 1:], axis=1)
  201. cast_coords_no_outliers = []
  202. for i in range(len(avg_dists)):
  203. if avg_dists[i] < max_range:
  204. cast_coords_no_outliers.append(cast_coords[i])
  205. return np.array(cast_coords_no_outliers)
  206. def gen_modified_coords(clf, signals, elec_x, elec_y, xx, yy, coords, params):
  207. min_dist = params['min_dist']
  208. max_dist = params['max_dist']
  209. num_points = params['num_points']
  210. opp_dist = params['opp_dist']
  211. # Get boundary estimation (true/false values assigned to dense coordinate map) and cast to boolean
  212. Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
  213. Z = Z.reshape(xx.shape)
  214. Z = (Z != 0)
  215. # Estimate axon hillock as well as direction of neuron
  216. ah_x, ah_y, new_coords = estimate_axon_hillock(signals, elec_x, elec_y, return_med = True)
  217. # Calculate line of best fit - assign half the points to be identical (axon hillock)
  218. mult = 2
  219. fit_coords = np.zeros((len(new_coords)*(mult+1), 2))
  220. fit_coords[:len(new_coords)*mult, 0] = ah_x
  221. fit_coords[:len(new_coords)*mult, 1] = ah_y
  222. fit_coords[len(new_coords)*mult:, 0] = new_coords[:, 0]
  223. fit_coords[len(new_coords)*mult:, 1] = new_coords[:, 1]
  224. m, b = np.polyfit(fit_coords[:, 0], fit_coords[:, 1], 1)
  225. # Find points above line
  226. q = yy > (xx*m + b)
  227. # Determine whether points above line are mostly in category 0 or 1 of SVM
  228. greater_than_category = np.sum(q & Z)/np.sum(q) > 0.5
  229. # Generate points close to axon_hillock along line of best fit at specified distances
  230. nearby_dists = np.linspace(min_dist, max_dist, num_points)
  231. # Find closest point to (ah_x, ah_y) on line
  232. b2 = ah_y + ah_x/m
  233. ah_x2 = (b2 - b)/(m + 1/m)
  234. ah_y2 = m*ah_x2 + b
  235. # Get nearby points along line of best fit
  236. nearby_x = nearby_dists/np.sqrt(1 + m**2) + ah_x2
  237. nearby_y = m*nearby_x + b
  238. # Get one point one either side of each of those points along the line perpendicular to line of best fit
  239. b_vals = nearby_y + nearby_x/m
  240. opposite_dists = np.arange(-1*opp_dist, opp_dist + 0.1, 2*opp_dist)
  241. opposite_x = np.add.outer(opposite_dists/np.sqrt(1 + (1/m)**2), nearby_x)
  242. b_vals = np.ones(np.shape(opposite_x)) * b_vals
  243. opposite_y = -1*opposite_x / m + b_vals
  244. # Generate modified coordinates and targets
  245. mod_coords = np.zeros((len(coords)+np.shape(opposite_x)[1]*np.shape(opposite_x)[0], 2))
  246. mod_coords[:len(coords), :] = coords
  247. mod_coords[len(coords):len(coords)+len(opposite_x[0]), 0] = opposite_x[0, :]
  248. mod_coords[len(coords):len(coords)+len(opposite_x[0]), 1] = opposite_y[0, :]
  249. mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 0] = opposite_x[1, :]
  250. mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 1] = opposite_y[1, :]
  251. mod_targets = np.zeros((len(mod_coords),))
  252. mod_targets[:len(targets)] = targets[:]
  253. mod_targets[len(targets):np.shape(opposite_x)[1] + len(targets)] = not greater_than_category
  254. mod_targets[len(targets) + np.shape(opposite_x)[1]:np.shape(opposite_x)[1]*2 + len(targets)] = greater_than_category
  255. return mod_coords, mod_targets
  256. def gen_modified_coords2(clf, signals, elec_x, elec_y, xx, yy, coords, boundary, params):
  257. min_dist = params['min_dist']
  258. max_dist = params['max_dist']
  259. num_points = params['num_points']
  260. opp_dist = params['opp_dist']
  261. weight = params['weight']
  262. eps = params['epsilon']
  263. max_range = params['max_range']
  264. # Get boundary estimation (true/false values assigned to dense coordinate map) and cast to boolean
  265. Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
  266. Z = Z.reshape(xx.shape)
  267. Z = (Z != 0)
  268. ah_x, ah_y, new_coords = estimate_axon_hillock(signals, elec_x, elec_y, return_med = True)
  269. indices = np.arange(4, 200, 4)
  270. new_coords = get_lbf_points(signals, indices, elec_x, elec_y)
  271. cast_coords = cast_lbf_to_boundary(new_coords, boundary)
  272. cast_coords = remove_outliers(4, cast_coords, max_range)
  273. m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Normal', weight, eps = eps)
  274. # for i in range(3):
  275. # m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Huber', weight, eps = eps)
  276. # if m != -1e8 and b != -1e8:
  277. # break
  278. # else:
  279. # eps += 0.05
  280. # if m == -1e8 and b == -1e8:
  281. # m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Normal', weight, eps = eps)
  282. q = yy > (xx*m + b)
  283. greater_than_category = np.sum(q & Z)/np.sum(q) > 0.5
  284. # Points close to axon_hillock along line of best fit
  285. nearby_dists = np.linspace(min_dist, max_dist, num_points)
  286. #nearby_dists = np.arange(-5, 5.1, 0.01)*5
  287. # Find closest point to (ah_x, ah_y) on line
  288. b2 = ah_y + ah_x/m
  289. ah_x2 = (b2 - b)/(m + 1/m)
  290. ah_y2 = m*ah_x2 + b
  291. #print(ah_x, ah_x2)
  292. # Get nearby points along line of best fit
  293. nearby_x = nearby_dists/np.sqrt(1 + m**2) + ah_x2
  294. nearby_y = m*nearby_x + b
  295. #print(np.sqrt((nearby_x - ah_x2)**2 + (nearby_y - (ah_y2))**2))
  296. # Get one point one either side of each of those points along the line perpendicular to line of best fit
  297. b_vals = nearby_y + nearby_x/m
  298. opposite_dists = np.arange(-2, 2.1, 4)*5
  299. opposite_x = np.add.outer(opposite_dists/np.sqrt(1 + (1/m)**2), nearby_x)
  300. b_vals = np.ones(np.shape(opposite_x)) * b_vals
  301. opposite_y = -1*opposite_x / m + b_vals
  302. # Re-calculate SVM with modified coordinates
  303. mod_coords = np.zeros((len(coords)+np.shape(opposite_x)[1]*np.shape(opposite_x)[0], 2))
  304. mod_coords[:len(coords), :] = coords
  305. mod_coords[len(coords):len(coords)+len(opposite_x[0]), 0] = opposite_x[0, :]
  306. mod_coords[len(coords):len(coords)+len(opposite_x[0]), 1] = opposite_y[0, :]
  307. mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 0] = opposite_x[1, :]
  308. mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 1] = opposite_y[1, :]
  309. mod_targets = np.zeros((len(mod_coords),))
  310. mod_targets[:len(targets)] = targets[:]
  311. mod_targets[len(targets):np.shape(opposite_x)[1] + len(targets)] = not greater_than_category
  312. mod_targets[len(targets) + np.shape(opposite_x)[1]:np.shape(opposite_x)[1]*2 + len(targets)] = greater_than_category
  313. return mod_coords, mod_targets
  314. # %%
  315. # Run for all cells
  316. snrs = [40, 20, 0]
  317. mea_names = ['400MEA50', '400MEA75', '400MEA100']
  318. file_prefix = 'mag_templates_flattened_morphology_L5_TTPC1_cADpyr232_1_n300_'
  319. for snr in snrs:
  320. for mea_name in mea_names:
  321. # Parameters
  322. #mea_name = '400MEA50'
  323. thresh = 0.0
  324. #snr = 40
  325. iters_per_cell = 1
  326. C = 0.1
  327. gamma = 0.1
  328. cells = range(300)
  329. print(snr, mea_name)
  330. # Load template
  331. #templates_file = f'ziad_mearec_templates/mag_templates_flattened_morphology_L5_TTPC1_cADpyr232_1_n300_{mea_name}.h5'
  332. #tempgen = mr.tools.load_templates(templates_file, verbose=False)
  333. # Get apical dendrite coordinates and extracellular magnetic fields
  334. with open(f'{file_prefix}{mea_name}.npy', 'rb') as f:
  335. all_y = np.load(f)
  336. all_z = np.load(f)
  337. mags = np.load(f)
  338. # Main loop
  339. elec_x, elec_y = get_electrodes2(mea_name)
  340. xx, yy = make_meshgrid(np.array(elec_x), np.array(elec_y))
  341. dists = np.zeros((len(cells), iters_per_cell))
  342. dists_std = np.zeros((len(cells), iters_per_cell))
  343. dists_list = []
  344. dists_std_list = []
  345. fails = 0
  346. for n, template_id in enumerate(cells):
  347. #print("Template ID: ", template_id)
  348. for itr in range(iters_per_cell):
  349. #start = time.time()
  350. #elec_x, elec_y = get_electrodes2(mea_name)
  351. #cell = load_cell(template_id, tempgen)
  352. # Generate noise according to SNR parameter
  353. noise = generate_noise(snr, mags[template_id], np.shape(mags[template_id]))
  354. # Extract signals with magnitude that exceeds threshold
  355. signals = mags[template_id] + noise
  356. coords, targets = get_strong_signals(signals, elec_x, elec_y, thresh)
  357. # Estimate axon hillock based on strongest signals
  358. #ah_x, ah_y = estimate_axon_hillock(mags[template_id] + noise, elec_x, elec_y)
  359. #real_x = all_y[template_id][0]
  360. #real_y = all_z[template_id][0]
  361. #est_dist = np.sqrt((real_x - ah_x)**2 + (real_y - ah_y)**2)
  362. #print("Estimated axon hillock distance: ", est_dist)
  363. # Apply SVM to coordinates of strong signals
  364. model = svm.SVC(kernel="rbf")
  365. clf = model.fit(coords, targets)
  366. boundary = get_boundary_coords(xx, yy, clf)
  367. # Generate modified coordinates and calculate new SVM
  368. params = {}
  369. params['min_dist'] = -25
  370. params['max_dist'] = 25
  371. params['num_points'] = 1000
  372. params['opp_dist'] = 10
  373. params['weight'] = 100
  374. params['epsilon'] = 1.2
  375. params['max_range'] = 50
  376. mod_coords, mod_targets = gen_modified_coords2(clf, signals, elec_x, elec_y, xx, yy, coords, boundary, params)
  377. mod_model = svm.SVC(kernel='rbf')
  378. mod_clf = mod_model.fit(mod_coords, mod_targets)
  379. mod_boundary = get_boundary_coords(xx, yy, mod_clf)
  380. boundaries = [boundary, mod_boundary]
  381. # if len(boundary) != 0:
  382. # dist, std, all_dists = get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = boundary)
  383. # print("Original dist: ", dist)
  384. if len(mod_boundary) != 0:
  385. dist, std, all_dists = get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = mod_boundary)
  386. #print("Modified dist: ", dist)
  387. for i in range(len(all_dists)):
  388. print(all_dists[i], end='\t')
  389. #print()
  390. else:
  391. dist = 10000000
  392. std = 1000000
  393. print()
  394. #print('Dist: ', dist)
  395. if dist < 10000000:
  396. dists[n, itr] = dist
  397. dists_std[n, itr] = std
  398. dists_list.append(dist)
  399. dists_std_list.append(std)
  400. else:
  401. fails += 1
  402. #print("Time: ", time.time()-start)
  403. # %%
  404. for i in range(3):
  405. print(i)
  406. if i == 1:
  407. break
  408. # %%
  409. # Compute modified SVM for single cell
  410. file_prefix = 'mag_templates_flattened_morphology_L5_TTPC1_cADpyr232_1_n300_'
  411. mea_name = '400MEA50'
  412. snr = 40
  413. thresh = 0
  414. template_id = 8
  415. plot = True
  416. # Modified SVM parameters
  417. params = {}
  418. params['min_dist'] = -25
  419. params['max_dist'] = 25
  420. params['num_points'] = 100
  421. params['opp_dist'] = 10
  422. # Load actual coordinates of cell
  423. with open(f'{file_prefix}{mea_name}.npy', 'rb') as f:
  424. all_y = np.load(f)
  425. all_z = np.load(f)
  426. mags = np.load(f)
  427. # Get electrode coordinates
  428. elec_x, elec_y = get_electrodes2(mea_name)
  429. xx, yy = make_meshgrid(np.array(elec_x), np.array(elec_y))
  430. # Generate noise according to SNR parameter
  431. noise = generate_noise(snr, mags[template_id], np.shape(mags[template_id]))
  432. # Extract signals with magnitude that exceeds threshold
  433. signals = mags[template_id] + noise
  434. coords, targets = get_strong_signals(signals, elec_x, elec_y, thresh)
  435. # Apply SVM to coordinates of strong signals
  436. model = svm.SVC(kernel="rbf")
  437. clf = model.fit(coords, targets)
  438. boundary = get_boundary_coords(xx, yy, clf)
  439. dist, std, all_dists = get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = boundary)
  440. print("Original dist: ", dist)
  441. # Plot original SVM
  442. tempgen = mr.tools.load_templates('ziad_mearec_templates/' + file_prefix + mea_name + '.h5', verbose=False)
  443. cell = load_cell(template_id, tempgen)
  444. if plot:
  445. fig, ax = plotcell_1plane_morph(cell, 1000, 1000)
  446. ax.set_xlim([-600, 600])
  447. ax.set_ylim([-600, 600])
  448. #out, Z = plot_contours(ax, clf, xx, yy, cmap=plt.cm.coolwarm, alpha=0.8)
  449. Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
  450. Z = Z.reshape(xx.shape)
  451. Z = (Z != 0)
  452. # Generate modified coordinates
  453. # mod_coords, mod_targets = gen_modified_coords(clf, signals, elec_x, elec_y, xx, yy, coords, params)
  454. # mod_model = svm.SVC(kernel='rbf')
  455. # mod_clf = mod_model.fit(mod_coords, mod_targets)
  456. # Estimate axon hillock and plot
  457. ah_x, ah_y, new_coords = estimate_axon_hillock(signals, elec_x, elec_y, return_med = True)
  458. if plot:
  459. ax.scatter(ah_x, ah_y, color='green')
  460. ax.scatter(new_coords[:, 0], new_coords[:, 1], color='black')
  461. # mult = 2
  462. # fit_coords = np.zeros((len(new_coords)*(mult+1), 2))
  463. # fit_coords[:len(new_coords)*mult, 0] = ah_x
  464. # fit_coords[:len(new_coords)*mult, 1] = ah_y
  465. # fit_coords[len(new_coords)*mult:, 0] = new_coords[:, 0]
  466. # fit_coords[len(new_coords)*mult:, 1] = new_coords[:, 1]
  467. # m, b = np.polyfit(fit_coords[:, 0], fit_coords[:, 1], 1)
  468. weight = 100
  469. eps = 1.25
  470. indices = np.arange(4, 200, 4)
  471. new_coords = get_lbf_points(signals, indices, elec_x, elec_y)
  472. cast_coords = cast_lbf_to_boundary(new_coords, boundary)
  473. ax.scatter(cast_coords[:, 0], cast_coords[:, 1], color='red')
  474. # Remove outliers
  475. nbrs = NearestNeighbors(n_neighbors=4, algorithm='ball_tree').fit(cast_coords)
  476. distances, indices = nbrs.kneighbors(cast_coords)
  477. avg_dists = np.mean(distances[:, 1:], axis=1)
  478. max_range = 50
  479. cast_coords_no_outliers = []
  480. for i in range(len(avg_dists)):
  481. if avg_dists[i] < max_range:
  482. cast_coords_no_outliers.append(cast_coords[i])
  483. cast_coords = np.array(cast_coords_no_outliers)
  484. ax.scatter(cast_coords[:, 0], cast_coords[:, 1], color='purple')
  485. m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Huber', weight, eps = eps)
  486. line_x_coords = np.linspace(np.min(elec_x), np.max(elec_x), 400)
  487. if plot:
  488. ax.plot(line_x_coords, m*line_x_coords + b, color = 'blue')
  489. m2, b2 = calc_lbf(ah_x, ah_y, cast_coords, 'Normal', weight)
  490. line_x_coords = np.linspace(np.min(elec_x), np.max(elec_x), 400)
  491. if plot:
  492. ax.plot(line_x_coords, m2*line_x_coords + b2, color = 'orange')
  493. # weight = 1000
  494. # fit_coords = np.zeros((len(new_coords)+1, 2))
  495. # fit_coords[0, 0] = ah_x
  496. # fit_coords[0, 1] = ah_y
  497. # fit_coords[1:, 0] = new_coords[:, 0]
  498. # fit_coords[1:, 1] = new_coords[:, 1]
  499. # weights = np.ones((len(new_coords)+1,))
  500. # weights[0] = weight
  501. # m, b = np.polyfit(fit_coords[:, 0], fit_coords[:, 1], 1, w = weights)
  502. # line_x_coords = np.linspace(np.min(elec_x), np.max(elec_x), 400)
  503. # if plot:
  504. # ax.plot(line_x_coords, m*line_x_coords + b, color = 'orange')
  505. # q = yy > (xx*m + b)
  506. # print(np.unique(Z))
  507. # greater_than_category = np.sum(q & Z)/np.sum(q) > 0.5
  508. # # Points close to axon_hillock along line of best fit
  509. # nearby_dists = np.arange(-5, 5.1, 0.01)*5
  510. # # Find closest point to (ah_x, ah_y) on line
  511. # b2 = ah_y + ah_x/m
  512. # ah_x2 = (b2 - b)/(m + 1/m)
  513. # ah_y2 = m*ah_x2 + b
  514. # print(ah_x, ah_x2)
  515. # # Get nearby points along line of best fit
  516. # nearby_x = nearby_dists/np.sqrt(1 + m**2) + ah_x2
  517. # nearby_y = m*nearby_x + b
  518. # print(np.sqrt((nearby_x - ah_x2)**2 + (nearby_y - (ah_y2))**2))
  519. # # Get one point one either side of each of those points along the line perpendicular to line of best fit
  520. # b_vals = nearby_y + nearby_x/m
  521. # opposite_dists = np.arange(-2, 2.1, 4)*5
  522. # opposite_x = np.add.outer(opposite_dists/np.sqrt(1 + (1/m)**2), nearby_x)
  523. # b_vals = np.ones(np.shape(opposite_x)) * b_vals
  524. # opposite_y = -1*opposite_x / m + b_vals
  525. # if plot:
  526. # ax.plot(opposite_x[0, :], opposite_y[0, :], color='purple')
  527. # ax.plot(opposite_x[1, :], opposite_y[1, :], color='green')
  528. # # ax.contourf(xx, yy, q, alpha = 0.4)
  529. # # Re-calculate SVM with modified coordinates
  530. # mod_coords = np.zeros((len(coords)+np.shape(opposite_x)[1]*np.shape(opposite_x)[0], 2))
  531. # mod_coords[:len(coords), :] = coords
  532. # mod_coords[len(coords):len(coords)+len(opposite_x[0]), 0] = opposite_x[0, :]
  533. # mod_coords[len(coords):len(coords)+len(opposite_x[0]), 1] = opposite_y[0, :]
  534. # mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 0] = opposite_x[1, :]
  535. # mod_coords[len(coords) + len(opposite_x[0]):len(coords)+len(opposite_x[0])*2, 1] = opposite_y[1, :]
  536. # mod_targets = np.zeros((len(mod_coords),))
  537. # mod_targets[:len(targets)] = targets[:]
  538. # mod_targets[len(targets):np.shape(opposite_x)[1] + len(targets)] = not greater_than_category
  539. # mod_targets[len(targets) + np.shape(opposite_x)[1]:np.shape(opposite_x)[1]*2 + len(targets)] = greater_than_category
  540. # sample_weights = np.ones((len(mod_targets),))
  541. # sample_weights[len(targets):] = 1
  542. # mod_model = svm.SVC(kernel="rbf")
  543. # mod_clf = mod_model.fit(mod_coords, mod_targets)
  544. # mod_boundary = get_boundary_coords(xx, yy, mod_clf)
  545. # dist, std, all_dists_mod = get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = mod_boundary)
  546. # print("Modified dist: ", dist)
  547. # if plot:
  548. # fig, ax = plotcell_1plane_morph(cell, 1000, 1000)
  549. # out, Z = plot_contours(ax, mod_clf, xx, yy, cmap=plt.cm.coolwarm, alpha=0.8)
  550. # print("Original Dists: ", all_dists[:10])
  551. # print("Modified Dists: ", all_dists_mod[:10])
  552. # %%
  553. a = []
  554. a.append([2, 0])
  555. a.append([4, 3])
  556. a.append([0, 0])
  557. print(np.shape(np.array(a)))
  558. # %%
  559. nbrs = NearestNeighbors(n_neighbors=4, algorithm='ball_tree').fit(cast_coords)
  560. distances, indices = nbrs.kneighbors(cast_coords)
  561. avg_dists = np.mean(distances[:, 1:], axis=1)
  562. print(np.shape(avg_dists))
  563. print(np.shape(avg_dists[avg_dists < 50]))
  564. print(indices)
  565. # %%
  566. fig, ax = plotcell_1plane_morph(cell, 1000, 1000)
  567. #out, Z = plot_contours(ax, clf, xx, yy, cmap=plt.cm.coolwarm, alpha=0.8)
  568. weight = 100
  569. indices = np.arange(4, 200, 4)
  570. new_coords = get_lbf_points(signals, indices, elec_x, elec_y)
  571. cast_coords = cast_lbf_to_boundary(new_coords, boundary)
  572. ax.scatter(cast_coords[:, 0], cast_coords[:, 1], color='red')
  573. m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Huber', weight, eps = 1.1)
  574. print(m, b)
  575. line_x_coords = np.linspace(-250, 150, 400)
  576. if plot:
  577. plt.plot(line_x_coords, m*line_x_coords + b, color = 'blue')
  578. m, b = calc_lbf(ah_x, ah_y, cast_coords, 'Normal', weight)
  579. print(m, b)
  580. line_x_coords = np.linspace(np.min(elec_x), np.max(elec_x), 400)
  581. if plot:
  582. plt.plot(line_x_coords, m*line_x_coords + b, color = 'orange')
  583. plt.xlim([-475, 475])
  584. plt.ylim([-475, 475])
  585. # %%
  586. np.max(elec_x)
  587. # %%
  588. line_x_coords = np.linspace(np.min(elec_x), np.max(elec_x), 400)
  589. m, b = calc_lbf(ah_x, ah_y, fit_coords[1:, :], 'Huber', 1000)
  590. plt.plot(line_x_coords, m*line_x_coords + b, color = 'orange')
  591. m, b = calc_lbf(ah_x, ah_y, fit_coords[1:, :], 'Normal', 1000)
  592. plt.plot(line_x_coords, m*line_x_coords + b, color = 'green')
  593. # %%
  594. print(all_y[template_id, 0], all_z[template_id, 0])
  595. soma = np.zeros((1, 2))
  596. soma[0] = [all_y[template_id, 0], all_z[template_id, 0]]
  597. print(boundary)
  598. dist = distance.cdist(soma, boundary)
  599. print(np.min(dist), np.argmin(dist))
  600. # %%
  601. print(all_y[template_id, 0], all_z[template_id, 0])
  602. soma = np.zeros((1, 2))
  603. soma[0] = [all_y[template_id, 0], all_z[template_id, 0]]
  604. print(boundary)
  605. dist = distance.cdist(soma, mod_boundary)
  606. print(np.min(dist), np.argmin(dist))
  607. # %%
  608. plt.plot(all_dists)
  609. plt.plot(all_dists_mod)
  610. # %%
  611. a = np.zeros((3, 2))
  612. a[0] = [0, 0]
  613. a[1] = [2, 1]
  614. a[2] = [-3, 2]
  615. b = np.zeros((2, 2))
  616. b[0] = [4, 5]
  617. b[1] = [-4, 0]
  618. np.min(distance.cdist(a, b), axis=1)
  619. #get_apic_dist_real(template_id, all_y, all_z, xx, yy, clf, boundary = boundary)
  620. # %%
  621. nearby_dists = np.arange(-5, 5.1, 1)
  622. # Find closest point to (ah_x, ah_y) on line
  623. b2 = ah_y + ah_x/m
  624. ah_x2 = (b2 - b)/(m + 1/m)
  625. ah_y2 = m*ah_x2 + b
  626. print(ah_x, ah_x2)
  627. # Get nearby points along line of best fit
  628. nearby_x = nearby_dists/np.sqrt(1 + m**2) + ah_x2
  629. nearby_y = m*nearby_x + b
  630. print(np.sqrt((nearby_x - ah_x2)**2 + (nearby_y - (ah_y2))**2))
  631. # Get one point one either side of each of those points along the line perpendicular to line of best fit
  632. b_vals = nearby_y + nearby_x/m
  633. opposite_dists = np.arange(-2, 2.1, 4)
  634. print(nearby_x)
  635. opposite_x = np.add.outer(opposite_dists/np.sqrt(1 + (1/m)**2), nearby_x)
  636. b_vals = np.ones(np.shape(opposite_x)) * b_vals
  637. opposite_y = -1*opposite_x / m + b_vals
  638. print(opposite_y)
  639. # %%
  640. np.shape(targets)
  641. # %%
  642. mod_coords
  643. # %%
  644. mod_targets = np.zeros((len(mod_coords),))
  645. mod_targets[:len(targets)] = targets[:]
  646. mod_targets[len(targets):np.shape(opposite_x)[1] + len(targets)] = not greater_than_category
  647. mod_targets[len(targets) + np.shape(opposite_x)[1]:np.shape(opposite_x)[1]*2 + len(targets)] = greater_than_category
  648. print(mod_targets[400:])
  649. # %%
  650. greater_than_category
  651. # %%
  652. with open(f'mag_templates_flattened_morphology_L5_TTPC1_cADpyr232_1_n300_400MEA50.npy', 'rb') as f:
  653. all_y = np.load(f)
  654. all_z = np.load(f)
  655. mags = np.load(f)
  656. elec_x, elec_y = get_electrodes2('400MEA50')
  657. # %%
  658. mags_pwr = np.sqrt(np.sum(mags[0]**2, axis=1))
  659. mags_strong_indices = np.argsort(-1*mags_pwr)[:4]
  660. mags_relative_pwr = mags_pwr[mags_strong_indices] / np.sum(mags_pwr[mags_strong_indices])
  661. new_x = np.sum(elec_x[mags_strong_indices] * mags_relative_pwr)
  662. new_y = np.sum(elec_y[mags_strong_indices] * mags_relative_pwr)
  663. print(new_x, new_y)
  664. # %%
  665. elec_x[mags_strong_indices] * mags_relative_pwr
  666. # %% [markdown]
  667. # ### IGNORE EVERYTHING BELOW THIS POINT
  668. # %%
  669. dists_list = np.array(dists_list)
  670. dists_std_list = np.array(dists_std_list)
  671. print("Mean distance: ", np.mean(dists_list))
  672. print("Mean std of dist: ", np.sqrt(np.mean(dists_std_list**2)))
  673. print("Num fails: ", fails)
  674. # %%
  675. np.sqrt(np.mean(dists_std[:, 0]**2))
  676. # %%
  677. for i in range(len(dists)):
  678. mean_total = 0
  679. var_total = 0
  680. count = 0
  681. for j in range(len(dists[0])):
  682. if dists[i, j] < 1000000:
  683. mean_total += dists[i, j]
  684. var_total += dists_std[i, j]**2
  685. count += 1
  686. print(mean_total/count, '\t', np.sqrt(var_total/count))
  687. # %%
  688. dists[0, 1] = 60
  689. dists[0, 2] = 1000000
  690. dists_std[0] = 12
  691. # %%
  692. elec_y
  693. # %%
  694. electrodes = get_electrodes('400MEA75')
  695. elec_x = electrodes.y
  696. elec_y = electrodes.z
  697. # %%
  698. len(elec_x)
  699. # %%
  700. a, b = get_electrodes2('400MEA75')
  701. # %%
  702. len(a)
  703. # %%
  704. np.arange(-1*max_coord, max_coord+1, pitch)
  705. # %%
  706. l1 = 100
  707. l2 = 300
  708. a = np.random.random(l1)
  709. b = np.random.random(l2)
  710. c = np.zeros(l1+l2)
  711. c[:l1] = a
  712. c[l1:] = b
  713. print(np.std(a))
  714. print(np.std(b))
  715. print(np.std(c))
  716. # %%
  717. print(np.sqrt((np.std(a)**2*(l1-1) + np.std(b)**2*(l2-1))/(l1+l2-2)))
  718. print(np.sqrt((np.std(a)**2*(l1) + np.std(b)**2*(l2))/(l1+l2)))
  719. # %%
  720. w = 1e-3
  721. r = 1e-3
  722. I = 10000e-9
  723. N = 100
  724. mu_r = 1000
  725. f = 1e3
  726. B_flux = w*np.log((r+w)/r)*I*2e-7
  727. print(B_flux)
  728. print(I*2e-7/(r+w/2))
  729. emf = N*mu_r*B_flux*f
  730. print(emf*1e9, 'nV')
  731. # %%

Ziad_MorphologyReconstruction4.ipynb, under CC-BY-4.0 · at the source

Overview

  1. Electrical Engineering Department, Stanford University, Stanford, California, United States of America
Institutions: Stanford University (United States)
Journal: PLoS computational biology, volume 22, issue 5, article e1014283
Dates: received 3 October 2025; accepted 29 April 2026; published online 22 May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1371/journal.pcbi.1014283 · PMID 42172273 · PMCID PMC13196990 · OpenAlex W4410256442
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: computational modeling (no new data) (modality), human (organism), computational (subfield)
Methods: Spectral & time-frequency, Statistics, Machine learning, Single-unit activity, calcium imaging
MeSH: Magnetic Fields*, Models, Neurological*, Neurons*, Action Potentials, Animals, Brain, Computational Biology, Computer Simulation, Humans (* major topic)
Topic: Neural Networks and Applications (Artificial Intelligence, Computer Science), according to OpenAlex
Funding: National Science Foundation; Stanford Bio-X; Wu Tsai Neurosciences Institute, Stanford University (SG4-19); National Science Foundation Graduate Research Fellowship Program
Citations: not cited yet (Europe PMC); 68 references in the paper

Abstract

Brain implants that measure neural magnetic fields, rather than electrical potentials, are expected to confer significant clinical advantages related to implant longevity and signal fidelity due to the elimination of the electrode-tissue interface. However, the informational differences between neural electrical potentials and magnetic fields remain poorly understood. Using a mathematical formalism based on neuronal current sources, we directly establish the complementary informational content of extracellular magnetic fields and electrical potentials. This formalism also reveals that extracellular magnetic fields generated by spiking neurons inherently exhibit one order lower spatial polarity than electric fields, resulting in more favorable distance-scaling characteristics. We then use computational modeling to illustrate how dense networks of neurons are easier to distinguish and spike sort on the basis of their magnetic, rather than electrical, spike templates. Lastly, we show how the solenoidal nature of neural magnetic fields facilitates approximate morphological reconstruction, even with sparse sensor arrays. Our findings highlight the unique experimental advantages of neural magnetic field sensing, motivating the development of compact, low-noise devices capable of meeting the stringent sensitivity requirements for cortical recordings.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.

figshare 32061105

License: CC-BY-4.0
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Size: 7 files
Software Heritage: not checked
Found in: “Data Availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NEURON (387 files), NumPy (54 files), Matplotlib (45 files), LFPy (12 files), SciPy (12 files), scikit-learn (10 files), Plotly (7 files), h5py (6 files), Elephant (4 files), Neo (4 files), SymPy (2 files), SpikeInterface (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
  • 28 September 2026: the link answers (HTTP 200)
410 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 410 scripts, each with its path and the digest of its content;
  • 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data Availability

Data and code can be accessed at https://doi.org/10.6084/m9.figshare.32061105.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Funding: added National Science Foundation; Stanford Bio-X; Wu Tsai Neurosciences Institute, Stanford University: SG4-19; National Science Foundation Graduate Research Fellowship Program

Version 1, 28 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 2 authors, 9 MeSH terms, 57 references.

Cite

This paper

Ali, Z., & Poon, A. S. Y. (2026). Spatial richness of neural magnetic fields. PLoS computational biology, 22(5), e1014283. https://doi.org/10.1371/journal.pcbi.1014283

BibTeX

@article{ali2026spatial,
author = {Ali, Ziad and Poon, Ada S Y},
title = {{Spatial richness of neural magnetic fields}},
journal = {PLoS computational biology},
year = {2026},
month = may,
volume = {22},
number = {5},
pages = {e1014283},
publisher = {PLOS},
issn = {1553-734X},
doi = {10.1371/journal.pcbi.1014283},
url = {https://doi.org/10.1371/journal.pcbi.1014283},
pmid = {42172273},
pmcid = {PMC13196990}
}

RIS

TY - JOUR
AU - Ali, Ziad
AU - Poon, Ada S Y
TI - Spatial richness of neural magnetic fields
T2 - PLoS computational biology
J2 - PLoS Comput Biol
PY - 2026
DA - 2026/05/22
VL - 22
IS - 5
SP - e1014283
SN - 1553-734X
PB - PLOS
DO - 10.1371/journal.pcbi.1014283
UR - https://doi.org/10.1371/journal.pcbi.1014283
LA - en
ER -

CSL-JSON

{
"id": "10.1371/journal.pcbi.1014283",
"type": "article-journal",
"title": "Spatial richness of neural magnetic fields",
"container-title": "PLoS computational biology",
"author": [
{
"family": "Ali",
"given": "Ziad"
},
{
"family": "Poon",
"given": "Ada S Y"
}
],
"container-title-short": "PLoS Comput Biol",
"volume": "22",
"issue": "5",
"page": "e1014283",
"DOI": "10.1371/journal.pcbi.1014283",
"PMID": "42172273",
"PMCID": "PMC13196990",
"ISSN": "1553-734X",
"publisher": "PLOS",
"URL": "https://doi.org/10.1371/journal.pcbi.1014283",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
22
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1016/j.isci.2026.115488 [code]
An integrated &lt;i&gt;i&lt;/i&gt; &lt;i&gt;n vitro&lt;/i&gt; platform and biophysical modeling approach for studying synaptic transmission in isolated neuronal pairs.
Journal: iScience
In common: LFPy, Elephant, Neo, 7 other tools, 2 references
[2] doi:10.7554/elife.110588 [code]
Opening the black box toward a modular approach to spike sorting.
Journal: eLife
In common: Neo, SpikeInterface, h5py, 4 other tools, 3 references
[3] doi:10.1093/bioinformatics/btag328 [code]
eFEL: electrophysiology feature extraction library.
Journal: Bioinformatics (Oxford, England)
In common: Neo, NEURON, h5py, 4 other tools, 2 references
[4] doi:10.1371/journal.pcbi.1014730 [code]
A unified model of short- and long-term plasticity: Effects on network connectivity and information capacity.
Journal: PLoS computational biology
In common: Elephant, Neo, SymPy, 4 other tools, computational modeling (no new data)
[5] doi:10.1016/j.patter.2026.101590 [code]
Density-based longitudinal neuron tracking in high-density electrophysiological recordings.
Journal: Patterns (New York, N.Y.)
In common: Neo, SpikeInterface, h5py, 4 other tools, 2 references
[6] doi: [code]
Naturalistic behavior and self-generated neural activity predictive of self-correction
Journal: bioRxiv : the preprint server for biology
In common: Elephant, Neo, SpikeInterface, 4 other tools
[7] doi:10.7554/elife.103046 [code]
Dichotomy between extracellular signatures of active dendritic chemical synapses and gap junctions.
Journal: eLife
In common: LFPy, NEURON, Matplotlib, 1 other tool, 3 references
[8] doi:10.1371/journal.pcbi.1014304 [code]
Linking reduced prefrontal microcircuit inhibition in schizophrenia to EEG biomarkers in silico.
Journal: PLoS computational biology
In common: LFPy, NEURON, scikit-learn, 3 other tools, 1 reference
[9] doi:10.7554/elife.110170 [code]
Efficient and reproducible pipelines for spike sorting large-scale electrophysiology data.
Journal: eLife
In common: SpikeInterface, NumPy, 5 references
[10] doi:10.1186/s12974-026-03890-4 [code]
Neuronal toll-like receptor-4 regulation of matrix metalloproteinase-9 activity mediates dentate circuit dysfunction after traumatic brain injury.
Journal: Journal of neuroinflammation
In common: Neo, SpikeInterface, h5py, 4 other tools

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.