Skip to content

Commit fe3f219

Browse files
author
nengel
committed
Added Unit tests
1 parent e516648 commit fe3f219

File tree

9 files changed

+249
-10
lines changed

9 files changed

+249
-10
lines changed

src/core_functions/shuffle_core.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
if nargin < 4
4040
msg = "not enough input arguments.";
41-
error('shuffle:notEnoughInput', msg);
41+
error('shuffle_core:notEnoughInput', msg);
4242
end
4343

4444
nObjects = size(neural_data, 2);

src/tools/binning.m

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,25 @@
138138
if length(opts.n_bins) < nVars && opts.computeOptimBins == false
139139
opts.n_bins((end+1):nVars) = repmat(opts.n_bins(end), 1, nVars - length(opts.n_bins));
140140
end
141+
if strcmp(opts.bin_method, 'userEdges')
142+
if ~isfield(opts, 'edges')
143+
error("binning.m: specify the edges in opts.edges for bin_method userEdges.");
144+
end
145+
end
146+
if strcmp(opts.bin_method, 'userEdges')
147+
if length(opts.edges) < nVars
148+
opts.edges((end+1):nVars) = repmat(opts.edges(end), 1, nVars - length(opts.edges));
149+
end
150+
end
141151
inputs_b = inputs;
142152

143153

144154
for var = 1:nVars
145155
unique_vals = unique(inputs{var})';
146-
if strcmp(opts.bin_method{var}, 'none') && size(unique_vals,2) > 100
156+
if strcmp(opts.bin_method{var}, 'none') && length(unique_vals) > 100
147157
opts.bin_method{var} = 'eqpop';
148158
opts.n_bins{var} = 100;
159+
warning('No binning procedure defined for a variable with more than 100 unique values. Binning method is set to eqpop and n_bins to 100');
149160
end
150161
if ~strcmp(opts.bin_method{var}, 'none')
151162
data = inputs{var};
@@ -171,6 +182,9 @@
171182
nbins = opts.n_bins{var};
172183
bin_method = opts.bin_method{var};
173184
nbins_length = length(nbins);
185+
if strcmp(bin_method, 'userEdges')
186+
edges = opts.edges{var};
187+
end
174188
if nbins_length ~= nDims
175189
if nbins_length == 1
176190
nbins = repmat(nbins, nDims, 1);
@@ -210,14 +224,8 @@
210224
end
211225
edg = linspace(leftEdg, rightEdg, nbins(dim)+1);
212226
edg(nbins(dim)+1) = edg(nbins(dim)+1) + 1;
213-
case 'userEdges'
214-
if ~isfield(opts, 'edges')
215-
error('binningUserEdges:Missing specified edges. edgess must be specified for var (%d) in dimension (%d).', var, dim);
216-
end
217-
edges = opts.userEdges;
218-
if ~isvector(edges) || length(edges) < 2
219-
error('binningUserEdges:InvalidEdges. At least two edges must be specified for var (%d) in dimension (%d).', var, dim);
220-
end
227+
case 'userEdges'
228+
221229
minValue = min(data_dim(:));
222230
maxValue = max(data_dim(:));
223231
if any(edges < minValue) || any(edges > maxValue)

src/tools/reduce_dim.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
%
5252
% You should have received a copy of the GNU General Public License
5353
% along with this program. If not, see <http://www.gnu.org/licenses
54+
if nargin < 1
55+
msg = 'Please input your data.';
56+
error('reduce_dim:notEnoughInput', msg);
57+
end
5458

5559
if nargin < 2
5660
warning('reduce_dim:notEnoughInput', 'Not enough input arguments. Collapsing the first dimension by default.');
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/tools/test_BiasCorrection.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
classdef test_BiasCorrection < matlab.unittest.TestCase
2+
methods (Test)
3+
4+
5+
6+
7+
end
8+
end

