How to make for loops run faster - Psychtoolbox in matlab - matlab

I created a MATLAB code using Psychtoolbox to make an experiment.
Everything works as I intended but it seems the initial loading of the experiment takes too long. The task is a simple yes/no response task whether the target word('probe') appeared in the previous set of word stimuli.
I put basic intro text as an image and then wait for any keypress to start the experiment but it will take about 40 seconds to actually begin the first trial after any keystroke. I want to make it work without any delay. It should start its first trial immediately after any keystroke.
I checked the timestops with GetSecs() on numerous positions in the code and it was not anything to do with loading stimuli or initial setting of the experiment before the for loop I attached below.
To make things look simpler, I changed some of the variables into actual numbers I used. I can gurantee that it is not due to large stimuli size since it is only 1500 words. Once the for loop starts, it goes smoothly but it takes 40 seconds to actually start the first trial so I think it is something to do with a specific function in the for loop or the way I built it.
Please let me know if anything is too vague or unclear. I will do my best to make things read better.
Edit: I minimalized the code leaving only the function names used in Psychtoolbox. I left the functions I used in between loops to let you know if they could cause any delay. It will not be possible to run this without Psychtoolbox installed so I guess you can briefly examine the structure of the code.
for trial = 1:250
for i = 1:6
DrawFormattedText();
Screen();
WaitSecs(0.5);
end
DrawFormattedText();
flipTime = Screen();
WaitSecs(0.5);
DrawFormattedText();
flipTime = Screen();
rt = 0;
resp = 0;
while GetSecs - flipTime < 3
clear keyCode;
RestrictKeysForKbCheck();
[keyIsDown,secs,keyCode] = KbCheck;
respTime = GetSecs;
pressedKeys = find(keyCode);
% ESC key quits the experiment
if keyCode(KbName('ESCAPE')) == 1
clear all
close all
sca
return
end
% Check for response keys
if ~isempty(pressedKeys)
for i = 1:2
if KbName(i) == pressedKeys(1)
resp = i;
rt = respTime - flipTime;
end
end
end
% Exit loop once a response is recorded
if rt > 0
break;
end
end
if rt == 0 || rt > 3 % 3 second limit for subjects to react to the probe stimuli
DrawFormattedText();
Screen();
WaitSecs(1);
end
Screen();
vbl = Screen();
WaitSecs(1);
% Record the trial data into output data matrix
respMat{1, trial} = trial;
respMat{2, trial} = resp;
respMat{3, trial} = rt;
end

Related

Matlab: Criteria for displaying screens to subjects

