08c14c476c
Adds 6 switch cases replicating the paper's linear mixed model success ~ day * tDCS + (1|subject) on the Box-B2(tDCS=1) vs Box-A2(tDCS=0) arm, reporting the days x tDCS interaction, days, and tDCS effects as t/F/p. mergeA2_full reproduces the paper's interaction (F=7.09 vs 7.12, p=0.009 vs 0.008); interpretation is sign-aware and honestly notes our data diverges from the paper (Box-B2 ahead on day 1, gap narrows -- not 'equal on day 1, accumulates'). Adds tLme tests. Suite 31/31. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.9 KiB
Matlab
64 lines
2.9 KiB
Matlab
classdef tLme < matlab.unittest.TestCase
|
|
%TLME Tests for the linear "days x tDCS" mixed model (tdcs_lme) and its
|
|
% switch/case entry. Golden values computed from this dataset; the
|
|
% mergeA2_full interaction closely reproduces the paper's F=7.12/p=0.008.
|
|
|
|
methods (Test)
|
|
|
|
function testSubjectCountsAndDayEffect(testCase)
|
|
% N (Box-A2 + Box-B2 subjects) per scenario, and the always-present
|
|
% strong learning (day) effect.
|
|
cfg = tdcs_config();
|
|
expectedN = struct('unmerged_full', 6, 'mergeA2_full', 8, 'mergeB2_full', 8);
|
|
for sc = fieldnames(expectedN)'
|
|
L = tdcs_lme(tdcs_scenario_data(sc{1}), cfg);
|
|
testCase.verifyEqual(L.nSubjects, expectedN.(sc{1}), ...
|
|
sprintf('%s: wrong subject count', sc{1}));
|
|
testCase.verifyEqual(L.day.df1, 1); % F(1) term
|
|
testCase.verifyLessThan(L.day.p, 1e-6, ...
|
|
sprintf('%s: day (learning) effect should be highly significant', sc{1}));
|
|
end
|
|
end
|
|
|
|
function testMergeA2ReplicatesPaperInteraction(testCase)
|
|
% mergeA2_full reproduces the paper's days x tDCS interaction
|
|
% (paper: F(1)=7.12, p=0.008; |t|=2.68).
|
|
cfg = tdcs_config();
|
|
L = tdcs_lme(tdcs_scenario_data('mergeA2_full'), cfg);
|
|
testCase.verifyEqual(L.interaction.df1, 1);
|
|
testCase.verifyEqual(L.interaction.F, 7.09, 'AbsTol', 0.1);
|
|
testCase.verifyEqual(L.interaction.p, 0.009, 'AbsTol', 0.002);
|
|
testCase.verifyEqual(abs(L.interaction.t), 2.66, 'AbsTol', 0.05);
|
|
% single-df term: F == t^2
|
|
testCase.verifyEqual(L.interaction.F, L.interaction.t^2, 'RelTol', 1e-6);
|
|
end
|
|
|
|
function testEffectStructShape(testCase)
|
|
cfg = tdcs_config();
|
|
L = tdcs_lme(tdcs_scenario_data('unmerged_full'), cfg);
|
|
for f = {'interaction', 'day', 'tDCS'}
|
|
e = L.(f{1});
|
|
for g = {'estimate', 'se', 't', 'df', 'p', 'F', 'df1', 'df2', 'Fp'}
|
|
testCase.verifyTrue(isfield(e, g{1}), ...
|
|
sprintf('%s missing field %s', f{1}, g{1}));
|
|
end
|
|
end
|
|
end
|
|
|
|
function testSwitchRunsAndWritesResults(testCase)
|
|
% The lme_* switch cases run end-to-end and write results/<name>.txt.
|
|
here = fileparts(fileparts(mfilename('fullpath'))); % analysis/matlab
|
|
outFile = fullfile(here, 'results', 'lme_mergeA2_full.txt');
|
|
if exist(outFile, 'file'); delete(outFile); end
|
|
evalc("tdcs_glm('lme_mergeA2_full')");
|
|
testCase.verifyTrue(exist(outFile, 'file') == 2, ...
|
|
'lme_mergeA2_full.txt was not written');
|
|
end
|
|
|
|
function testBadScenarioErrors(testCase)
|
|
testCase.verifyError(@() tdcs_glm('lme_nonsense'), 'tdcs_glm:badScenario');
|
|
end
|
|
|
|
end
|
|
end
|