Coverage for src/driada/experiment/synthetic/core.py: 65.00%

40 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-25 15:40 +0300

1""" 

2Core utilities for synthetic data generation. 

3 

4This module contains the fundamental utilities used across all synthetic data 

5generation functions, including firing rate validation and calcium signal generation. 

6""" 

7 

8import numpy as np 

9import warnings 

10from ..exp_base import * 

11from ...information.info_base import TimeSeries, MultiTimeSeries, aggregate_multiple_ts 

12 

13 

14def validate_peak_rate(peak_rate, context=""): 

15 """ 

16 Validate that peak firing rate is within physiologically realistic range. 

17  

18 Parameters 

19 ---------- 

20 peak_rate : float 

21 Peak firing rate in Hz. 

22 context : str, optional 

23 Context string for more informative warning message. 

24  

25 Notes 

26 ----- 

27 Typical firing rates for neurons: 

28 - Cortical pyramidal cells: 0.1-2 Hz (sparse firing) 

29 - Hippocampal place cells: 0.5-5 Hz (with brief peaks up to 20 Hz) 

30 - Fast-spiking interneurons: 5-50 Hz 

31  

32 For calcium imaging with GCaMP indicators: 

33 - Decay time ~1-2 seconds limits temporal resolution 

34 - Firing rates >2 Hz can cause signal saturation 

35 - Realistic modeling should use rates in 0.1-2 Hz range 

36 """ 

37 if peak_rate > 2.0: 

38 warning_msg = ( 

39 f"peak_rate={peak_rate:.1f} Hz exceeds recommended maximum of 2.0 Hz " 

40 f"for calcium imaging. " 

41 f"High firing rates can cause calcium signal saturation due to slow " 

42 f"indicator dynamics (decay time ~2s). Consider using peak_rate <= 2.0 Hz " 

43 f"for more realistic calcium traces." 

44 ) 

45 if context: 

46 warning_msg = f"{context}: {warning_msg}" 

47 warnings.warn(warning_msg, UserWarning, stacklevel=2) 

48 

49 

50def generate_pseudo_calcium_signal(events=None, 

51 duration=600, 

52 sampling_rate=20.0, 

53 event_rate=0.2, 

54 amplitude_range=(0.5,2), 

55 decay_time=2, 

56 noise_std=0.1): 

57 

58 """ 

59 Generate a pseudo-calcium imaging signal with noise. 

60 

61 Parameters: 

62 - duration: Total duration of the signal in seconds. 

63 - sampling_rate: Sampling rate in Hz. 

64 - event_rate: Average rate of calcium events per second. 

65 - amplitude_range: Tuple of (min, max) for the amplitude of calcium events. 

66 - decay_time: Time constant for the decay of calcium events in seconds. 

67 - noise_std: Standard deviation of the Gaussian noise to be added. 

68 

69 Returns: 

70 - signal: Numpy array representing the pseudo-calcium signal. 

71 """ 

72 

73 if events is None: 

74 # Calculate number of samples 

75 num_samples = int(duration * sampling_rate) 

76 

77 # Generate calcium events 

78 num_events = np.random.poisson(event_rate * duration) 

79 event_times = np.random.uniform(0, duration, num_events) 

80 event_amplitudes = np.random.uniform(amplitude_range[0], amplitude_range[1], num_events) 

81 

82 else: 

83 num_samples = len(events) 

84 event_times = np.where(events>0)[0] 

85 # Use amplitude_range to modulate event amplitudes instead of using binary values 

86 if len(event_times) > 0: 

87 event_amplitudes = np.random.uniform(amplitude_range[0], amplitude_range[1], len(event_times)) 

88 else: 

89 event_amplitudes = np.array([]) 

90 

91 # Initialize the signal with zeros 

92 signal = np.zeros(num_samples) 

93 

94 # Add calcium events to the signal 

95 for t, a in zip(event_times, event_amplitudes): 

96 if events is None: 

97 event_index = int(t * sampling_rate) 

98 else: 

99 event_index = int(t) 

100 

101 decay = np.exp(-np.arange(num_samples - event_index) / (decay_time * sampling_rate)) 

102 signal[event_index:] += a * decay 

103 

104 # Add Gaussian noise 

105 noise = np.random.normal(0, noise_std, num_samples) 

106 signal += noise 

107 

108 return signal 

109 

110 

111def generate_pseudo_calcium_multisignal(n, 

112 events=None, 

113 duration=600, 

114 sampling_rate=20, 

115 event_rate=0.2, 

116 amplitude_range=(0.5,2), 

117 decay_time=2, 

118 noise_std=0.1): 

119 """ 

120 Generate multiple pseudo calcium signals. 

121  

122 Parameters 

123 ---------- 

124 n : int 

125 Number of neurons. 

126 events : ndarray, optional 

127 Event array (n_neurons x n_timepoints). 

128 duration : float 

129 Duration in seconds. 

130 sampling_rate : float 

131 Sampling rate in Hz. 

132 event_rate : float 

133 Average rate of calcium events per second. 

134 amplitude_range : tuple 

135 (min, max) for the amplitude of calcium events. 

136 decay_time : float 

137 Time constant for the decay of calcium events in seconds. 

138 noise_std : float 

139 Standard deviation of the Gaussian noise. 

140  

141 Returns 

142 ------- 

143 ndarray 

144 Calcium signals (n_neurons x n_timepoints). 

145 """ 

146 sigs = [] 

147 for i in range(n): 

148 local_events = None 

149 if events is not None: 

150 local_events = events[i, :] 

151 

152 sig = generate_pseudo_calcium_signal(events=local_events, 

153 duration=duration, 

154 sampling_rate=sampling_rate, 

155 event_rate=event_rate, 

156 amplitude_range=amplitude_range, 

157 decay_time=decay_time, 

158 noise_std=noise_std) 

159 sigs.append(sig) 

160 

161 return np.vstack(sigs)