I am delivering a psychology experiment in Matlab, where screens with questions will be presented to subject. The screen will also collect and display the subjects' responses. For example: Screen displays '2+3' and also displays what participant types (say, 99999), until they press enter.
GOAL: get it to stop displaying the question after 16 seconds if the participant has not yet pressed enter. (That is, stop displaying the screen if time=16sec OR if subject presses Enter.)
The problem revolves around the following code:
While CurrentTime<TimeOut
respond=GetChar() <-(Waits till user press enter)
end
So whatever the statements we add before/after capturing respond statements is not executed.
Any help on how to work around this issue would be greatly appreciated! Thanks.
Here is an example, I presented an oval as an example, you can obviously replace that with whatever your stimuli are. Enter and Return are separate keys, I wasn't sure which you're looking for, so in the example the loop looks for either.
%% include at top of experiment / block
waitForResponseSeconds = 16; % number of second to wait for a response
enterKey = KbName('enter'); % numeric code for enter key
returnKeys = KbName('return'); % numeric code for return key(s)
responseKeys = [enterKey returnKeys];
wPtr = Screen('OpenWindow', 0, 0, [0 0 400 400]);
%% within the trial loop:
hasResponded = 0;
% present the stimulus (here the window pointer is called wPtr, you may
% need to adjust this depending on what you named the window pointer.
Screen('FillOval', wPtr, [100 0 100], [0 0 400 400]);
[~, Onset] = Screen('Flip', wPtr);
while ~hasResponded && ((GetSecs - Onset) <= waitForResponseSeconds)
[keyIsDown, secs, keyCode] = KbCheck;
if any(keyCode(responseKeys))
rt = 1000.*(secs-Onset); % get response time
hasResponded = 1;
end
% Wait 1 ms before checking again to prevent
% overload of the machine at elevated priority
WaitSecs(0.001);
end
%% end of exp
sca;

KbCheck (?) unreliable in Psychtoolbox RT paradigm

I am trying to create a simple RT experiment in MATLAB R2014b, using Psychtoolbox. Participants will have to categorize faces, pressing one of two buttons as fast as they can. I created the paradigm on one computer, and it is working fine on that, but when I moved it to another (the one I want to be testing on), there was a weird error: even though the program seemed to be logging keypresses on the majority of trials, sometimes it wouldn't respond, and I had to press the key a number of times before it proceeded to the next trial. I am unsure what's going in, but I assume there could be something wrong with the computer itself (what might that be?), or with this particular bit of the code:
Screen('Flip', mainwin);
timeStart = GetSecs;keyIsDown=0; correct=0; rt=0;
while 1
bf = 0; %this variable is irrelevant here, I use it later to break
out of a loop
while (GetSecs - timeStart) < 0.2 %faces are presented briefly, but
%I'm recording responses here anyway, just in case there are some
%fast anticipatory responses - after this loop is over, I keep
%recording RT and button press the exact same way, but with no
%stimulus present
[keyIsDown, secs, keyCode] = KbCheck;
FlushEvents('keyDown');
if keyIsDown
nKeys = sum(keyCode);
if nKeys==1
if keyCode(Key1)||keyCode(Key2)
rt = 1000.*(GetSecs-timeStart);
keypressed=find(keyCode);
Screen('Flip', mainwin);
type = 'T';
bf = 1;
if keyCode(Key1) & targ_pic == 1
correct = 1;
elseif keyCode(Key2) & targ_pic == 0
correct = 1;
end
break;
elseif keyCode(escKey)
ShowCursor; fclose(outfile); Screen('CloseAll');
return
end
keyIsDown=0; keyCode=0;
end
else
keypressed = 0;
end
end
Can anyone maybe spot something that may be wrong with this?
Incidentally: is this the correct way to get RT out of PTB? I found that bit of code online, but I'm a little uncertain about why the "secs" variable is not used.
Both computers run Windows 10.
A few suggestions:
The Flip command will return the estimate of stimulus onset time, currently you are calling GetSecs after the Flip command, which is not necessary and will always return a value that is slightly later than the actual screen Flip. Likewise, you can use the time of key press returned by KbCheck, rather than calling GetSecs after identifying a key press.
I don't think you need the FlushEvents command, and it might be causing some timing variability.
It is also sometimes useful to pause for a short amount of time (for example 1 millisecond) between KbCheck events.
Below is a version of your code snippet with a few of these changes. It may also be more concise to have a single response checking loop, in which you Flip off the stimulus after 200 ms, rather than separate pre 200 ms and post 200 ms response checking loops, though I haven't made that change here.
keyIsDown=0; correct=0; rt=0;
[~, timeStart] = Screen('Flip', mainwin);
while 1
bf = 0; %this variable is irrelevant here, I use it later to break
%out of a loop
while (GetSecs - timeStart) < 0.2 %faces are presented briefly, but
%I'm recording responses here anyway, just in case there are some
%fast anticipatory responses - after this loop is over, I keep
%recording RT and button press the exact same way, but with no
%stimulus present
[keyIsDown, secs, keyCode] = KbCheck;
if keyIsDown
nKeys = sum(keyCode);
if nKeys==1
if keyCode(Key1)||keyCode(Key2)
rt = 1000.*(secs-timeStart);
keypressed=find(keyCode);
Screen('Flip', mainwin);
type = 'T';
bf = 1;
if keyCode(Key1) & targ_pic == 1
correct = 1;
elseif keyCode(Key2) & targ_pic == 0
correct = 1;
end
break;
elseif keyCode(escKey)
ShowCursor; fclose(outfile); Screen('CloseAll');
return
end
keyIsDown=0; keyCode=0;
end
else
keypressed = 0;
end
WaitSecs(.001);
end
end

Repeating trials in MatLab

i'm am very new to Matlab but really want improve. For my experiment i want to show a picture which the participant response yes/no to, using two different keys (f&g) and then the next picture is presented and it repeats so onward.
Presenting the picture, using the keys works for far, but i can't get it to repeat the trial. Thus my question is how can i get the program to repeat/loop my trial?
Is there something wrong in my code so far or is there additional coding i should use?
this is my code so far
function try1_6()
cleanupObj= onCleanup(#() myCleanupFxn);
% PRETEST
% Initialize screen with black background
winID = Screen('openWindow',0, [0 0 0]);
%Parameter
backcol=255;
textcol=0;
% Load image file(s)
structimages= [];
TheImagesdir = dir('theImagesdir/*.jpg');
for i=1: length(TheImagesdir);
TheImages = imread(['theImagesdir/' TheImagesdir(i).name], 'JPEG');
% Get width and height
imageX = size(TheImages,2);
imageY = size(TheImages,1);
% Convert to texture
myTexture = Screen('MakeTexture', winID, TheImages);
% Set destination rectangle
destRect = [50 100 50+imageX 100+imageY];
%save to structure
structimages(end+1).filename=TheImagesdir(i).name;
structimages(end).destRect= destRect;
structimages(end).texture= myTexture;
end
%Make triallist
numberOfItems= [5]; %list of all possible items
Nrepeats=4;
Response=0;
TrialList=HH_mkTrialList({numberOfItems Response},Nrepeats);
%PRESENTATION
for trialnum=1:size(TrialList,1)
nitems = TrialList(trialnum,1);
Screen('FillRect', winID,backcol); % makes the screen blank
%displays text
DrawFormattedText(winID,'dkjfghaslkdfglksdjgfh','center','center',textcol);
Screen('Flip', winID)
HH_waitForKeyPress({'space'}); % waits for spacebar to be pressed
Screen('FillRect',winID,backcol);
Screen('Flip',winID);
WaitSecs(1);
%display picture
whichTheImages= randi(length(TheImagesdir)); % randomly selects image for directory
Screen('FillRect',winID,backcol);
Screen('DrawTexture', winID, myTexture, [], destRect);
Screen('Flip', winID);
HH_waitForKeyPress({'f','j'},5)
if resp==-1
break
end
TrialList(trialnum,4)= response; %records response
end
end
function myCleanupFxn()
Screen('CloseAll')
end
There are a number of problems with you code that you need to address. First of all, TrialList is used before it is declared/initialized. The Make triallist block of code seems out of place in the body of the for loop, and should probably be placed before you loop TrialList.
Your second problem is the inner for loop that loads images. Right now, it loads every image found in the directory, on every trial! There is no reason for you to be doing this, and you should be placing this for loop outside the trial loop as well. Furthermore, your original code never worked as intended, because you never save the loaded texture anywhere; myTexture is overwritten by the last image in your folder and that's the only texture you're ever gonna get. So in addition to pre-loading the images before the loop, you need to save them in a data structure so that you can use them later in your trial loop. A simple struct will work nicely here:
structImages = [];
TheImagesdir = dir('theImagesdir/*.jpg');
for i = 1:length(TheImagesdir);
TheImages = imread(['theImagesdir/' TheImagesdir(i).name], 'JPEG');
% Get width and height
imageX = size(TheImages,2);
imageY = size(TheImages,1);
% Convert to texture
myTexture = Screen('MakeTexture', winID, TheImages);
% Set destination rectangle
destRect = [50 100 50+imageX 100+imageY];
%save to structure
structImages(end+1).filename = TheImagesdir(i).name;
structImages(end).destRect = destRect;
structImages(end).texture = myTexture;
end
There are other inconsistencies in your code:
whichTheIamges is defined but not used
resp is used in the comparison if resp==-1 but is not defined
response is saved into TrialList before it is defined
Finally, the biggest problem is Screen('CloseAll', winID); is inside the trial loop, so you tear down your whole presentation platform after the first trial.
FYI, as noted in my comment wrapping your entire script in a try block is really poor practice. I suspect you do this because you want to be able to Ctrl+C mid-task, but there's a better way to do this. If you make your entire script a function then you can use the onCleanup method to execute code whenever your function exits (whether normally, by error, or by interruption). The method goes like this:
function myScript()
%//make your script a function. There is an additional advantages to doing this:
%//function performance is better than script performance.
%//blah-blah-blah
%//setup the cleanup object before opening screen
cleanupObj = onCleanup(#() myCleanupFxn);
%//open the screen
winID = Screen('openWindow',0, [0 0 0]);
%//blah-blah-blah
end
function myCleanupFxn()
%//local function, not visible outside of this file
Screen('CloseAll');
end

Do while loops automatically suppress output from code enclosed by the while loop?

I finished my assignment and it runs the way the descriptions in the prompts say it should.
This is more of a question of sheer curiosity.
Sometimes to find out where my code might not be working I'll remove the ';' at the end of statements in the script and let it output as much as it can before it hits the error. For small scripts I'm finding this useful. I haven't learned about using proper debugging features just yet.
Anyway, I have some code for an assignment
function endValue = tradeStock(initInvest, price, buy, sell)
% given an initial investment, vector of price fluctuations (price), vector
% of lows (buy), and a vector of peaks (sell), this function will return
% the final value of said initial investment. Assumed in the process is a
% per transaction cost of $12.95
% vectorization not possible, use loops
format bank
tcost = 12.95;
purse = initInvest;
endValue = 0;
if max(size(buy))~=max(size(sell))
disp('Buy and sell vectors must be the same size')
return
end
for i = 1:max(size(buy))
stock = floor(purse/price(buy(i)));
purch = stock*price(buy(i)) + tcost;
while purse < purch
stock = stock - 1;
purch = stock*price(buy(i)) + tcost;
end
if purch <= 0
disp('You have run out of money before the market closed')
endValue = initInvest;
return
end
purse = purse - purch; % paid for stock
purse = purse + stock*price(sell(i)) - tcost; % sell stock
end
endValue = purse;
If you start out with an initial Investment that is too small, the code should straight away enter the while loop, and then wind up in the following if then statement returning you from the function and giving you back your initial Investment. And this indeed happens if you enter in an initInvest that is too small.
However, if I remove the ';' from the code enclosed by the while loop, and run it with an initInvest value that is too small, no extra values are output from within the while loop upon execution.....
Do while loops just automatically suppress output from any enclosed code?

Matlab: Print progress from parfor loop

I run a lot of long simulations in Matlab, typically taking from a couple of minutes to a couple of hours, so to speed things up I decided to run the simulations simultaneously using a parfor loop.
arglist = [arg1, arg2, arg3, arg4];
parfor ii = 1:size(arglist, 2)
myfun(arglist(ii));
end
Everything worked just fine, except for one thing: the progress printing. Since each simulation takes a lot of time, I usually print progress using something like
prevlength = 0;
for ii = 1:tot_iter
% Some calculations here
msg = sprintf('Working on %d of %d, %.2f percent done', ii, tot_iter, ii/tot_iter);
fprintf(repmat('\b', 1, prevlength))
fprintf(msg);
prevlength = numel(msg);
end
but, as could be expected, when doing this inside a parfor loop, you get chaos.
I have googled a lot in search of a solution and have found a bunch of "parfor progress printers" like this one. However, all of them print the progress of the entire parfor loop instead of showing how far each of the individual iterations have come. Since I only have about 4-8 iterations in the parfor loop, but each iteration takes about an hour, this approach isn't very helpful to me.
The ideal solution for me would be something that looks like this
Working on 127 of 10000, 1.27 percent done
Working on 259 of 10000, 2.59 percent done
Working on 3895 of 10000, 38.95 percent done
Working on 1347 of 10000, 13.47 percent done
that is, each simulation gets one line showing how far it has run. I'm not sure though if this is possible at all, I can at least not imagine any way to do this.
Another way would be to do something like this
Sim 1: 1.27% Sim 2: 2.59% Sim 3: 38.95% Sim 4: 13.47%
that is, show all the progresses on the same line. To do this, you would need to keep track of what position on the line each simulation is to write on and write there, without erasing the other progresses. I can't figure out how this would be done, is this possible to do?
If there is some other solution to my problem (showing the progress of each individual iteration) that I haven't thought of, I'd be happy to hear about it.
Since this is the first time I ask a question here on SO it is quite possible that there is something that I have missed; if so, please feel free to comment below.
Edit
After receiving this answer, I thought that I should share how I used it to solve my problem since I didn't use it exactly as in the answer, in case someone else encounters the same problem.
Here is a small test program with basically the same structure as my program, making use of the progress bar (parfor_progress) mentioned in the answer:
function parfor_progress_test()
cpus = feature('numCores');
matlabpool('open', cpus);
cleaner = onCleanup(#mycleaner);
args = [1000, 1000, 1000, 1000];
m = sum(args);
parfor_progress(m);
parfor ii = 1:size(args,2)
my_fun(args(ii));
end
parfor_progress(0);
end
function my_fun(N)
for ii = 1:N
pause(rand*0.01);
parfor_progress;
end
end
function mycleaner
matlabpool close;
fclose all;
end
Simple Progress Bar
Something like a progress bar could be done similar to this...
Before the parfor loop :
fprintf('Progress:\n');
fprintf(['\n' repmat('.',1,m) '\n\n']);
And during the loop:
fprintf('\b|\n');
Here we have m is the total number of iterations, the . shows the total number of iterations and | shows the number of iterations completed. The \n makes sure the characters are printed in the parfor loop.
Progress Bar and Percentage Completion
Otherwise you could try this: http://www.mathworks.com/matlabcentral/fileexchange/32101-progress-monitor--progress-bar--that-works-with-parfor
It will display a progress bar and percentage completion but can be easily modified to just include the percentage completion or progress bar.
This function amends a character to a file on every iteration and then reads the number of characters written to that file which indicates the number of iterations completed. This file accessing method is allowed in parfor's.
Assuming you correctly add the above to your MATLAB path somehow, you can then use the following:
arglist = [arg1, arg2, arg3, arg4];
parfor_progress(size(arglist, 2)); % Set the total number of iterations
parfor ii = 1:size(arglist, 2)
myfun(arglist(ii));
parfor_progress; % Increment the progress counter
end
parfor_progress(0); % Reset the progress counter
Time to Completion and Percentage Completion
There is also a function called showTimeToCompletion() which is available from: https://www.soundzones.com/software/sound-zone-tools/
and works alongside parfor_progress. This function allows you to print a detailed summary of the progress of a parfor loop (or any loop for that matter) which contains the start time, length of time running, estimated finish time and percentage completion. It makes smart use of the \b (backspace) character so that the command window isn't flooded with text. Although not strictly a progress 'bar' it is perhaps more informative.
The third example in the header of the function file,
fprintf('\t Completion: ');
showTimeToCompletion; startTime=tic;
len=1e2;
p = parfor_progress( len );
parfor i = 1:len
pause(1);
p = parfor_progress;
showTimeToCompletion( p/100, [], [], startTime );
end
outputs the following to the command window:
Completion: 31.00%
Remaining: 00:00:23
Total: 00:00:33
Expected Finish: 3:30:07PM 14-Nov-2017
Useful for estimating the completion of a running simulation, especially one that may take hours or days.
Starting in R2013b, you can use PARFEVAL to evaluate your function asynchronously and have the client display progress updates. (Obviously this approach is not quite as simple as adding stuff to your PARFOR loop). There's an example here.
The Diary property of the Future returned by PARFEVAL is updated continuously while processing, so that might also be useful if you have a small number of large tasks.
Starting in R2017a, you can use parallel.pool.DataQueue and afterEach to implement a waitbar for parfor, like so:
if isempty(gcp('nocreate'))
parpool('local', 3);
end
dq = parallel.pool.DataQueue;
N = 10;
wb = waitbar(0, 'Please wait...');
% Use the waitbar's UserData to track progress
wb.UserData = [0 N];
afterEach(dq, #(varargin) iIncrementWaitbar(wb));
afterEach(dq, #(idx) fprintf('Completed iteration: %d\n', idx));
parfor idx = 1:N
pause(rand());
send(dq, idx);
end
close(wb);
function iIncrementWaitbar(wb)
ud = wb.UserData;
ud(1) = ud(1) + 1;
waitbar(ud(1) / ud(2), wb);
wb.UserData = ud;
end
after exploring #Edric answer I found that there is an example in the Matlab documentation that exactly implements a waitbar for a pareval loop. Check help FetchNext
N = 100;
for idx = N:-1:1
% Compute the rank of N magic squares
F(idx) = parfeval(#rank, 1, magic(idx));
end
% Build a waitbar to track progress
h = waitbar(0, 'Waiting for FevalFutures to complete...');
results = zeros(1, N);
for idx = 1:N
[completedIdx, thisResult] = fetchNext(F);
% store the result
results(completedIdx) = thisResult;
% update waitbar
waitbar(idx/N, h, sprintf('Latest result: %d', thisResult));
end
% Clean up
delete(h)