tests/tools/test_Binning.m

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
classdef test_Binning < matlab.unittest.TestCase
2+
3+
4+
methods (Test)
5+
6+
function testEqpop(testCase)
7+
rng('default');
8+
X = randi([1, 10],1, 100);
9+
Y = rand(2, 20, 100);
10+
opts.n_bins = {5, [6, 2]};
11+
opts.bin_method = {'eqpop'};
12+
binned_data = binning({X, Y}, opts);
13+
uniqueX = unique(binned_data{1});
14+
uniqueY_1 = unique(binned_data{1, 2}(1,:,:));
15+
uniqueY_2 = unique(binned_data{1, 2}(2,:,:));
16+
assert(length(unique(uniqueX)) == 5, 'Number of bins is incorrect');
17+
assert(length(unique(uniqueY_1)) == 6, 'Number of bins is incorrect');
18+
assert(length(unique(uniqueY_2)) == 2, 'Number of bins is incorrect');
19+
20+
X = rand(1, 200);
21+
opts.bin_method = {'none'};
22+
binned_data = binning({X}, opts);
23+
end
24+
25+
26+
function testEqspace(testCase)
27+
rng('default');
28+
X = randi([1, 10],1, 100);
29+
Y = rand(2, 20, 100);
30+
opts.n_bins = {5, [6, 2]};
31+
opts.bin_method = {'eqspace'};
32+
binned_data = binning({X, Y}, opts);
33+
uniqueX = unique(binned_data{1});
34+
uniqueY_1 = unique(binned_data{1, 2}(1,:,:));
35+
uniqueY_2 = unique(binned_data{1, 2}(2,:,:));
36+
assert(length(unique(uniqueX)) == 5, 'Number of bins is incorrect');
37+
assert(length(unique(uniqueY_1)) == 6, 'Number of bins is incorrect');
38+
assert(length(unique(uniqueY_2)) == 2, 'Number of bins is incorrect');
39+
end
40+
41+
function testUserEdges(testCase)
42+
rng('default');
43+
X = randi([1, 10],1, 100);
44+
Y = rand(2, 20, 100);
45+
opts.edges = {[1,3,5,7], [0.2, 0.6]};
46+
opts.bin_method = {'userEdges'};
47+
binned_data = binning({X, Y}, opts);
48+
uniqueX = unique(binned_data{1});
49+
uniqueY_1 = unique(binned_data{1, 2}(1,:,:));
50+
uniqueY_2 = unique(binned_data{1, 2}(2,:,:));
51+
assert(length(unique(uniqueX)) == 4, 'Number of bins is incorrect');
52+
assert(length(unique(uniqueY_1)) == 3, 'Number of bins is incorrect');
53+
assert(length(unique(uniqueY_2)) == 3, 'Number of bins is incorrect');
54+
end
55+
56+
function testScottsRule(testCase)
57+
rng('default');
58+
data = randn(100, 1);
59+
num_bins = scottsRule(data);
60+
assert(num_bins == 8, 'Test failed: unexpected output');
61+
62+
data = ones(50, 1);
63+
num_bins = scottsRule(data);
64+
assert(num_bins == 1, 'Test failed: unexpected output');
65+
end
66+
67+
function testFreedmanDiaconisRule(testCase)
68+
rng('default');
69+
data = randn(100, 1);
70+
num_bins = freedmanDiaconisRule(data);
71+
assert(num_bins == 10, 'Test failed: unexpected output');
72+
73+
data = ones(50, 1);
74+
num_bins = freedmanDiaconisRule(data);
75+
assert(num_bins == 1, 'Test failed: unexpected output');
76+
end
77+
end
78+
end

