-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtest_microtime_linearization.py
More file actions
297 lines (219 loc) · 9.6 KB
/
test_microtime_linearization.py
File metadata and controls
297 lines (219 loc) · 9.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
"""
Comprehensive tests for MicrotimeLinearization class and TTTR integration.
Tests both the MicrotimeLinearization class directly and its integration with TTTR objects.
"""
import pytest
import numpy as np
import tttrlib
class TestMicrotimeLinearizationClass:
"""Test MicrotimeLinearization class functionality"""
def test_default_constructor(self):
"""Test creating a linearizer with default constructor"""
linearizer = tttrlib.MicrotimeLinearization()
# Initially no LUTs configured
assert not linearizer.has_luts()
def test_set_channel_lut_basic(self):
"""Test setting LUT for a single channel"""
linearizer = tttrlib.MicrotimeLinearization()
# Create a simple LUT (identity mapping)
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i))
# Set LUT for channel 0
linearizer.set_channel_lut(0, lut)
assert linearizer.has_luts()
def test_set_channel_shift(self):
"""Test setting shift for a channel"""
linearizer = tttrlib.MicrotimeLinearization()
# Set shift for channel 5
linearizer.set_channel_shift(5, 10)
# Note: has_luts() only checks for LUTs, not shifts
# Shifts without LUTs are allowed
def test_lut_size_validation(self):
"""Test that all LUTs must have the same size"""
linearizer = tttrlib.MicrotimeLinearization()
# Set first LUT with 256 elements
lut256 = tttrlib.VectorFloat()
for i in range(256):
lut256.append(float(i))
linearizer.set_channel_lut(0, lut256)
# Try to set second LUT with different size - should throw exception
lut128 = tttrlib.VectorFloat()
for i in range(128):
lut128.append(float(i))
with pytest.raises(Exception): # Should throw std::invalid_argument
linearizer.set_channel_lut(1, lut128)
def test_multiple_channels_same_lut_size(self):
"""Test setting multiple channels with same LUT size"""
linearizer = tttrlib.MicrotimeLinearization()
# Set first LUT
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i))
linearizer.set_channel_lut(0, lut)
# Set second LUT with same size - should work
lut2 = tttrlib.VectorFloat()
for i in range(256):
lut2.append(float(i + 100))
linearizer.set_channel_lut(1, lut2)
assert linearizer.has_luts()
class TestMicrotimeLinearizationWithTTTR:
"""Test MicrotimeLinearization integration with TTTR objects"""
def _create_simple_tttr_data(self, n_photons=100, n_channels=8):
"""Helper to create TTTR object with test data"""
tttr = tttrlib.TTTR()
for i in range(n_photons):
macro_time = i * 10
micro_time = i % 256 # Simple pattern
channel = i % n_channels
event_type = 0
tttr.append_event(macro_time, micro_time, channel, event_type)
return tttr
def test_apply_channel_luts_basic(self):
"""Test configuring LUTs and shifts in TTTR object"""
tttr = self._create_simple_tttr_data()
# Create LUT for channel 0
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i)) # Identity LUT
# Configure LUTs and shifts
channel_luts = tttrlib.MapIntVectorFloat()
channel_luts[0] = lut
channel_shifts = tttrlib.MapSignedCharInt()
channel_shifts[0] = 5 # Shift channel 0 by 5
# Apply configuration
result = tttr.apply_channel_luts(channel_luts, channel_shifts)
assert result >= 0 # Returns number of modified events
def test_apply_luts_and_shifts_basic(self):
"""Test applying linearization and shifts"""
tttr = self._create_simple_tttr_data()
# Configure LUT for channel 0
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i)) # Identity LUT
channel_luts = tttrlib.MapIntVectorFloat()
channel_luts[0] = lut
channel_shifts = tttrlib.MapSignedCharInt()
channel_shifts[0] = 0 # No shift
# Configure and apply
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(42) # With seed
assert result >= 0 # Should succeed
def test_apply_luts_and_shifts_with_shifts_only(self):
"""Test applying only shifts (no LUTs)"""
tttr = self._create_simple_tttr_data()
# Only configure shifts, no LUTs
channel_luts = tttrlib.MapIntVectorFloat() # Empty
channel_shifts = tttrlib.MapSignedCharInt()
channel_shifts[0] = 10 # Shift channel 0
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(-1) # Use environment variable
assert result >= 0
def test_linearization_preserves_data_structure(self):
"""Test that linearization preserves TTTR data structure"""
tttr = self._create_simple_tttr_data(50)
# Store original data
original_size = len(tttr)
original_macro = np.array(tttr.get_macro_times())
original_channels = np.array(tttr.routing_channels)
# Apply identity linearization (should not change much)
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i)) # Identity
channel_luts = tttrlib.MapIntVectorFloat()
channel_luts[0] = lut
channel_shifts = tttrlib.MapSignedCharInt()
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(42)
# Verify data structure is preserved
assert result >= 0
assert len(tttr) == original_size
assert np.array_equal(original_macro, np.array(tttr.get_macro_times()))
assert np.array_equal(original_channels, np.array(tttr.routing_channels))
def test_multiple_channels_different_luts(self):
"""Test linearization with different LUTs for different channels"""
tttr = tttrlib.TTTR()
# Create data with multiple channels
n_photons = 200
for i in range(n_photons):
macro_time = i * 10
micro_time = i % 256
channel = i % 4 # Channels 0-3
event_type = 0
tttr.append_event(macro_time, micro_time, channel, event_type)
# Create different LUTs for each channel
channel_luts = tttrlib.MapIntVectorFloat()
for ch in range(4):
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i + ch * 10)) # Different offset per channel
channel_luts[ch] = lut
channel_shifts = tttrlib.MapSignedCharInt()
# Apply
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(42)
assert result >= 0
assert len(tttr) == n_photons
def test_reproducible_results_with_seed(self):
"""Test that using same seed produces consistent results"""
# Create two identical TTTR objects
tttr1 = self._create_simple_tttr_data(50)
tttr2 = self._create_simple_tttr_data(50)
# Configure identical LUTs
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i))
channel_luts = tttrlib.MapIntVectorFloat()
channel_luts[0] = lut
channel_shifts = tttrlib.MapSignedCharInt()
# Apply to both with same seed
seed = 12345
tttr1.apply_channel_luts(channel_luts, channel_shifts)
tttr2.apply_channel_luts(channel_luts, channel_shifts)
result1 = tttr1.apply_luts_and_shifts(seed)
result2 = tttr2.apply_luts_and_shifts(seed)
assert result1 >= 0
assert result2 >= 0
# Results should be identical
micro1 = np.array(tttr1.get_micro_times())
micro2 = np.array(tttr2.get_micro_times())
assert np.array_equal(micro1, micro2)
def test_environment_variable_seed(self):
"""Test using environment variable for seed"""
import os
tttr = self._create_simple_tttr_data(30)
# Set environment variable
os.environ['TTTR_RND_SEED'] = '999'
try:
# Configure and apply with default seed (should use env var)
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i))
channel_luts = tttrlib.MapIntVectorFloat()
channel_luts[0] = lut
channel_shifts = tttrlib.MapSignedCharInt()
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(-1) # -1 means use env var
assert result >= 0
finally:
# Clean up environment
if 'TTTR_RND_SEED' in os.environ:
del os.environ['TTTR_RND_SEED']
def test_large_dataset_performance(self):
"""Test linearization on larger dataset"""
tttr = self._create_simple_tttr_data(10000, 16) # 10k photons, 16 channels
# Create LUTs for all channels
channel_luts = tttrlib.MapIntVectorFloat()
for ch in range(16):
lut = tttrlib.VectorFloat()
for i in range(256):
lut.append(float(i)) # Identity
channel_luts[ch] = lut
channel_shifts = tttrlib.MapSignedCharInt()
# Apply
tttr.apply_channel_luts(channel_luts, channel_shifts)
result = tttr.apply_luts_and_shifts(42)
assert result >= 0
assert len(tttr) == 10000
if __name__ == "__main__":
pytest.main([__file__, "-v"])