-
Notifications
You must be signed in to change notification settings - Fork 0
/
PSESimulationCode.m
339 lines (230 loc) · 11.1 KB
/
PSESimulationCode.m
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
% Clear the workspace and close open figure windows
close all;
clear
% Shuffle the random number generator
rng('shuffle');
%-----------------------------------
% Possible parameters to change
%-----------------------------------
% Values used: 10, 25, 40 and 55
trailPerStim = 10;
% Controls the spread of the individual observer data around the population
% effects
sigma = 1;
% Number of experiments
numExps = 100;
% Number of observers per condition (final sims: 5 to 15)
numObsAll = 5:15;
numObsType = length(numObsAll);
slopeEffect = linspace(0, 0.4, numObsType);
pseCD = [cd '/PSE Trials Per Stimulus Level ' num2str(trailPerStim)];
slopeCD = [cd '/Slope Trials Per Stimulus Level ' num2str(trailPerStim)];
if exist(pseCD, 'dir') == 0
mkdir(pseCD)
end
if exist(slopeCD, 'dir') == 0
mkdir(slopeCD)
end
%-----------------------------------
% Setup variables
%-----------------------------------
% Sessions in the simulated experiment (X variable)
sessions = 0:5;
numSessions = length(sessions);
% Experimental condition properties
interceptEffect = 0;
% "No effect" control condition
slopeControl = 0;
interceptControl = 0;
% Generating control group line of no effect
noEffectControlLine = slopeControl .* sessions + interceptControl;
%--------------------------
% Fitting procedure
%--------------------------
% Psychometric funtion specification
PF = @PAL_CumulativeNormal;
% Fitting parameters (fixed gamma and lambda at zero)
gamma = 0;
lambda = 0;
freeLambda = 0;
% Alpha and Beta free to vary
paramsFree = [1 1 0 0];
% Type PAL_minimize('options','help') for help
options = PAL_minimize('options');
options.TolFun = 1e-06;
options.MaxIter = 10000;
options.MaxFunEvals = 10000;
% Number of simulations
numBoots = 3;
%--------------------------
% Fitting procedure cont
%--------------------------
% Number of steps on psychometric function (final sims: 9)
numSteps = 9;
% Range (degrees)
theRange = 10;
theHalfRange = theRange / 2;
% Setting up trials
outOfNum = ones(1, numSteps) .* trailPerStim;
%--------------------------
% Main simulation loop
%--------------------------
c = 0;
tic
% Loop over participants
for participant = 1:length(numObsAll)
% Get this number of observers in this experiment
numObs = numObsAll(participant);
% Double this for both experimental group and control
ContT = numObs*2;
% Switch point for the control group later
ContB = ContT/2+1;
% Empty matrix to filled by population sim each loop (Columns sessions,
% rows participants (top half of rows is the experimental group, bottom
% half of rows is the control group)
populationPSES = nan(ContT, numSessions);
%--------------------------
% Output matrices
%--------------------------
pseMat = nan(ContT, numSessions);
slopeMat = nan(ContT, numSessions);
% Loop over the magnitude of the effect
for Mag = 1:length(slopeEffect)
% Specify the experimental effect
slope_E = slopeEffect(Mag);
effectLine = slope_E .* sessions + interceptEffect;
% Loop over the number of experiements specified (final sims: 100)
for expNum = 1:numExps
% Loop to split the the condtions for indexing to in the matrix
% (experimental and control)
for groupNum = 1:2
% Loop over the sessions (columns of the output matrix)
for i = 1:numSessions
% Data for experimental group of simulated observer or
% the control group
if groupNum == 1
% Simulating PSES with experimental effect at
% population (this is the effect line)
popEffect = effectLine;
% Generating a variable to index off below (this is
% the effect for this session across all
% participants)
obsPSEs_E = randn(1, numObs) .* sigma + popEffect(i);
% Record the effect for all participants
populationPSES(1:numObs, i) = obsPSEs_E;
% Data for control group of simulated observers
elseif groupNum == 2
%simulating for PSES with no effect control
%at population level
popEffect = noEffectControlLine;
%Generating a varaible to index off below
obsPSEs_C = randn(1, numObs) .* sigma + popEffect(i);
%indexing the control condition PSES
populationPSES(ContB:ContT, i) = obsPSEs_C;
end
end
end
% For this magnitude of effect, for this experiment, we now
% have the population data for the experiment and control
% groups
%------------------------------------------------
% Now, we loop over all of the sessions
%------------------------------------------------
for sessionNum = 1:numSessions
% Looping ove the Population PSes specified by column
psesForThisSession = populationPSES(:, sessionNum);
% all Observers
parfor thisObsNum = 1:ContT
thisObsDone = 0;
while thisObsDone == 0
%--------------------------
% Observer properties
%--------------------------
%index of the pse for specified row of matrix above
thisObsPSE = psesForThisSession(thisObsNum);
% Stimulus levels to be used in the experiment
stimLevels = linspace(0 - theHalfRange, 0 + theHalfRange, numSteps);
% Setup slope variable
funcSigma = theRange * 0.25;
funcSlope = 1 / funcSigma;
%--------------------------
% Simulate our observer
%--------------------------
% Simulate Observer
simData = PAL_PF_SimulateObserverParametric([thisObsPSE, funcSlope, gamma, lambda],...
stimLevels, outOfNum, PF);
percRightward = simData ./ trailPerStim .* 100;
%--------------------------
% Fit a function
%--------------------------
% Parameter grid defining parameter space through which to perform a
% brute-force search for values to be used as initial guesses in iterative
% parameter search.
searchGrid = struct;
searchGrid.beta = linspace(funcSlope/4, funcSlope * 2, 20);
searchGrid.alpha = linspace(min(stimLevels), max(stimLevels), 20);
searchGrid.gamma = gamma;
searchGrid.lambda = lambda;
% Fit function using maximum likelihood
[params] = PAL_PFML_Fit(stimLevels, simData, outOfNum,...
searchGrid, paramsFree, PF, 'searchOptions', options);
% Check to see if this function is valid
[isInvalid] = validityCheck(params, stimLevels);
if isInvalid == 0
thisObsDone = 1;
end
end
% Evaluating the function
% extender = 2;
% stimLevelsFineGrain = linspace(min(stimLevels) - extender, max(stimLevels) + extender, 100);
% propCorrectModel = PF(params, stimLevelsFineGrain);
% percCorrectModel = propCorrectModel .* 100;
%
%------------------------------
% Bootstrapping for errorbars
%------------------------------
% Run the bootstrapping
% [SD, paramsSim, LLSim, converged] = PAL_PFML_BootstrapParametric(...
% stimLevels, outOfNum, params, paramsFree, numBoots, PF, ...
% 'searchGrid', searchGrid, 'searchOptions', options);
%
% Get the PSE
pse = params(1);
pseMat(thisObsNum, sessionNum) = pse;
% Get the PSE
slope = params(2);
slopeMat(thisObsNum, sessionNum) = slope;
end
end
c = c+1;
disp(['Participant ' num2str(participant) ' / ' num2str(length(numObsAll))...
'| Mag ' num2str(Mag) ' / ' num2str(length(slopeEffect))...
'| Exp ' num2str(expNum) ' / ' num2str(numExps)]);
%output to specific working directory
dlmwrite([pseCD '/PSE_ExpNum_' num2str(c) '.txt'], pseMat, '\t')
dlmwrite([slopeCD '/Slope_ExpNum_' num2str(c) '.txt'], slopeMat, '\t')
end
end
end
toc
function [isInvalid] = validityCheck(params, stimLevels)
pse = params(1);
slope = params(2);
% See if PSEs are (1) nan, (2) inf, (3) out of range
pseLessThan = pse < min(stimLevels);
pseMoreThan = pse > max(stimLevels);
pseInf = isinf(pse);
pseNaN = isnan(pse);
% See if Slopes are (1) nan, (2) inf
slopeInf = isinf(slope);
slopeNaN = isnan(slope);
% The checking vector
checkVector = pseLessThan + pseMoreThan + pseInf + pseNaN + slopeInf + slopeNaN;
numInvalid = sum(checkVector);
% Remove the bad data
if numInvalid > 0
isInvalid = 1;
else
isInvalid = 0;
end
end