tests/tools/test_tools.m

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
classdef test_tools < matlab.unittest.TestCase
2+
3+
methods (Test)
4+
function reduceDim(testCase)
5+
testCase.verifyError(@()reduce_dim(), 'reduce_dim:notEnoughInput');
6+
7+
%Valid Input
8+
Nd_1d = [1, 2, 3, 4];
9+
expected_output_1d = Nd_1d;
10+
assert(isequal(reduce_dim(Nd_1d, 1), expected_output_1d),'Test failed: unexpected output');
11+
12+
Nd_1d = [1, 2, 1; 2, 3, 1];
13+
expected_output_1d = [3, 6, 1];
14+
assert(isequal(reduce_dim(Nd_1d, 1), expected_output_1d),'Test failed: unexpected output');
15+
end
16+
function testNonPoissonSpikeGen(testCase)
17+
testCase.verifyError(@()non_poisson_spike_gen(), 'nonpoissonspikegen:notEnoughInput');
18+
19+
%Valid Input
20+
time = 0:0.1:12;
21+
rate = 6;
22+
noise_prob = 0;
23+
spikes = non_poisson_spike_gen(time, rate, noise_prob);
24+
assert(length(spikes) == length(time), 'Test failed: Incorrect length of spikes');
25+
assert(all(ismember(spikes, [0, 1])), 'Test failed: spikes should contain only 0s and 1s');
26+
27+
time = 0:0.1:10;
28+
rate = 8;
29+
noise_prob = 0.2;
30+
spikes = non_poisson_spike_gen(time, rate, noise_prob);
31+
assert(length(spikes) == length(time), 'Test failed: Incorrect length of spikes');
32+
assert(all(ismember(spikes, [0, 1])), 'Test failed: spikes should contain only 0s and 1s');
33+
34+
%Invalid Input
35+
time = 0;
36+
rate = 6;
37+
noise_prob = 0;
38+
testCase.verifyError(@()non_poisson_spike_gen(time, rate, noise_prob), 'MATLAB:badsubscript');
39+
40+
time = NaN;
41+
testCase.verifyError(@()non_poisson_spike_gen(time, rate, noise_prob), 'nonpoissonspikegen:NaNInput');
42+
end
43+
44+
function testpoissonSpikeGen(testCase)
45+
testCase.verifyError(@()poisson_spike_gen(), 'poissonspikegen:notEnoughInput');
46+
47+
%Valid Input
48+
time = 0:0.1:12;
49+
rate = 6;
50+
noise_prob = 0;
51+
spikes = poisson_spike_gen(time, rate, noise_prob);
52+
assert(length(spikes) == length(time), 'Test failed: Incorrect length of spikes');
53+
assert(all(ismember(spikes, [0, 1])), 'Test failed: spikes should contain only 0s and 1s');
54+
55+
time = 0:0.1:10;
56+
rate = sin(time);
57+
noise_prob = 0;
58+
spikes = poisson_spike_gen(time, rate, noise_prob);
59+
assert(length(spikes) == length(time), 'Test failed: Incorrect length of spikes');
60+
assert(all(ismember(spikes, [0, 1])), 'Test failed: spikes should contain only 0s and 1s');
61+
62+
time = 0:0.1:10;
63+
rate = 8;
64+
noise_prob = 0.2;
65+
spikes = poisson_spike_gen(time, rate, noise_prob);
66+
assert(length(spikes) == length(time), 'Test failed: Incorrect length of spikes');
67+
assert(all(ismember(spikes, [0, 1])), 'Test failed: spikes should contain only 0s and 1s');
68+
69+
%Invalid input
70+
time = 0;
71+
rate = 6;
72+
noise_prob = 0;
73+
testCase.verifyError(@()poisson_spike_gen(time, rate, noise_prob), 'MATLAB:badsubscript');
74+
75+
time = NaN;
76+
testCase.verifyError(@()poisson_spike_gen(time, rate, noise_prob), 'poissonspikegen:NaNInput');
77+
end
78+
79+
80+
function testShuffle(testCase)
81+
testCase.verifyError(@()shuffle_core(), 'shuffle_core:notEnoughInput');
82+
83+
% Valid Input
84+
rng('default');
85+
behav_data = randi([1, 10], 100, 1);
86+
neural_data = rand(100, 10);
87+
consistency = 0;
88+
index = [1, 0];
89+
shuffled_data = shuffle_core(behav_data, neural_data, consistency, index);
90+
assert(~isequal(neural_data, shuffled_data), 'Data should be shuffled');
91+
nbins = 10;
92+
[counts_neural, ~] = histcounts(neural_data(:,1), nbins);
93+
[counts_shuffled, ~] = histcounts(shuffled_data(:,1), nbins);
94+
assert(isequal(counts_neural, counts_shuffled));
95+
uniqueBehav = unique(behav_data);
96+
for i = 1:length(uniqueBehav)
97+
currentBehav = uniqueBehav(i);
98+
idx = behav_data == currentBehav;
99+
currentNeuralData = neural_data(idx, :);
100+
currentShuffledData = shuffled_data(idx, :);
101+
for object_idx = 1:size(neural_data, 2)
102+
[counts_neural, ~] = histcounts(currentNeuralData(:,object_idx), nbins);
103+
[counts_shuffled, ~] = histcounts(currentShuffledData(:,object_idx), nbins);
104+
assert(~isequal(counts_neural, counts_shuffled), 'Histograms of the first dimension should differ.');
105+
end
106+
end
107+
108+
consistency = 1;
109+
shuffled_data = shuffle_core(behav_data, neural_data, consistency, index);
110+
assert(~isequal(neural_data, shuffled_data), 'Data should be shuffled');
111+
nbins = 10;
112+
[counts_neural, ~] = histcounts(neural_data(:,1), nbins);
113+
[counts_shuffled, ~] = histcounts(shuffled_data(:,1), nbins);
114+
assert(isequal(counts_neural, counts_shuffled));
115+
uniqueBehav = unique(behav_data);
116+
for i = 1:length(uniqueBehav)
117+
currentBehav = uniqueBehav(i);
118+
idx = behav_data == currentBehav;
119+
currentNeuralData = neural_data(idx, :);
120+
currentShuffledData = shuffled_data(idx, :);
121+
for object_idx = 1:size(neural_data, 2)
122+
[counts_neural, ~] = histcounts(currentNeuralData(:,object_idx), nbins);
123+
[counts_shuffled, ~] = histcounts(currentShuffledData(:,object_idx), nbins);
124+
assert(isequal(counts_neural, counts_shuffled), 'Shuffling should be consistent across behavData.');
125+
end
126+
end
127+
128+
% Test Warning
129+
lastwarn('');
130+
disp('Dont worry, this warning is part of the test ;)');
131+
behav_data = randi([1, 10], 100);
132+
neural_data = rand(100, 10);
133+
consistency = 0;
134+
index = [1, 1];
135+
shuffle_core(behav_data, neural_data, consistency, index);
136+
[warnMsg, warnId] = lastwarn;
137+
expectedWarningMsg = 'Shuffling the second dimension (neuralObjects) may alter probability distributions of individual objects.';
138+
assert(contains(warnMsg, expectedWarningMsg), 'Expected warning message was not issued.');
139+
end
140+
end
141+
end

0 commit comments

Comments
 (0)