bluecellulab 2.6.48__py3-none-any.whl → 2.6.50__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bluecellulab might be problematic. Click here for more details.

@@ -3,14 +3,28 @@ try:
3
3
  import efel
4
4
  except ImportError:
5
5
  efel = None
6
+ from itertools import islice
7
+ import logging
8
+ from matplotlib.collections import LineCollection
9
+ import matplotlib.pyplot as plt
10
+ import neuron
6
11
  import numpy as np
12
+ import pathlib
13
+ import seaborn as sns
7
14
 
15
+ from bluecellulab import Cell
8
16
  from bluecellulab.analysis.inject_sequence import run_stimulus
9
17
  from bluecellulab.analysis.plotting import plot_iv_curve, plot_fi_curve
18
+ from bluecellulab.analysis.utils import exp_decay
19
+ from bluecellulab.simulation import Simulation
10
20
  from bluecellulab.stimulus import StimulusFactory
21
+ from bluecellulab.stimulus.circuit_stimulus_definitions import Hyperpolarizing
11
22
  from bluecellulab.tools import calculate_rheobase
12
23
 
13
24
 
25
+ logger = logging.getLogger(__name__)
26
+
27
+
14
28
  def compute_plot_iv_curve(cell,
15
29
  injecting_section="soma[0]",
16
30
  injecting_segment=0.5,
@@ -48,7 +62,7 @@ def compute_plot_iv_curve(cell,
48
62
  post_delay (float, optional): The delay after the stimulation ends before the simulation stops
49
63
  (in ms). Default is 100.0 ms.
50
64
  threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
51
- response. Default is -30 mV.
65
+ response. Default is -20 mV.
52
66
  nb_bins (int, optional): The number of discrete current levels between 0 and the maximum current.
53
67
  Default is 11.
54
68
  rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
@@ -158,7 +172,7 @@ def compute_plot_fi_curve(cell,
158
172
  max_current (float, optional): The maximum amplitude of the injected current (in nA).
159
173
  Default is 0.8 nA.
160
174
  threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
161
- response. Default is -30 mV.
175
+ response. Default is -20 mV.
162
176
  nb_bins (int, optional): The number of discrete current levels between 0 and `max_current`.
163
177
  Default is 11.
164
178
  rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
@@ -211,3 +225,264 @@ def compute_plot_fi_curve(cell,
211
225
  output_fname=output_fname)
212
226
 
213
227
  return np.array(list_amp), np.array(spike_count)
228
+
229
+
230
+ class BPAP:
231
+ # taken from the examples
232
+
233
+ def __init__(self, cell: Cell) -> None:
234
+ self.cell = cell
235
+ self.dt = 0.025
236
+ self.stim_start = 1000
237
+ self.stim_duration = 3
238
+ self.basal_cmap = sns.color_palette("crest", as_cmap=True)
239
+ self.apical_cmap = sns.color_palette("YlOrBr_r", as_cmap=True)
240
+
241
+ @property
242
+ def start_index(self) -> int:
243
+ """Get the index of the start of the stimulus."""
244
+ return int(self.stim_start / self.dt)
245
+
246
+ @property
247
+ def end_index(self) -> int:
248
+ """Get the index of the end of the stimulus."""
249
+ return int((self.stim_start + self.stim_duration) / self.dt)
250
+
251
+ def get_recordings(self):
252
+ """Get the soma, basal and apical recordings."""
253
+ all_recordings = self.cell.get_allsections_voltagerecordings()
254
+ soma_rec = None
255
+ dend_rec = {}
256
+ apic_rec = {}
257
+ for key, value in all_recordings.items():
258
+ if "soma" in key:
259
+ soma_rec = value
260
+ elif "dend" in key:
261
+ dend_rec[key] = value
262
+ elif "apic" in key:
263
+ apic_rec[key] = value
264
+
265
+ return soma_rec, dend_rec, apic_rec
266
+
267
+ def run(self, duration: float, amplitude: float) -> None:
268
+ """Apply depolarization and hyperpolarization at the same time."""
269
+ sim = Simulation()
270
+ sim.add_cell(self.cell)
271
+ self.cell.add_allsections_voltagerecordings()
272
+ self.cell.add_step(start_time=self.stim_start, stop_time=self.stim_start + self.stim_duration, level=amplitude)
273
+ hyperpolarizing = Hyperpolarizing("single-cell", delay=0, duration=duration)
274
+ self.cell.add_replay_hypamp(hyperpolarizing)
275
+ sim.run(duration, dt=self.dt, cvode=False)
276
+
277
+ def amplitudes(self, recs) -> list[float]:
278
+ """Return amplitude across given sections."""
279
+ efel_feature_name = "maximum_voltage_from_voltagebase"
280
+ traces = [
281
+ {
282
+ 'T': self.cell.get_time(),
283
+ 'V': rec,
284
+ 'stim_start': [self.stim_start],
285
+ 'stim_end': [self.stim_start + self.stim_duration]
286
+ }
287
+ for rec in recs.values()
288
+ ]
289
+ features_results = efel.get_feature_values(traces, [efel_feature_name])
290
+ amps = [feat_res[efel_feature_name][0] for feat_res in features_results]
291
+
292
+ return amps
293
+
294
+ def distances_to_soma(self, recs) -> list[float]:
295
+ """Return the distance to the soma for each section."""
296
+ res = []
297
+ soma = self.cell.soma
298
+ for key in recs.keys():
299
+ section_name = key.rsplit(".")[-1].split("[")[0] # e.g. "dend"
300
+ section_idx = int(key.rsplit(".")[-1].split("[")[1].split("]")[0]) # e.g. 0
301
+ attribute_value = getattr(self.cell.cell.getCell(), section_name)
302
+ section = next(islice(attribute_value, section_idx, None))
303
+ # section e.g. cADpyr_L2TPC_bluecellulab_x[0].dend[0]
304
+ res.append(neuron.h.distance(soma(0.5), section(0.5)))
305
+ return res
306
+
307
+ def get_amplitudes_and_distances(self):
308
+ soma_rec, dend_rec, apic_rec = self.get_recordings()
309
+ soma_amp = self.amplitudes({"soma": soma_rec})[0]
310
+ dend_amps = None
311
+ dend_dist = None
312
+ apic_amps = None
313
+ apic_dist = None
314
+ if dend_rec:
315
+ dend_amps = self.amplitudes(dend_rec)
316
+ dend_dist = self.distances_to_soma(dend_rec)
317
+ if apic_rec:
318
+ apic_amps = self.amplitudes(apic_rec)
319
+ apic_dist = self.distances_to_soma(apic_rec)
320
+
321
+ return soma_amp, dend_amps, dend_dist, apic_amps, apic_dist
322
+
323
+ def fit(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
324
+ """Fit the amplitudes vs distances to an exponential decay function."""
325
+ from scipy.optimize import curve_fit
326
+
327
+ popt_dend = None
328
+ if dend_amps and dend_dist:
329
+ dist = [0] + dend_dist # add soma distance
330
+ amps = [soma_amp] + dend_amps # add soma amplitude
331
+ popt_dend, _ = curve_fit(exp_decay, dist, amps)
332
+
333
+ popt_apic = None
334
+ if apic_amps and apic_dist:
335
+ dist = [0] + apic_dist # add soma distance
336
+ amps = [soma_amp] + apic_amps # add soma amplitude
337
+ popt_apic, _ = curve_fit(exp_decay, dist, amps)
338
+
339
+ return popt_dend, popt_apic
340
+
341
+ def validate(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
342
+ """Check that the exponential fit is decaying."""
343
+ validated = True
344
+ notes = ""
345
+ popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
346
+ if popt_dend is None:
347
+ logger.debug("No dendritic recordings found.")
348
+ notes += "No dendritic recordings found.\n"
349
+ elif popt_dend[1] <= 0:
350
+ logger.debug("Dendritic fit is not decaying.")
351
+ validated = False
352
+ notes += "Dendritic fit is not decaying.\n"
353
+ else:
354
+ notes += "Dendritic validation passed: dendritic amplitude is decaying with distance relative to soma.\n"
355
+ if popt_apic is None:
356
+ logger.debug("No apical recordings found.")
357
+ notes += "No apical recordings found.\n"
358
+ elif popt_apic[1] <= 0:
359
+ logger.debug("Apical fit is not decaying.")
360
+ validated = False
361
+ notes += "Apical fit is not decaying.\n"
362
+ else:
363
+ notes += "Apical validation passed: apical amplitude is decaying with distance relative to soma.\n"
364
+
365
+ return validated, notes
366
+
367
+ def plot_amp_vs_dist(
368
+ self,
369
+ soma_amp,
370
+ dend_amps,
371
+ dend_dist,
372
+ apic_amps,
373
+ apic_dist,
374
+ show_figure=True,
375
+ save_figure=False,
376
+ output_dir="./",
377
+ output_fname="bpap.pdf",
378
+ ):
379
+ """Plot the results of the BPAP analysis."""
380
+ popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
381
+
382
+ outpath = pathlib.Path(output_dir) / output_fname
383
+ fig, ax1 = plt.subplots(figsize=(10, 6))
384
+ ax1.scatter([0], [soma_amp], marker="^", color='black', label='Soma')
385
+ if dend_amps and dend_dist:
386
+ ax1.scatter(
387
+ dend_dist,
388
+ dend_amps,
389
+ c=dend_dist,
390
+ cmap=self.basal_cmap,
391
+ label='Basal Dendrites',
392
+ )
393
+ if popt_dend is not None:
394
+ x = np.linspace(0, max(dend_dist), 100)
395
+ y = exp_decay(x, *popt_dend)
396
+ ax1.plot(x, y, color='darkgreen', linestyle='--', label='Basal Dendritic Fit')
397
+ if apic_amps and apic_dist:
398
+ ax1.scatter(
399
+ apic_dist,
400
+ apic_amps,
401
+ c=apic_dist,
402
+ cmap=self.apical_cmap,
403
+ label='Apical Dendrites'
404
+ )
405
+ if popt_apic is not None:
406
+ x = np.linspace(0, max(apic_dist), 100)
407
+ y = exp_decay(x, *popt_apic)
408
+ ax1.plot(x, y, color='goldenrod', linestyle='--', label='Apical Fit')
409
+ ax1.set_xlabel('Distance to Soma (um)')
410
+ ax1.set_ylabel('Amplitude (mV)')
411
+ ax1.legend()
412
+ fig.suptitle('Back-propagating Action Potential Analysis')
413
+ fig.tight_layout()
414
+ if save_figure:
415
+ fig.savefig(outpath)
416
+ if show_figure:
417
+ plt.show()
418
+
419
+ return outpath
420
+
421
+ def plot_one_axis_recordings(self, fig, ax, rec_list, dist, cmap):
422
+ """Plot the soma and dendritic recordings on one axis.
423
+
424
+ Args:
425
+ fig (matplotlib.figure.Figure): The figure to plot on.
426
+ ax (matplotlib.axes.Axes): The axis to plot on.
427
+ rec_list (list): List of recordings to plot.
428
+ dist (list): List of distances from the soma for each recording.
429
+ cmap (matplotlib.colors.Colormap): Colormap to use for the recordings.
430
+ """
431
+ time = self.cell.get_time()
432
+ line_collection = LineCollection(
433
+ [np.column_stack([time, rec]) for rec in rec_list],
434
+ array=dist,
435
+ cmap=cmap,
436
+ )
437
+ ax.set_xlim(
438
+ self.stim_start - 0.1,
439
+ self.stim_start + 30
440
+ )
441
+ ax.set_ylim(
442
+ min([min(rec[self.start_index:]) for rec in rec_list]) - 2,
443
+ max([max(rec[self.start_index:]) for rec in rec_list]) + 2
444
+ )
445
+ ax.add_collection(line_collection)
446
+ fig.colorbar(line_collection, label="soma distance (um)", ax=ax)
447
+
448
+ def plot_recordings(
449
+ self,
450
+ show_figure=True,
451
+ save_figure=False,
452
+ output_dir="./",
453
+ output_fname="bpap_recordings.pdf",
454
+ ):
455
+ """Plot the recordings from all dendrites."""
456
+ soma_rec, dend_rec, apic_rec = self.get_recordings()
457
+ dend_dist = []
458
+ apic_dist = []
459
+ if dend_rec:
460
+ dend_dist = self.distances_to_soma(dend_rec)
461
+ if apic_rec:
462
+ apic_dist = self.distances_to_soma(apic_rec)
463
+ # add soma_rec to the lists
464
+ dend_rec_list = [soma_rec] + list(dend_rec.values())
465
+ dend_dist = [0] + dend_dist
466
+ apic_rec_list = [soma_rec] + list(apic_rec.values())
467
+ apic_dist = [0] + apic_dist
468
+
469
+ outpath = pathlib.Path(output_dir) / output_fname
470
+ fig, (ax1, ax2) = plt.subplots(figsize=(10, 12), nrows=2, sharex=True)
471
+
472
+ self.plot_one_axis_recordings(fig, ax1, dend_rec_list, dend_dist, self.basal_cmap)
473
+ self.plot_one_axis_recordings(fig, ax2, apic_rec_list, apic_dist, self.apical_cmap)
474
+
475
+ # plt.setp(ax1.get_xticklabels(), visible=False)
476
+ ax1.set_title('Basal Dendritic Recordings')
477
+ ax2.set_title('Apical Dendritic Recordings')
478
+ ax1.set_ylabel('Voltage (mV)')
479
+ ax2.set_ylabel('Voltage (mV)')
480
+ ax2.set_xlabel('Time (ms)')
481
+ fig.suptitle('Back-propagating Action Potential Recordings')
482
+ fig.tight_layout()
483
+ if save_figure:
484
+ fig.savefig(outpath)
485
+ if show_figure:
486
+ plt.show()
487
+
488
+ return outpath
@@ -0,0 +1,7 @@
1
+ """Utility functions for analysis."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def exp_decay(x, a, b, c):
7
+ return a * np.exp(-b * x) + c
@@ -233,7 +233,7 @@ class BluepyCircuitAccess:
233
233
  source_popid, target_popid = zip(*pop_ids)
234
234
 
235
235
  result = result.assign(
236
- source_popid=source_popid, target_popid=target_popid
236
+ source_popid=pd.Series(source_popid), target_popid=pd.Series(target_popid)
237
237
  )
238
238
 
239
239
  if result.empty:
@@ -19,6 +19,7 @@ import pathlib
19
19
 
20
20
  import efel
21
21
 
22
+ from bluecellulab.analysis.analysis import BPAP
22
23
  from bluecellulab.analysis.analysis import compute_plot_fi_curve
23
24
  from bluecellulab.analysis.analysis import compute_plot_iv_curve
24
25
  from bluecellulab.analysis.inject_sequence import run_multirecordings_stimulus
@@ -101,9 +102,11 @@ def spiking_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
101
102
  title="Spiking Test - Step at 200% of Rheobase",
102
103
  )
103
104
 
105
+ notes = "Validation passed: Spikes detected." if passed else "Validation failed: No spikes detected."
104
106
  return {
105
- "skipped": False,
107
+ "name": "Simulatable Neuron Spiking Validation",
106
108
  "passed": passed,
109
+ "validation_details": notes,
107
110
  "figures": [outpath],
108
111
  }
109
112
 
@@ -139,20 +142,63 @@ def depolarization_block_test(cell, rheobase, out_dir):
139
142
  title="Depolarization Block Test - Step at 200% of Rheobase",
140
143
  )
141
144
 
145
+ notes = "Validation passed: No depolarization block detected." if not depol_block else "Validation failed: Depolarization block detected."
142
146
  return {
143
- "skipped": False,
147
+ "name": "Simulatable Neuron Depolarization Block Validation",
144
148
  "passed": not depol_block,
149
+ "validation_details": notes,
145
150
  "figures": [outpath],
146
151
  }
147
152
 
148
153
 
154
+ def bpap_test(cell, rheobase, out_dir="./"):
155
+ """Back-propagating action potential test: exponential fit should decay.
156
+
157
+ Args:
158
+ cell (Cell): The cell to test.
159
+ rheobase (float): The rheobase current to use for the test.
160
+ out_dir (str): Directory to save the figure.
161
+ """
162
+ amplitude = 10. * rheobase # Use 1000% of the rheobase current
163
+ bpap = BPAP(cell)
164
+ bpap.run(duration=1500, amplitude=amplitude)
165
+ soma_amp, dend_amps, dend_dist, apic_amps, apic_dist = bpap.get_amplitudes_and_distances()
166
+ validated, notes = bpap.validate(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
167
+ outpath_amp_dist = bpap.plot_amp_vs_dist(
168
+ soma_amp,
169
+ dend_amps,
170
+ dend_dist,
171
+ apic_amps,
172
+ apic_dist,
173
+ show_figure=False,
174
+ save_figure=True,
175
+ output_dir=out_dir,
176
+ output_fname="bpap.pdf"
177
+ )
178
+ outpath_recordings = bpap.plot_recordings(
179
+ show_figure=False,
180
+ save_figure=True,
181
+ output_dir=out_dir,
182
+ output_fname="bpap_recordings.pdf",
183
+ )
184
+
185
+ return {
186
+ "name": "Simulatable Neuron Back-propagating Action Potential Validation",
187
+ "validation_details": notes,
188
+ "passed": validated,
189
+ "figures": [outpath_amp_dist, outpath_recordings],
190
+ }
191
+
192
+
149
193
  def ais_spiking_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
150
194
  """AIS spiking test: axon should spike before soma."""
195
+ name = "Simulatable Neuron AIS Spiking Validation"
151
196
  # Check that the cell has an axon
152
197
  if len(cell.axonal) == 0:
153
198
  return {
154
- "skipped": True,
155
- "passed": False,
199
+ "name": name,
200
+ "passed": True,
201
+ "validation_details": "Validation skipped: Cell does not have an axon section.",
156
202
  "figures": [],
157
203
  }
158
204
 
@@ -192,22 +238,30 @@ def ais_spiking_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
192
238
  for recording in recordings:
193
239
  if recording.spike is None or len(recording.spike) == 0:
194
240
  return {
195
- "skipped": False,
241
+ "name": name,
196
242
  "passed": False,
243
+ "validation_details": "Validation failed: No spikes detected in one or both recordings.",
197
244
  "figures": [outpath1, outpath2],
198
245
  }
199
246
 
200
247
  # Check if axon spike happens before soma spike
201
248
  passed = bool(axon_recording.spike[0] < soma_recording.spike[0])
249
+ notes = (
250
+ "Validation passed: Axon spikes before soma."
251
+ if passed
252
+ else "Validation failed: Axon does not spike before soma."
253
+ )
202
254
  return {
203
- "skipped": False,
255
+ "name": name,
204
256
  "passed": passed,
257
+ "validation_details": notes,
205
258
  "figures": [outpath1, outpath2],
206
259
  }
207
260
 
208
261
 
209
262
  def hyperpolarization_test(cell, rheobase, out_dir):
210
263
  """Hyperpolarization test: hyperpolarized voltage should be lower than RMP."""
264
+ name = "Simulatable Neuron Hyperpolarization Validation"
211
265
  # Run the stimulus
212
266
  stim_factory = StimulusFactory(dt=1.0)
213
267
  step_stimulus = stim_factory.iv(threshold_current=rheobase, threshold_percentage=-40)
@@ -240,15 +294,22 @@ def hyperpolarization_test(cell, rheobase, out_dir):
240
294
  ss_voltage = features_results[0]["steady_state_voltage_stimend"][0]
241
295
  if rmp is None or ss_voltage is None:
242
296
  return {
243
- "skipped": False,
297
+ "name": name,
244
298
  "passed": False,
299
+ "validation_details": "Validation failed: Could not determine RMP or steady state voltage.",
245
300
  "figures": [outpath],
246
301
  }
247
302
  hyperpol_bool = bool(ss_voltage < rmp)
248
303
 
304
+ notes = (
305
+ f"Validation passed: Hyperpolarized voltage ({ss_voltage:.2f} mV) is lower than RMP ({rmp:.2f} mV)."
306
+ if hyperpol_bool
307
+ else f"Validation failed: Hyperpolarized voltage ({ss_voltage:.2f} mV) is not lower than RMP ({rmp:.2f} mV)."
308
+ )
249
309
  return {
250
- "skipped": False,
310
+ "name": name,
251
311
  "passed": hyperpol_bool,
312
+ "validation_details": notes,
252
313
  "figures": [outpath],
253
314
  }
254
315
 
@@ -257,15 +318,22 @@ def rin_test(rin):
257
318
  """Rin should have an acceptable biological range (< 1000 MOhm)"""
258
319
  passed = bool(rin < 1000)
259
320
 
321
+ notes = (
322
+ f"Validation passed: Input resistance (Rin) = {rin:.2f} MOhm is smaller than 1000 MOhm."
323
+ if passed
324
+ else f"Validation failed: Input resistance (Rin) = {rin:.2f} MOhm is higher than 1000 MOhm, which is not realistic."
325
+ )
260
326
  return {
261
- "skipped": False,
327
+ "name": "Simulatable Neuron Input Resistance Validation",
262
328
  "passed": passed,
329
+ "validation_details": notes,
263
330
  "figures": [],
264
331
  }
265
332
 
266
333
 
267
334
  def iv_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
268
335
  """IV curve should have a positive slope."""
336
+ name = "Simulatable Neuron IV Curve Validation"
269
337
  amps, steady_states = compute_plot_iv_curve(
270
338
  cell,
271
339
  rheobase=rheobase,
@@ -281,14 +349,21 @@ def iv_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
281
349
  # Check for positive slope
282
350
  if len(amps) < 2 or len(steady_states) < 2:
283
351
  return {
284
- "skipped": False,
352
+ "name": name,
285
353
  "passed": False,
354
+ "validation_details": "Validation failed: Not enough data points to determine slope.",
286
355
  "figures": [outpath],
287
356
  }
288
357
  slope = numpy.polyfit(amps, steady_states, 1)[0]
289
358
  passed = bool(slope > 0)
359
+ notes = (
360
+ f"Validation passed: Slope of IV curve = {slope:.2f} is positive."
361
+ if passed
362
+ else f"Validation failed: Slope of IV curve = {slope:.2f} is not positive."
363
+ )
290
364
  return {
291
- "skipped": False,
365
+ "name": name,
366
+ "validation_details": notes,
292
367
  "passed": passed,
293
368
  "figures": [outpath],
294
369
  }
@@ -296,6 +371,7 @@ def iv_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
296
371
 
297
372
  def fi_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
298
373
  """FI curve should have a positive slope."""
374
+ name = "Simulatable Neuron FI Curve Validation"
299
375
  amps, spike_counts = compute_plot_fi_curve(
300
376
  cell,
301
377
  rheobase=rheobase,
@@ -311,28 +387,38 @@ def fi_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
311
387
  # Check for positive slope
312
388
  if len(amps) < 2 or len(spike_counts) < 2:
313
389
  return {
314
- "skipped": False,
390
+ "name": name,
315
391
  "passed": False,
392
+ "validation_details": "Validation failed: Not enough data points to determine slope.",
316
393
  "figures": [outpath],
317
394
  }
318
395
  slope = numpy.polyfit(amps, spike_counts, 1)[0]
319
396
  passed = bool(slope > 0)
397
+ notes = (
398
+ f"Validation passed: Slope of FI curve = {slope:.2f} is positive."
399
+ if passed
400
+ else f"Validation failed: Slope of FI curve = {slope:.2f} is not positive."
401
+ )
320
402
  return {
321
- "skipped": False,
403
+ "name": name,
404
+ "validation_details": notes,
322
405
  "passed": passed,
323
406
  "figures": [outpath],
324
407
  }
325
408
 
326
409
 
327
- def run_validations(cell, cell_name, spike_threshold_voltage=-30):
410
+ def run_validations(
411
+ cell, cell_name, spike_threshold_voltage=-30, output_dir="./memodel_validation_figures"
412
+ ):
328
413
  """Run all the validations on the cell.
329
414
 
330
415
  Args:
331
416
  cell (Cell): The cell to validate.
332
417
  cell_name (str): The name of the cell, used in the output directory.
333
418
  spike_threshold_voltage (float): The voltage threshold for spike detection.
419
+ output_dir (str): The directory to save the validation figures.
334
420
  """
335
- out_dir = pathlib.Path("memodel_validation_figures") / cell_name
421
+ out_dir = pathlib.Path(output_dir) / cell_name
336
422
  out_dir.mkdir(parents=True, exist_ok=True)
337
423
 
338
424
  # cell = Cell.from_template_parameters(template_params)
@@ -360,10 +446,12 @@ def run_validations(cell, cell_name, spike_threshold_voltage=-30):
360
446
  depolarization_block_result = depolarization_block_test(cell, rheobase, out_dir)
361
447
 
362
448
  # Validation 3: Backpropagating AP Test
363
- # logger.debug("Running backpropagating AP test")
449
+ logger.debug("Running backpropagating AP test")
450
+ bpap_result = bpap_test(cell, rheobase, out_dir)
364
451
 
365
452
  # Validation 4: Postsynaptic Potential Test
366
453
  # logger.debug("Running postsynaptic potential test")
454
+ # We have to wait for ProbAMPANMDA_EMS to be present in entitycore to implement this test
367
455
 
368
456
  # Validation 5: AIS Spiking Test
369
457
  logger.debug("Running AIS spiking test")
@@ -377,11 +465,11 @@ def run_validations(cell, cell_name, spike_threshold_voltage=-30):
377
465
  logger.debug("Running Rin test")
378
466
  rin_result = rin_test(rin)
379
467
 
380
- # Validation 8: IV Test
468
+ # # Validation 8: IV Test
381
469
  logger.debug("Running IV test")
382
470
  iv_test_result = iv_test(cell, rheobase, out_dir, spike_threshold_voltage)
383
471
 
384
- # Validation 9: FI Test
472
+ # # Validation 9: FI Test
385
473
  logger.debug("Running FI test")
386
474
  fi_test_result = fi_test(cell, rheobase, out_dir, spike_threshold_voltage)
387
475
 
@@ -393,6 +481,7 @@ def run_validations(cell, cell_name, spike_threshold_voltage=-30):
393
481
  },
394
482
  "spiking_test": spiking_test_result,
395
483
  "depolarization_block_test": depolarization_block_result,
484
+ "bpap_test": bpap_result,
396
485
  "ais_spiking_test": ais_spiking_test_result,
397
486
  "hyperpolarization_test": hyperpolarization_result,
398
487
  "rin_test": rin_result,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluecellulab
3
- Version: 2.6.48
3
+ Version: 2.6.50
4
4
  Summary: Biologically detailed neural network simulations and analysis.
5
5
  Author: Blue Brain Project, EPFL
6
6
  License: Apache2.0
@@ -27,6 +27,7 @@ Requires-Dist: pydantic<3.0.0,>=2.5.2
27
27
  Requires-Dist: typing-extensions>=4.8.0
28
28
  Requires-Dist: networkx>=3.1
29
29
  Requires-Dist: h5py>=3.8.0
30
+ Requires-Dist: seaborn
30
31
  Dynamic: license-file
31
32
 
32
33
  |banner|
@@ -15,9 +15,10 @@ bluecellulab/type_aliases.py,sha256=DvgjERv2Ztdw_sW63JrZTQGpJ0x5uMTFB5hcBHDb0WA,
15
15
  bluecellulab/utils.py,sha256=SbOOkzw1YGjCKV3qOw0zpabNEy7V9BRtgMLsQJiFRq4,1526
16
16
  bluecellulab/verbosity.py,sha256=T0IgX7DrRo19faxrT4Xzb27gqxzoILQ8FzYKxvUeaPM,1342
17
17
  bluecellulab/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- bluecellulab/analysis/analysis.py,sha256=3gDJRau5SL6wOhAUJ16vqP7aYLtoAZ7f1qZbSUVoUU8,10818
18
+ bluecellulab/analysis/analysis.py,sha256=kZaSYCYOK3A4rrYFPfdsw6rsbjutP-cpFt_VxDx73cs,21270
19
19
  bluecellulab/analysis/inject_sequence.py,sha256=uU6Q6y0ErMDDEgdsTZBXCJ4LgwkATY7G8Fa6mmjpL98,12523
20
20
  bluecellulab/analysis/plotting.py,sha256=PqRoaZz33ULMw8A9YnZXXrxcUd84M_dwlYMTFhG7YT4,3999
21
+ bluecellulab/analysis/utils.py,sha256=eMirP557D11BuedgSqjripDxOq1haIldNbnYNetV1bg,121
21
22
  bluecellulab/cell/__init__.py,sha256=Sbc0QOsJ8E7tSwf3q7fsXuE_SevBN6ZmoCVyyU5zfII,208
22
23
  bluecellulab/cell/cell_dict.py,sha256=VE7pi-NsMVRSmo-PSdbiLYmolDOu0Gc6JxFBkuQpFdk,1346
23
24
  bluecellulab/cell/core.py,sha256=BualY0WOtC0arKmvKzQ3sQ0fhc2jrtd6wqSd9M-e25E,33797
@@ -41,7 +42,7 @@ bluecellulab/circuit/simulation_access.py,sha256=keME58gzLVAPEg2nnWD_bwEm9V2Kjeq
41
42
  bluecellulab/circuit/synapse_properties.py,sha256=TvUMiXZAAeYo1zKkus3z1EUvrE9QCIQ3Ze-jSnPSJWY,6374
42
43
  bluecellulab/circuit/validate.py,sha256=wntnr7oIDyasqD1nM-kqz1NpfWDxBGhx0Ep3e5hHXIw,3593
43
44
  bluecellulab/circuit/circuit_access/__init__.py,sha256=sgp6m5kP-pq60V1IFGUiSUR1OW01zdxXNNUJmPA8anI,201
44
- bluecellulab/circuit/circuit_access/bluepy_circuit_access.py,sha256=aXvtZR8EwpVEkB4KAupjC4DX_1xYC4OrHwMUQcQIWrQ,14273
45
+ bluecellulab/circuit/circuit_access/bluepy_circuit_access.py,sha256=m5PWSYrrkORb55HN1ZgJpQFLeSHxsNBtzyJ1n0zuVro,14295
45
46
  bluecellulab/circuit/circuit_access/definition.py,sha256=_sUU0DkesGOFW82kS1G9vki0o0aQP76z3Ltk7Vo9KK4,4435
46
47
  bluecellulab/circuit/circuit_access/sonata_circuit_access.py,sha256=tADHxVZw4VgZAz2z4NKMUwc0rd_EO40EZggw5fDhnF4,10411
47
48
  bluecellulab/circuit/config/__init__.py,sha256=aaoJXRKBJzpxxREo9NxKc-_CCPmVeuR1mcViRXcLrC4,215
@@ -65,10 +66,10 @@ bluecellulab/stimulus/stimulus.py,sha256=a_hKJUtZmIgjiFjbJf6RzUPokELqn0IHCgIWGw5
65
66
  bluecellulab/synapse/__init__.py,sha256=RW8XoAMXOvK7OG1nHl_q8jSEKLj9ZN4oWf2nY9HAwuk,192
66
67
  bluecellulab/synapse/synapse_factory.py,sha256=NHwRMYMrnRVm_sHmyKTJ1bdoNmWZNU4UPOGu7FCi-PE,6987
67
68
  bluecellulab/synapse/synapse_types.py,sha256=zs_yBvGTH4QrbQF3nEViidyq1WM_ZcTSFdjUxB3khW0,16871
68
- bluecellulab/validation/validation.py,sha256=iGOgtgpZ3Yid1VlxsCycS14a0qRkP42zhj4WFPCBX4Q,13017
69
- bluecellulab-2.6.48.dist-info/licenses/AUTHORS.txt,sha256=EDs3H-2HXBojbma10psixk3C2rFiOCTIREi2ZAbXYNQ,179
70
- bluecellulab-2.6.48.dist-info/licenses/LICENSE,sha256=dAMAR2Sud4Nead1wGFleKiwTZfkTNZbzmuGfcTKb3kg,11335
71
- bluecellulab-2.6.48.dist-info/METADATA,sha256=a7gi6zv56VtPk9dN6g2qWN926WnR3VdX-_7T1SvT2h4,8236
72
- bluecellulab-2.6.48.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
73
- bluecellulab-2.6.48.dist-info/top_level.txt,sha256=VSyEP8w9l3pXdRkyP_goeMwiNA8KWwitfAqUkveJkdQ,13
74
- bluecellulab-2.6.48.dist-info/RECORD,,
69
+ bluecellulab/validation/validation.py,sha256=GxqyMCJ2wYXVJC-3TTqpQFF_uIlfs1lq9jeJmSCuopg,17038
70
+ bluecellulab-2.6.50.dist-info/licenses/AUTHORS.txt,sha256=EDs3H-2HXBojbma10psixk3C2rFiOCTIREi2ZAbXYNQ,179
71
+ bluecellulab-2.6.50.dist-info/licenses/LICENSE,sha256=dAMAR2Sud4Nead1wGFleKiwTZfkTNZbzmuGfcTKb3kg,11335
72
+ bluecellulab-2.6.50.dist-info/METADATA,sha256=-d8pVyjbpYrjC5xmEl1c1FGNJEk3lGI6ZCa8s9FmAyE,8259
73
+ bluecellulab-2.6.50.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ bluecellulab-2.6.50.dist-info/top_level.txt,sha256=VSyEP8w9l3pXdRkyP_goeMwiNA8KWwitfAqUkveJkdQ,13
75
+ bluecellulab-2.6.50.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5