├── .gitignore ├── Instructions.html ├── README.md ├── html ├── Instructions.md ├── optimizexgui_bestx.jpg ├── optimizexgui_csv.jpg ├── optimizexgui_gui_contrastnum.jpg ├── optimizexgui_gui_contrastspec.jpg ├── optimizexgui_gui_main.jpg ├── optimizexgui_main_contrasts.jpg ├── optimizexgui_main_labeled.jpg └── optimizexgui_output.jpg ├── optimizeXGUI.m └── unused └── testcarryorder.m /.gitignore: -------------------------------------------------------------------------------- 1 | design_params*mat 2 | .DS_Store 3 | .DS_Store? 4 | ._* 5 | .Spotlight-V100 6 | .Trashes 7 | ehthumbs.db* 8 | Thumbs.db 9 | design_params* 10 | best_designs* 11 | unused/ 12 | index*md -------------------------------------------------------------------------------- /Instructions.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 11 | OptimizeXGUI Instructions 12 | 13 | 14 | 15 | 16 | 322 | 323 | 324 |
325 |

OptimizeXGUI Instructions

326 |

Here you'll learn to use a program called OptimizeXGUI.m to optimize a complex, randomized event-related design that contains multiple event types (i.e., conditions). This software is specifically designed for optimizing the detection of specific linear contrasts of interest (i.e., activation differences). Most importantly, it (hopefully) will be easy for you to use, leaving you with no excuse for not optimizing your design for your next event-related study. In addition, you should check out Tor Wager'sDesign Optimization software,which is more powerful than this software but requires more MATLAB experience and time to be able to use effectively.

327 |

To get started, go ahead and run the program in a MATLAB command window: 328 |

329 |         optimizeXGUI;               % make sure you're in the right directory
330 |

You should now see the main input dialogue:

331 | 332 |

Feel free to create your own unique design by modifying the inputs. Or, you can just use the "default" values to proceed on to the next step, which is to tell the software which contrasts you care most about:

333 | 334 |

That's it! The software will do the rest of the work, some of which you can see in the MATLAB command window:

335 | 336 |

Once it finishes, you should see a figure pop up showing you the most efficient design matrix:

337 | 338 |

In addition, you should see a new folder in the directory in which you ran the program. Inside that directory are .csv files (and .txt files, which can be read a bit easier back into MATLAB) like this:

339 | 340 |

For most applications, this should be all of the information you need to implement your experiment, and to do so in a manner that is optimal given the contrasts you care about. Of course, you might want to think about running the software for more than a minute if you do decide to use this for a study! There is no hard-and-fast rule for how long you do need to run it, but it wouldn't hurt (presumably) to run it overnight.

341 | 345 |
346 | 439 | 440 | 441 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **THIS REPOSITORY IS NO LONGER ACTIVELY MAINTAINED - USE AT YOUR OWN RISK** 2 | 3 | --- 4 | 5 | # easy-optimize-x 6 | GUI-based MATLAB software for optimizing the design of fMRI tasks. See [Help Page](http://spunt.github.io/easy-optimize-x) for usage instructions. 7 | 8 | You can use the following DOI to cite use of this software: [![DOI](https://zenodo.org/badge/21612/spunt/easy-optimize-x.svg)](https://zenodo.org/badge/latestdoi/21612/spunt/easy-optimize-x) 9 | 10 | ### to do 11 | * add m-sequencing for trial counterbalancing 12 | * look into improving GA (possibly with turboGA.m from MATLAB File Exchange) 13 | -------------------------------------------------------------------------------- /html/Instructions.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | 3 | ## About ## 4 | Here you'll learn to use a program called OptimizeXGUI.m to optimize a complex, randomized event-related design that contains multiple event types (i.e., conditions). This software is specifically designed for optimizing the detection of specific linear contrasts of interest (i.e., activation differences). Most importantly, it (hopefully) will be easy for you to use, leaving you with no excuse for not optimizing your design for your next event-related study. In addition, you should check out Tor Wager's [Design Optimization software](http://wagerlab.colorado.edu/tools), which is more powerful than this software but requires more MATLAB experience and time to be able to use effectively. 5 | 6 | ## Launching *optimizeXGUI* 7 | To get started, go ahead and run the program in a MATLAB command window (make sure you're in the right directory): 8 | ``` 9 | >> optimizeXGUI 10 | ``` 11 | You should now see the main input dialogue: 12 | 13 | ![Main Dialogue](https://raw.githubusercontent.com/spunt/easy-optimize-x/master/html/optimizexgui_main_labeled.jpg) 14 | 15 | Feel free to create your own unique design by modifying the inputs. Or, you can just use the "default" values to proceed on to the next step, which is to tell the software which contrasts you care most about: 16 | 17 | ![Main Dialogue](https://raw.githubusercontent.com/spunt/easy-optimize-x/master/html/optimizexgui_main_contrasts.jpg) 18 | 19 | That's it! The software will do the rest of the work, some of which you can see in the MATLAB command window: 20 | 21 | ![Main Dialogue](https://raw.githubusercontent.com/spunt/easy-optimize-x/master/html/optimizexgui_output.jpg) 22 | 23 | Once it finishes, you should see a figure pop up showing you the most efficient design matrix: 24 | 25 | ![Main Dialogue](https://raw.githubusercontent.com/spunt/easy-optimize-x/master/html/optimizexgui_bestx.jpg) 26 | 27 | In addition, you should see a new folder in the directory in which you ran the program. Inside that directory are .csv files (and .txt files, which can be read a bit easier back into MATLAB) like this: 28 | 29 | ![Main Dialogue](https://raw.githubusercontent.com/spunt/easy-optimize-x/master/html/optimizexgui_csv.jpg) 30 | 31 | For most applications, this should be all of the information you need to implement your experiment, and to do so in a manner that is optimal given the contrasts you care about. Of course, you might want to think about running the software for more than a minute if you do decide to use this for a study! There is no hard-and-fast rule for how long you do need to run it, but it wouldn't hurt (presumably) to run it overnight. 32 | 33 | ## Have Questions? 34 | Feel free to shoot me (Bob) an email at: bobspunt@gmail.com -------------------------------------------------------------------------------- /html/optimizexgui_bestx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_bestx.jpg -------------------------------------------------------------------------------- /html/optimizexgui_csv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_csv.jpg -------------------------------------------------------------------------------- /html/optimizexgui_gui_contrastnum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_gui_contrastnum.jpg -------------------------------------------------------------------------------- /html/optimizexgui_gui_contrastspec.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_gui_contrastspec.jpg -------------------------------------------------------------------------------- /html/optimizexgui_gui_main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_gui_main.jpg -------------------------------------------------------------------------------- /html/optimizexgui_main_contrasts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_main_contrasts.jpg -------------------------------------------------------------------------------- /html/optimizexgui_main_labeled.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_main_labeled.jpg -------------------------------------------------------------------------------- /html/optimizexgui_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spunt/easy-optimize-x/2c818675f61071356c3d7903eb3f7ab994991d04/html/optimizexgui_output.jpg -------------------------------------------------------------------------------- /optimizeXGUI.m: -------------------------------------------------------------------------------- 1 | function optimizeXGUI(params) 2 | % OPTIMIZEXGUI 3 | % 4 | % USAGE: optimizeXGUI([params]) 5 | % 6 | % Run this function at the command line to open the GUI. Once the design 7 | % search has ended, a time stamped folder will be created in the software 8 | % directory. In the folder, details regarding the most efficient designs 9 | % will be saved. This includes a .mat file with all of the design 10 | % information, as well as separate txt/csv files for the most efficient 11 | % designs. The text files will have 5 columns: 12 | % 13 | % column 1 -- trial # 14 | % column 2 -- condition 15 | % column 3 -- onsets (in secs) 16 | % column 4 -- trial duration (secs) 17 | % column 5 -- post-trial duration (secs) 18 | % 19 | % 20 | % The GUI was created using the MATLAB File Exchange contribution 21 | % "settingsdlg.m" (http://goo.gl/DFvcQ5), which is included as a 22 | % subfunction. Some of the code is lifted or adapted from software 23 | % developed by Russ Poldrack or Tor Wager. 24 | 25 | % Copyright 2014 Bob Spunt, Pasadena, California 26 | % California Institute of Technology, USA 27 | % Contact: bobspunt@gmail.com 28 | % Current Version: 20160104 29 | % 30 | if nargin==0 31 | 32 | params = getParams; 33 | 34 | elseif ~isstruct(params) 35 | 36 | if iscell(params), params = char(params); end 37 | 38 | try 39 | load(params) 40 | catch err 41 | error_handler(err); 42 | end 43 | 44 | end 45 | 46 | % ========================================================================= 47 | % Run defineX.m 48 | % ========================================================================= 49 | params = defineX(params); 50 | 51 | % ========================================================================= 52 | % Run optimizeX.m 53 | % ========================================================================= 54 | try 55 | optimizeX(params); 56 | delete(findall(0, 'Name','optimizeXGUI Progress')); 57 | catch err 58 | error_handler(err); 59 | delete(findall(0, 'Name','optimizeXGUI Progress')); 60 | end 61 | 62 | end 63 | % ========================================================================= 64 | % * 65 | % * SUBFUNCTIONS (PRIMARY) 66 | % * 67 | % ========================================================================= 68 | function params = getParams 69 | 70 | % ========================================================================= 71 | % Request Major Settings from User 72 | % ========================================================================= 73 | try 74 | % cbintervalstr = {'None', '1st/2nd Half (2)', 'Tertiles (3)', 'Quartiles (4)', 'Quintiles (5)' , 'Sextile (6)', 'Septile (7)', 'Octile (8)', 'Decile (10)', 'Hexadecile (16)'}; 75 | % cbintervalnum = [1 2 3 4 5 6 7 8 10 16]; 76 | % cbintervalstr = {'None', '1st/2nd Half (2)', 'Tertiles (3) [possibly slow]', 'Quartiles (4) [possibly very slow]'}; 77 | % cbintervalnum = [1 2 3 4]; 78 | % {'Interval Counterbalancing'; 'counterBalanceInterval'}, cbintervalstr, ... 79 | 80 | 81 | distOpt = { 82 | 'Rayleigh' 83 | 'Chisquare' 84 | 'Exponential' 85 | }; 86 | 87 | [params, button] = settingsdlg(... 88 | 'title' , 'OptimizeX Settings', ... 89 | 'separator' , 'General Settings', ... 90 | {'TR (s)'; 'TR'}, 2, ... 91 | {'High-Pass Filter Cutoff (s)'; 'hpf'}, 100, ...' 92 | 'separator' , 'Task Settings', ... 93 | {'N Conditions';'nconds'} , 4, ... 94 | {'N Trials Per Condition';'trialsPerCond'} , '25 25 25 25', ... 95 | {'Maximum Block Size'; 'maxRep'} , 3, ... 96 | 'separator' , 'Timing (s)', ... 97 | {'Trial Duration'; 'trialDur'}, 2, ... 98 | {'Mean ISI';'meanISI'} , 3, ... 99 | {'Min ISI';'minISI'} , 2, ... 100 | {'Max ISI';'maxISI'} , 6, ... 101 | {'ISI Distribution'; 'distISI'} , distOpt, ... 102 | {'Time before first trial'; 'restBegin'}, 10, ... 103 | {'Time after last trial'; 'restEnd'}, 10, ... 104 | 'separator' , 'Optimization Settings', ... 105 | {'N Designs to Save'; 'keep'}, 5, ... 106 | {'N Generations to Run';'ngen'} , 100, ... 107 | {'N Designs Per Generation';'gensize'} , 500, ... 108 | {'Max Time to Run (minutes)';'maxtime'} , .5); 109 | catch err 110 | error_handler(err); 111 | end 112 | if strcmpi(button, 'cancel') || isempty(button), return; end % canceled 113 | % params.counterBalanceInterval = cbintervalnum(strcmpi(cbintervalstr, params.counterBalanceInterval)); 114 | params.trialsPerCond = str2num(params.trialsPerCond); 115 | params.counterBalanceInterval = 0; 116 | if length(params.trialsPerCond)==1, params.trialsPerCond = repmat(params.trialsPerCond, 1, params.nconds); 117 | elseif length(params.trialsPerCond)~=params.nconds 118 | msg = sprintf('The number of entries in "N Trials Per Condition" does not match the number of conditions'); 119 | headsup(msg, 'Invalid Input', 1); 120 | optimizeXGUI 121 | end 122 | if params.minISI > params.meanISI | params.maxISI < params.meanISI 123 | msg = sprintf('The ISI values you''ve specified look odd: Min ISI cannot be greater than the Mean ISI, and the Max ISI cannot be less than the Mean ISI'); 124 | headsup(msg, 'Invalid Input', 1); 125 | optimizeXGUI 126 | end 127 | 128 | % ========================================================================= 129 | % Contrast Specification and Weighting 130 | % ========================================================================= 131 | [condata, button] = settingsdlg(... 132 | 'title' , 'Settings', ... 133 | {'How many contrasts of interest?';'ncontrast'} , 1); 134 | if strcmpi(button, 'cancel') || isempty(button), return; end 135 | vec = repmat('0 ', 1, params.nconds); 136 | all = []; 137 | for c = 1:condata.ncontrast 138 | tmp = [{{sprintf('Vector for Contrast %d', c); sprintf('con%d', c)}}, 139 | {vec}, 140 | {{sprintf('Weight for Contrast %d', c); sprintf('con%dw', c)}}, 141 | {1}]; 142 | all = [all; tmp]; 143 | end 144 | [data2, button] = settingsdlg(... 145 | 'title', 'Contrast Specification', ... 146 | all{:}); 147 | if strcmpi(button, 'cancel') || isempty(button), return; end 148 | con = struct2cell(data2); 149 | params.L = []; 150 | convec = con(1:2:end); 151 | conweight = con(2:2:end); 152 | params.L = []; 153 | params.conWeights = []; 154 | for c = 1:length(conweight) 155 | params.L = [params.L; str2num(convec{c})]; 156 | params.conWeights(c) = conweight{c}; 157 | end 158 | 159 | % ========================================================================= 160 | % Save Design Parameters 161 | % ========================================================================= 162 | [d, t] = get_timestamp; 163 | outfile = sprintf('design_params_%s_%s.mat', d, t); 164 | fprintf('\nParameters saved in %s\n', fullfile(pwd, outfile)); 165 | save(fullfile(pwd, outfile), 'params'); 166 | 167 | end 168 | function params = defineX(params) 169 | 170 | % Pulse Sequence Parameters 171 | params.nslices = 16; % number of time bins in each TR (SPM default = 16) 172 | 173 | % Derive Some Additional Parameters % 174 | params.ntrials=sum(params.trialsPerCond); % computes total number of trials 175 | params.scan_length=ceil((params.restBegin + params.restEnd + params.ntrials*(params.meanISI+params.trialDur))/params.TR); % computes total scan length (in TRs) 176 | params.TReff=params.TR/params.nslices; % computes effective TR 177 | 178 | 179 | %% Verify that valid order can be made with specified max reps %% 180 | fprintf('\nVerifying efficiency of creating valid trial orders with current parameters... '); 181 | tic; % start the timer 182 | order = []; 183 | for i = 1:params.nconds, order = [order; repmat(i, params.trialsPerCond(i), 1)]; end 184 | move_on = 0; 185 | maxtesttime = 5; 186 | while ~move_on 187 | tmp = order(randperm(params.ntrials)); 188 | nchunk = getchunks(tmp); 189 | if ~any(nchunk>params.maxRep) 190 | move_on = 1; 191 | elseif toc>=maxtesttime 192 | fprintf('FAIL\n\n'); 193 | error('Current parameters prevent efficient creation of valid trial orders. Try increasing Maximum Block Size parameter.'); 194 | end 195 | end 196 | order = tmp; 197 | % if toc>=maxtime*60, break, end 198 | fprintf('SUCCESS\n'); 199 | 200 | % Get ISI distribution % 201 | fprintf('\nCreating base distirbution of ISIs... '); 202 | minISI = params.minISI; 203 | maxISI = params.maxISI; 204 | meanISI = params.meanISI; 205 | TReff = params.TReff; 206 | mu = TReff:TReff:meanISI; 207 | jitSample = zeros(1000,length(mu)); 208 | % try 209 | % for s = 1:length(mu), jitSample(:,s) = random(params.distISI, mu(s), 1000, 1); end 210 | % catch 211 | switch lower(params.distISI) 212 | % Thanks to Jeremy Purcell for this fix for older versions of MATLAB 213 | case 'rayleigh' 214 | for s = 1:length(mu), jitSample(:,s) = raylrnd(mu(s), 1000, 1); end 215 | case 'chisquare' 216 | for s = 1:length(mu), jitSample(:,s) = chi2rnd(mu(s), 1000, 1); end 217 | case 'exponential' 218 | for s = 1:length(mu), jitSample(:,s) = exprnd(mu(s), 1000, 1); end 219 | end 220 | % end 221 | jitSample(jitSamplemaxISI) = NaN; 223 | jitdist = abs(meanISI - nanmean(jitSample)); 224 | minIDX = find(jitdist==min(jitdist)); 225 | params.jitSample = jitSample(:,minIDX(1)); 226 | params.jitSample(isnan(params.jitSample)) = []; 227 | fprintf('SUCCESS\n'); 228 | end 229 | function optimizeX(params) 230 | 231 | keep = params.keep; 232 | L = params.L; 233 | conWeights = params.conWeights; 234 | gensize = params.gensize; 235 | gensize = gensize - mod(gensize,2); % ensures gensize is divisible by 2 236 | ngen = params.ngen; 237 | maxtime = params.maxtime; 238 | nalpha = max([params.keep ceil(gensize*.01)]); 239 | halfgen = gensize/2; 240 | L(:,end+1) = 0; 241 | L = L'; 242 | ncontrasts = size(L,2); 243 | ngenbin = 10; 244 | 245 | %% Check Settings %% 246 | if size(L,1)~=params.nconds+1, error('# of columns in contrast matrix ''L'' does not equal # of conditions defined in params'); end 247 | if length(conWeights)~=size(L,2), error('# of contrast weights does not equal # of contrasts'); end 248 | 249 | %% Begin Optimization %% 250 | [d, t] = get_timestamp; 251 | fprintf('\nDesign Optimization Started %s on %s', t, d); 252 | fprintf('\n\n\tDESIGN PARAMETERS\n'); 253 | disp(params) 254 | 255 | %% Start the Progress Monitor %% 256 | progstr = sprintf('Generation %%0%dd/%d', length(num2str(ngen)), ngen); 257 | tic; % start the timer 258 | h = waitbar(1/ngen,sprintf(progstr, 1),'Name','optimizeXGUI Progress', ... 259 | 'CreateCancelBtn','setappdata(gcbf,''canceling'',1)'); 260 | setappdata(h,'canceling',0) 261 | 262 | %% Create First Generation %% 263 | fprintf(sprintf(['\n' progstr ' '], 1)); 264 | efficiency = zeros(gensize,1); 265 | order = cell(gensize,1); 266 | jitter = cell(gensize,1); 267 | genbins = floor(gensize/ngenbin:gensize/ngenbin:gensize); 268 | 269 | for i = 1:gensize 270 | 271 | d = makeX(params); 272 | X=d.X; 273 | X(:,end+1) = 1; 274 | for c = 1:ncontrasts 275 | eff(c) = 1/trace(L(:,c)'*pinv(X'*X)*L(:,c)); 276 | end 277 | efficiency(i) = eff*conWeights'; 278 | order{i} = d.combined(:,2); 279 | jitter{i} = d.combined(:,5); 280 | if ismember(i,genbins), fprintf('.'), end 281 | 282 | end 283 | fprintf(' Max Efficiency = %2.15f', max(efficiency)); 284 | maxgeneff(1) = max(efficiency); 285 | 286 | %% Loop Over Remaining Generations %% 287 | for g = 2:ngen 288 | 289 | fprintf(sprintf(['\n' progstr ' '], g)) 290 | waitbar(g/ngen, h, sprintf(progstr, g)); 291 | 292 | %% Check for Cancel button press 293 | if getappdata(h,'canceling'), delete(h); break; end 294 | 295 | %% Grab the Alphas %% 296 | tmp = sortrows([(1:length(efficiency))' efficiency], -2); 297 | fitidx = tmp(1:nalpha,1); 298 | fit.efficiency = efficiency(fitidx); 299 | fit.order = order(fitidx); 300 | fit.jitter = jitter(fitidx); 301 | 302 | %% Use the Alphas to Breed %% 303 | cross.efficiency = zeros(halfgen,1); 304 | cross.order = cell(halfgen,1); 305 | cross.jitter = cell(halfgen,1); 306 | genbins = floor(halfgen/(ngenbin/2):halfgen/(ngenbin/2):halfgen); 307 | for i = 1:halfgen 308 | 309 | %% Combine Orders %% 310 | conidx = randperm(params.nconds); 311 | orderidx = randperm(length(fit.order)); 312 | fixcon = conidx(1); 313 | varcon = conidx(2:end); 314 | calpha = fit.order{orderidx(1)}; 315 | mate = fit.order{orderidx(2)}; 316 | calpha(ismember(calpha,varcon)) = mate(ismember(mate,varcon)); 317 | d=makeX(params, calpha); 318 | X=d.X; 319 | X(:,end+1) = 1; 320 | for c = 1:ncontrasts 321 | eff(c) = 1/trace(L(:,c)'*pinv(X'*X)*L(:,c)); 322 | end 323 | cross.efficiency(i) = eff*conWeights'; 324 | cross.order{i} = d.combined(:,2); 325 | cross.jitter{i} = d.combined(:,5); 326 | if ismember(i,genbins), fprintf('.'), end 327 | 328 | end 329 | 330 | %% Check for Cancel button press 331 | if getappdata(h,'canceling'), delete(h); break; end 332 | 333 | %% Introduce Some Nasty Mutants %% 334 | if g>2 && maxgeneff(g-1)==maxgeneff(g-2) 335 | mutsize = gensize; 336 | else 337 | mutsize = halfgen; 338 | end 339 | mut.efficiency = zeros(mutsize,1); 340 | mut.order = cell(mutsize,1); 341 | mut.jitter = cell(mutsize,1); 342 | genbins = floor(mutsize/(ngenbin/2):mutsize/(ngenbin/2):mutsize); 343 | for i = 1:mutsize 344 | d=makeX(params); 345 | X=d.X; 346 | X(:,end+1) = 1; 347 | for c = 1:ncontrasts 348 | eff(c) = 1/trace(L(:,c)'*pinv(X'*X)*L(:,c)); 349 | end 350 | mut.efficiency(i) = eff*conWeights'; 351 | mut.order{i} = d.combined(:,2); 352 | mut.jitter{i} = d.combined(:,5); 353 | if ismember(i,genbins), fprintf('.'), end 354 | end 355 | 356 | %% Combine this Genertation and Compute Max Efficiency %% 357 | efficiency = [fit.efficiency; cross.efficiency; mut.efficiency]; 358 | order = [fit.order; cross.order; mut.order]; 359 | jitter = [fit.jitter; cross.jitter; mut.jitter]; 360 | fprintf(' Max Efficiency = %2.15f', max(efficiency)); 361 | maxgeneff(g) = max(efficiency); 362 | 363 | %% Break if Over Time %% 364 | if toc>=maxtime*60, break, end 365 | 366 | %% Check for Cancel button press 367 | if getappdata(h,'canceling'), delete(h); break; end 368 | 369 | end 370 | delete(h); 371 | 372 | %% Save Best Designs %% 373 | [d, t] = get_timestamp; 374 | outdir = sprintf('best_designs_%s_%s', d, t); mkdir(outdir); 375 | fprintf('\n\nSaving %d best designs to: %s\n', keep, fullfile(pwd, outdir)); 376 | tmp = sortrows([(1:length(efficiency))' efficiency], -2); 377 | fitidx = tmp(1:keep,1); 378 | best.efficiency = efficiency(fitidx); 379 | best.order = order(fitidx); 380 | best.jitter = jitter(fitidx); 381 | design = cell(keep,1); 382 | for i = 1:keep 383 | design{i}=breedX(params, best.order{i}, best.jitter{i}); 384 | fname = [outdir filesep 'design' num2str(i) '.txt']; 385 | dlmwrite(fname, design{i}.combined, 'delimiter', '\t') 386 | fname2 = [outdir filesep 'design' num2str(i) '.csv']; 387 | cc = [{'Trial' 'Condition' 'Onset' 'Duration' 'ISI'}; num2cell(design{i}.combined)]; 388 | writedesign(cc, fname2); 389 | end 390 | save([outdir filesep 'designinfo.mat'], 'design', 'params'); 391 | fprintf('Finished in %2.2f minutes at %s on %s\n\n', toc/60, t, d); 392 | 393 | %% Visualize the Best Design 394 | 395 | hfig = figure('color', 'white', 'units', 'norm', 'pos', [.3 .3 .3 .4]); 396 | winner = scalemat(design{1}.X); 397 | winner = [winner ones(size(winner,1), 1)]; 398 | h = imagesc(winner, 'parent', gca); colormap('gray'); 399 | set(gca, 'FontName', 'Arial', 'FontSize', 18, 'XTick', 1:size(winner,2)); 400 | xticklabel = strcat({'cond'}, get(gca, 'XTickLabel')); 401 | xticklabel{end} = 'constant'; 402 | set(gca, 'xticklabel', xticklabel); 403 | ylabel('TR', 'fontsize', 18, 'fontweight', 'bold'); 404 | title('The "Best" Design Matrix', 'fontsize', ceil(18*1.10), 'fontweight', 'bold'); 405 | 406 | end 407 | function design = makeX(params, order, verbose) 408 | if nargin < 3, verbose = 0; end 409 | if nargin < 2 410 | makeorder = 1; 411 | elseif isempty(order) 412 | makeorder = 1; 413 | else 414 | makeorder = 0; 415 | end 416 | 417 | %----------------------------------------------------------------- 418 | % Get a pseudoexponential distribution of ISIs 419 | %----------------------------------------------------------------- 420 | goodJit=0; 421 | while goodJit==0 422 | jitters=randsample(params.jitSample,params.ntrials-1,1); 423 | if mean(jitters) < params.meanISI+params.TReff && mean(jitters) > params.meanISI-params.TReff 424 | goodJit=1; 425 | end 426 | end 427 | 428 | %----------------------------------------------------------------- 429 | % Determine stimulus onset times 430 | %----------------------------------------------------------------- 431 | onset=zeros(1,params.ntrials); 432 | onset(1)=params.restBegin; 433 | for t=2:params.ntrials, 434 | onset(t)=onset(t-1) + params.trialDur + jitters(t-1); 435 | end; 436 | jitters(end+1)=params.restEnd; 437 | 438 | %----------------------------------------------------------------- 439 | % Make some trial orders 440 | %----------------------------------------------------------------- 441 | if makeorder 442 | if params.counterBalanceInterval 443 | order = make_order(params.trialsPerCond, params.maxRep, params.counterBalanceInterval); 444 | else 445 | order = []; 446 | for i = 1:params.nconds, order = [order; repmat(i, params.trialsPerCond(i), 1)]; end 447 | move_on = 0; 448 | while ~move_on 449 | tmp = order(randperm(params.ntrials)); 450 | nchunk = getchunks(tmp); 451 | if ~any(nchunk>params.maxRep), move_on = 1; end 452 | end 453 | order = tmp; 454 | end 455 | end 456 | %------------------------------------------------------------------------ 457 | % Create the design matrix (oversample the HRF depending on effective TR) 458 | %------------------------------------------------------------------------ 459 | cond=order; 460 | oversamp_rate=params.TR/params.TReff; 461 | dmlength=params.scan_length*oversamp_rate; 462 | oversamp_onset=(onset/params.TR)*oversamp_rate; 463 | hrf=spm_hrf(params.TReff); 464 | desmtx=zeros(dmlength,params.nconds); 465 | for c=1:params.nconds 466 | r=zeros(1,dmlength); 467 | cond_trials= cond==c; 468 | cond_ons=fix(oversamp_onset(cond_trials))+1; 469 | r(cond_ons)=1; 470 | cr=conv(r,hrf); 471 | desmtx(:,c)=cr(1:dmlength)'; 472 | onsets{c}=onset(cond==c); % onsets in actual TR timescale 473 | end; 474 | % sample the design matrix back into TR timescale 475 | desmtx=desmtx(1:oversamp_rate:dmlength,:); 476 | 477 | %------------------------------------------------------------------------ 478 | % Filter the design matrix 479 | %------------------------------------------------------------------------ 480 | K.RT = params.TR; 481 | K.HParam = params.hpf; 482 | K.row = 1:length(desmtx); 483 | K = spm_filter(K); 484 | for c=1:params.nconds 485 | desmtx(:,c)=spm_filter(K,desmtx(:,c)); 486 | end 487 | 488 | %------------------------------------------------------------------------ 489 | % Save the design matrix 490 | %------------------------------------------------------------------------ 491 | design.X = desmtx; 492 | design.combined=zeros(params.ntrials,5); 493 | design.combined(:,1)=1:params.ntrials; 494 | design.combined(:,2)=cond; 495 | design.combined(:,3)=onset; 496 | design.combined(:,4)=repmat(params.trialDur,params.ntrials,1); 497 | design.combined(:,5)=jitters; 498 | design.duration=(params.scan_length*params.TR)/60; 499 | end 500 | function design = breedX(params, order, jitters) 501 | % USAGE: design = breedX(params, order, jitters) 502 | if nargin<3, display('USAGE: design = breedX(params, order, jitters)'), return, end 503 | 504 | %----------------------------------------------------------------- 505 | % Determine stimulus onset times 506 | %----------------------------------------------------------------- 507 | onset=zeros(1,params.ntrials); 508 | onset(1)=params.restBegin; 509 | for t=2:params.ntrials, 510 | onset(t)=onset(t-1) + params.trialDur + jitters(t-1); 511 | end; 512 | 513 | %------------------------------------------------------------------------ 514 | % Create the design matrix (oversample the HRF depending on effective TR) 515 | %------------------------------------------------------------------------ 516 | cond=order; 517 | oversamp_rate=params.TR/params.TReff; 518 | dmlength=params.scan_length*oversamp_rate; 519 | oversamp_onset=(onset/params.TR)*oversamp_rate; 520 | hrf=spm_hrf(params.TReff); 521 | desmtx=zeros(dmlength,params.nconds); 522 | for c=1:params.nconds 523 | r=zeros(1,dmlength); 524 | cond_trials= cond==c; 525 | cond_ons=fix(oversamp_onset(cond_trials))+1; 526 | r(cond_ons)=1; 527 | cr=conv(r,hrf); 528 | desmtx(:,c)=cr(1:dmlength)'; 529 | onsets{c}=onset(cond==c); % onsets in actual TR timescale 530 | end; 531 | % sample the design matrix back into TR timescale 532 | desmtx=desmtx(1:oversamp_rate:dmlength,:); 533 | 534 | %------------------------------------------------------------------------ 535 | % Filter the design matrix 536 | %------------------------------------------------------------------------ 537 | K.RT = params.TR; 538 | K.HParam = params.hpf; 539 | K.row = 1:length(desmtx); 540 | K = spm_filter(K); 541 | for c=1:params.nconds 542 | desmtx(:,c)=spm_filter(K,desmtx(:,c)); 543 | end 544 | 545 | %------------------------------------------------------------------------ 546 | % Save the design matrix 547 | %------------------------------------------------------------------------ 548 | design.X = desmtx; 549 | design.combined=zeros(params.ntrials,5); 550 | design.combined(:,1)=1:params.ntrials; 551 | design.combined(:,2)=cond; 552 | design.combined(:,3)=onset; 553 | design.combined(:,4)=repmat(params.trialDur,params.ntrials,1); 554 | design.combined(:,5)=jitters; 555 | design.duration=(params.scan_length*params.TR)/60; 556 | end 557 | % ========================================================================= 558 | % * 559 | % * SUBFUNCTIONS (ORDERING) 560 | % * 561 | % ========================================================================= 562 | function ordervec = make_order(condtrials, maxrep, cbinterval, cborder) 563 | % MAKE_ORDER Make trial order w/optional counterbalancing 564 | % 565 | % USAGE: [ordervec, orderbinmat] = make_order(condtrials, maxrep, [cbinterval], [cborder]) 566 | % 567 | % ARGUMENTS 568 | % condtrials = vector whose length corresponds to the number of 569 | % conditions and whose values correspond to the number of trials per 570 | % condition. For example, [10 10 15] indicates that there are 3 571 | % conditions, with conditions 1 and 2 featuring 10 trials and 572 | % condition 3 featuring 15 trials. 573 | % maxrep = scalar that indicate the maximum number of times a trial 574 | % is allowed to be repeated on consecutive trials. Use to control the 575 | % extent to which trials from the same condition can be blocked. 576 | % cbinterval = counterbalancing across equal intervals 577 | % 0 or 1 (default) = none 578 | % > 1 = will divide into equal intervals of the specified size, e.g., 579 | % 2 will counterbalance across first and second half 580 | % 4 will counterbalance across quartiles 581 | % cborder = order of m-seq counterbalancing (0 for none) 582 | % 583 | 584 | % --------------------------------------- Copyright (C) 2014 --------------------------------------- 585 | % Author: Bob Spunt 586 | % Affilitation: Caltech 587 | % Email: spunt@caltech.edu 588 | % 589 | % $Revision Date: Aug_20_2014 590 | if nargin < 2, error('USAGE: [ordervec, orderbinmat] = make_order(condtrials, maxrep, [cbinterval], [cborder])'); end 591 | if nargin < 3, cbinterval = 0; end 592 | if nargin < 4, cborder = 0; end 593 | if maxrep==1, error('maxrep must be greater than 1'); end 594 | ncond = length(condtrials); 595 | ntrials = sum(condtrials); 596 | ntrialsmax = ncond*max(condtrials); 597 | if cbinterval > 1 598 | qt = 0:(1/cbinterval):1; 599 | goodn = [floor(condtrials/cbinterval); ceil(condtrials/cbinterval)]; 600 | qidx1 = ceil(quantile(1:ntrials, qt(1:end-1))); 601 | qidx2 = [qidx1(2:end)-1 ntrials]; 602 | end 603 | if cborder 604 | seqrep = 0; 605 | tmp = 0; 606 | while length(tmp) < ntrialsmax 607 | seqrep = seqrep + 1; 608 | tmp = carryoverCounterbalance(ncond, cborder, seqrep); 609 | end 610 | move_on = 0; 611 | while ~move_on 612 | move_on = 1; 613 | seq = carryoverCounterbalance(ncond, cborder, seqrep)'; 614 | for i = 1:ncond 615 | condseq = find(seq==i); 616 | seq(condseq(condtrials(i)+1:end)) = NaN; 617 | end 618 | tmp = seq(~isnan(seq)); 619 | if any(getchunks(tmp) > maxrep), move_on = 0; continue; end 620 | if cbinterval > 1 621 | orderbinmat = repmat(tmp, 1, ncond)==repmat(1:ncond, ntrials, 1); 622 | for i = 1:cbinterval 623 | if ~all(sum(repmat(sum(orderbinmat(qidx1(i):qidx2(i), :)), size(goodn, 1), 1)==goodn)) 624 | move_on = 0; continue; end 625 | end 626 | end 627 | end 628 | ordervec = tmp; 629 | else 630 | ordervec = []; 631 | for i = 1:ncond, ordervec = [ordervec; repmat(i, condtrials(i), 1)]; end 632 | move_on = 0; 633 | while ~move_on 634 | move_on = 1; 635 | tmp = ordervec(randperm(ntrials)); 636 | if any(getchunks(tmp) > maxrep), move_on = 0; continue; end 637 | if cbinterval > 1 638 | orderbinmat = repmat(tmp, 1, ncond)==repmat(1:ncond, ntrials, 1); 639 | for i = 1:cbinterval 640 | if ~all(sum(repmat(sum(orderbinmat(qidx1(i):qidx2(i), :)), size(goodn, 1), 1)==goodn)) 641 | move_on = 0; continue; end 642 | end 643 | end 644 | end 645 | ordervec = tmp; 646 | end 647 | end 648 | function order = make_order_old(condtrials, maxrep, cborder) 649 | if nargin < 3, error('USAGE: order = make_order(condtrials, maxrep, cborder)'); end 650 | if maxrep==1, error('maxrep must be greater than 1'); end 651 | ncond = length(condtrials); 652 | ntrials = sum(condtrials); 653 | seqrep = ceil(ntrials/length(carryoverCounterbalance(ncond, cborder, 1))); 654 | move_on = 0; 655 | while ~move_on 656 | seq = carryoverCounterbalance(ncond, cborder, seqrep); 657 | for i = 1:ncond 658 | condseq = find(seq==i); 659 | seq(condseq(condtrials(i)+1:end)) = NaN; 660 | end 661 | tmp = seq(~isnan(seq)); 662 | if ~any(getchunks(tmp) > maxrep), move_on = 1; end 663 | end 664 | order = tmp; 665 | end 666 | function condSequence = carryoverCounterbalance(numConds,cbOrder,reps) 667 | % CARRYOVERCOUNTERBALANCE 668 | % 669 | % USAGE: condSequence = carryoverCounterbalance(numConds,cbOrder,reps) 670 | % 671 | % This is a minimally modified version of software written by Joseph Brooks 672 | % and described in the following paper which should be cited if use 673 | % counterbalancing: 674 | % 675 | % Brooks, J.L. (2012). Counterbalancing for serial order carryover effects 676 | % in experimental condition orders. Psychological Methods. Vol 17(4), Dec 677 | % 2012, 600-614. doi.org/10.1037/a0029310 678 | % 679 | %__________________________________________________________________________ 680 | % Creates a serial-order counterbalanced sequence of conditions, i.e. it 681 | % ensures that every condition is preceded equally often by every other 682 | % condition (first-order) or every nTuple of length n for nth-order 683 | % counterbalancing. This can be done for any number of conditions. Uses 684 | % Kandel et al. 1996 method to determine Euler path (see accessory 685 | % functions included below). 686 | % 687 | % OUTPUT: -condSequence: the counterbalanced sequence. Length will depend 688 | % on parameters. Output as an array of numbers 689 | % 690 | % INPUTS: 691 | % -numConds: N unique conditions. Must be a positive integer 692 | % 693 | % -cbOrder: depth/order of counterbalancing. Must be a positive integer. 694 | % First-order counterbalancing ensures that every condition is preceded 695 | % by every other condition equally often. nth-order counterbalancing 696 | % ensures that every condition is preceded equally often by every 697 | % n-length sequence (nTuple) of conditions. e.g. 2nd-order 698 | % counterbalancing would ensure that condition 1 is preceded equally 699 | % often by each set of 2 conditions, 1 1, 1 2, 2 1, 2 2. 700 | % 701 | % -reps: The number of times each condition is preceded by every other 702 | % condition (first-order) or every nTuple (nth-order). Must be a positive 703 | % integer. This can be used to increase the total number of trials while 704 | % maintaining counterbalancing. 705 | % 706 | % -omitSelfAdjacencies: 0 = include condition self-adjacencies (e.g. 2 2 707 | % 1 2 1 1 2); 1 = exclude condition self-adjacencies (e.g. 1 2 1 2 1 2) 708 | % 709 | % VERSION: 1.0.01.03.2012 710 | % Joseph Brooks, UCL Institute of Cognitive Neuroscience brooks.jl@gmail.com 711 | % 712 | omitSelfAdjacencies = 0; 713 | if nargin<1, error('USAGE: condSequence = carryoverCounterbalance(numConds,cbOrder,rep)'); end 714 | if nargin<2, cbOrder = 1; end 715 | if nargin<3, reps = 2; end 716 | if reps < 1, error('ERROR: reps parameter must be > 0'); end 717 | if cbOrder < 1, error('ERROR: cbOrder parameter must be > 0'); end 718 | if numConds < 1, error('ERROR: numConds parameter must be > 0'); end 719 | if omitSelfAdjacencies < 0 || omitSelfAdjacencies > 1, 720 | error('ERROR: omitSelfAdjacencies parameter must be 0 or 1'); 721 | end; 722 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 723 | % CONSTRUCT ADJACENCY MATRIX FOR GRAPH WITH APPROPRIATE TEMPORAL 724 | % RELATIONSHIPS 725 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 726 | if cbOrder == 1 727 | %%%Construct adjacency matrix and condition list for cbOrder == 1 728 | %%%Fully-connected graph 729 | adjacencyMatrix = ones(numConds,numConds)*reps; 730 | nTuples = [1:numConds]; 731 | if omitSelfAdjacencies 732 | adjacencyMatrix = adjacencyMatrix - adjacencyMatrix.*eye(size(adjacencyMatrix)); 733 | end; 734 | else 735 | %%%CONSTRUCT N-TUPLE CORRESPONDING TO EACH NODE WHEN cbOrder > 1 736 | nTuples = nTuples_brooks(numConds,cbOrder); 737 | 738 | %If indicated in input parameters, remove nodes with self-adjacencies by putting zeros in 739 | %corresponding columns and rows of adjacency matrix 740 | if omitSelfAdjacencies 741 | nTuples = nTuples_removeSelfAdjacencies_brooks(nTuples); 742 | end 743 | 744 | %%%Construct adjacency matrix by connecting only those nTuples 745 | %%%which share the (n-1)-length-beginning/(n-1)-length-end of their sequences 746 | adjacencyMatrix = zeros(size(nTuples,1),size(nTuples,1)); 747 | for tminus1 = 1:size(adjacencyMatrix,1) 748 | for t = 1:size(adjacencyMatrix,2) 749 | if nTuples(tminus1,2:size(nTuples,2)) == nTuples(t,1:size(nTuples,2)-1) 750 | adjacencyMatrix(tminus1,t) = 1; 751 | end 752 | end 753 | end 754 | 755 | %%%Duplicate edges based on number of reps requested 756 | adjacencyMatrix = adjacencyMatrix*reps; 757 | end 758 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 759 | %%%FIND EULER PATH THROUGH GRAPH SPECIFIED BY ADJACENCY MATRIX%%%Uses Kandel et al 1996 method for Euler Circuit 760 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 761 | nodeSequence = eulerPathKandelMethod_carryoverCounterbalance(adjacencyMatrix); 762 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 763 | %%%CONSTRUCT CONDITION SEQUENCE FROM NODE SEQUENCE. 764 | %%%-For first-order counterbalancing node sequence = cond sequence 765 | %%%-For higher-order counterbalancing, overlapping parts of sequence 766 | %%%must be considered and only one additional condition from the 767 | %%%nTuple will be added for all nodes beyond the first 768 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 769 | if cbOrder == 1 770 | condSequence = nodeSequence; 771 | else 772 | condSequence = nTuples(nodeSequence(1),:); 773 | for i = 2:length(nodeSequence) 774 | condSequence = [condSequence nTuples(nodeSequence(i),size(nTuples,2))]; 775 | end 776 | end; 777 | end 778 | function [seq] = eulerPathKandelMethod_carryoverCounterbalance(adjacencyMatrix) 779 | % Finds an Euler circuit through a graph, G, and returns sequence of nodes 780 | % associated with that path. Based on method by Kandel, et al. (1996). 781 | % The circuit/path created is uniformly drawn from the set of all possible 782 | % Eulerian circuits of G. 783 | % 784 | % USAGE: 785 | % [seq] = eulerPathKandelMethod(adjacencyMatrix) 786 | % OUTPUT: seq is a ordered sequence of nodes corresponding to the circuit 787 | % through G. 788 | % 789 | % INPUT: adjacency matrix 790 | % Option1: If entering a single integer, n, then program assumes a 791 | % fully-connected digraph with n nodes and 1 instance of the edge 792 | % between each node. 793 | % Option2: If entering a matrix then the matrix will be 794 | % treated as the adjacency matrix of the graph, G. Must be an nxn matrix for 795 | % a graph with n nodes Dimension1 = represents the 796 | % node sending the directed edge, e.g. G(1,2) is an entry 797 | % representing a directed edge from node 1 to node 2. 798 | % 799 | % - adjacency matrix must be consistent with an Eulerian graph, 800 | % i.e., all nodes have in-degree = out-degree. 801 | % Semi-Eulerian graphs are not valid for this program. 802 | % - all entries in the adjacency matrix must be >= 0. Values 803 | % larger than one indicate duplicated instances of an edge between 804 | % the nodes. 805 | % 806 | % References: 807 | % Kandel, D., Matias, Y., Unger, R., & Winkler, P. (1996). Shuffling 808 | % biological sequences. Discrete Applied Mathematics, 71(1-3), 171-185: 809 | % 810 | % VERSION: 1.0.01.03.2012 811 | % by Joseph Brooks, UCL Institute of Cognitive Neuroscience 812 | % brooks.jl@gmail.com%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 813 | %%% CHECK INPUT & GIVE ERROR MESSAGES IF NOT APPROPRIATE 814 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 815 | %%%If input is integer then create adjacency matrix for fully connected 816 | %%%digraph with one instance of each edge 817 | if size(adjacencyMatrix,1) == 1 818 | adjacencyMatrix = ones(adjacencyMatrix,adjacencyMatrix); 819 | end 820 | %%%Is matrix square? 821 | if size(adjacencyMatrix,1) ~= size(adjacencyMatrix,2) 822 | error('ERROR: Adjacency matrix is not square'); 823 | end; 824 | %%%Is matrix 2D? 825 | if length(size(adjacencyMatrix)) > 2 826 | error('ERROR: Adjacency matrix should have only 2 dimensions'); 827 | end; 828 | %%% Is graph Eulerian? Error if not. Error if semi-Eulerian 829 | for i = 1:size(adjacencyMatrix,1) 830 | if sum(adjacencyMatrix(:,i)) ~= sum(adjacencyMatrix(i,:)) 831 | error('ERROR: non_Eulerian graph specfied. In-degree must equal out-degree at every vertex'); 832 | end; 833 | end 834 | %%% Does each vertex have at least one edge? 835 | for i = 1:size(adjacencyMatrix,1) 836 | if sum(adjacencyMatrix(:,i)) == 0 && sum(adjacencyMatrix(i,:)) == 0 837 | error('ERROR: Disconnected graph...at least one vertex has no edges.'); 838 | end; 839 | end 840 | %%%Are all values >= 0 841 | if min(min(adjacencyMatrix)) < 0 842 | error('ERROR: Adjacency matrix contains value < 0'); 843 | end; 844 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 845 | %%% START COMPUTATION 846 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 847 | %%%Uniformly draw arborescence from the set of all possible for the input graph 848 | %%%arb output is adjacency matrix for an arborescence of the input 849 | arb = arborescenceKandel_carryoverCounterbalance(adjacencyMatrix); 850 | %The following matrix indicates which out edges are NOT included in the 851 | %arborescence by subtracting the arborescence from the adjacency matrix. 852 | %These edges need to be randomly permuted in the outEdges list 853 | remainingEdges = adjacencyMatrix - arb; 854 | %%%For each node create a list of outgoing edges and randomly order them 855 | %%%except for any edge that is part of the arborescence. Any outgoing edges 856 | %%%that are in the arborescence should be last in the order 857 | outEdgesOrder = cell(size(adjacencyMatrix,1),1);for o = 1:size(adjacencyMatrix,1) 858 | %%%In the cell corresponding to node n, list all out edges from this 859 | %%%node including duplicate out edges as muliple instances in the list. 860 | for i = 1:size(adjacencyMatrix,2) 861 | for r = 1:remainingEdges(o,i) 862 | outEdgesOrder{o} = [outEdgesOrder{o} i]; 863 | end 864 | end 865 | 866 | %%%Randomly shuffle the out edges for this node 867 | outEdgesOrder{o} = outEdgesOrder{o}(randperm(length(outEdgesOrder{o}))); 868 | 869 | %%%Add arborescence edge to end of list if it exists 870 | if sum(arb(o,:)) > 0 871 | outEdgesOrder{o} = [outEdgesOrder{o} find(arb(o,:) == 1)]; 872 | end 873 | end 874 | %%%Set first node in sequence to the root of the arboresence (node 875 | %%%toward which all edges in arb are pointing) 876 | seq = find(sum(arb,2) == 0); 877 | %%% Generate sequence of nodes by starting at the root node of the arb 878 | %%% matrix and take the out edge from that node with the lowest number. 879 | %%% Add the target node of this out edge to the sequence and Remove the out 880 | %%% edge from the list for the source node. Repeat treating the target node 881 | %%% as the new source node until all edges have been traversed. 882 | while sum(cellfun('length',outEdgesOrder)) > 0 883 | seq = [seq outEdgesOrder{seq(end)}(1)]; 884 | 885 | if length(outEdgesOrder{seq(end-1)}) > 1 886 | outEdgesOrder{seq(end-1)} = outEdgesOrder{seq(end-1)}(2:end); 887 | else 888 | outEdgesOrder{seq(end-1)} = []; 889 | end 890 | end 891 | end 892 | function arb = arborescenceKandel_carryoverCounterbalance(adjacencyMatrix) 893 | %%% FIND UNIFORMLY-DRAWN ARBORESCENCE for the graph specified by the adjacency matrix 894 | %%% in the input. Do this by performing a backward random walk 895 | %%% on the graph...based on Kandel et al. (1996) 896 | %%% 897 | %%% INPUT: Adjacency Matrix of the graph. A square matrix of values >= 0 898 | %%% where a positive integer in Aij indicates an edge from vertex i to 899 | %%% vertex j. The size of each dimension of the matrix is equal to the 900 | %%% number of vertices of the graph 901 | %%% 902 | %%% OUTPUT: Adjacency Matrix of the arborescence. 903 | %%% 904 | %%% KANDEL ALGORITHM 905 | %%% (1) start at random node 906 | %%% (2) randomly choose an edge to cross (loops which connect back to the same node can be ignored) 907 | %%% (3) if this edge leads to a node that has not been visited then 908 | %%% this edge is part of the arborescence. For the edge's origin node, 909 | %%% it should be marked as the last edge to be traversed out of the 910 | %%% node when creating the sequence. 911 | %%% in outEdgeOrder matrix to make it the last traversed, i.e. size-of-matrix+1%%% (4) select an edge out and move to another node. 912 | %%% (5) repeated 2-4 until each node has been visited at least once. 913 | %%% 914 | %%% VERSION: 1.0.01.03.2012 915 | %%% by Joseph Brooks, UCL Institute of Cognitive Neuroscience 916 | %%% brooks.jl@gmail.com 917 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 918 | %%%CHECK INPUT%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 919 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 920 | %%%Is matrix square? 921 | if size(adjacencyMatrix,1) ~= size(adjacencyMatrix,2) 922 | error('ERROR: Adjacency matrix is not square'); 923 | end; 924 | %%%Is matrix 2D? 925 | if length(size(adjacencyMatrix)) > 2 926 | error('ERROR: Adjacency matrix should have only 2 dimensions'); 927 | end; 928 | %%% Does each vertex have at least one edge? 929 | for i = 1:size(adjacencyMatrix,1) 930 | if sum(adjacencyMatrix(:,i)) == 0 && sum(adjacencyMatrix(i,:)) == 0 931 | error('ERROR: At least one vertex is disconnected (i.e. has no edges)'); 932 | end; 933 | end 934 | %%%Are all values >= 0 935 | if min(min(adjacencyMatrix)) < 0 936 | error('ERROR: Adjacency matrix contains value < 0'); 937 | end; 938 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 939 | %%%COMPUTE ARBORESCENCE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 940 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 941 | %%%Create empty adjacency matrix to represent output arborescence 942 | arb = zeros(size(adjacencyMatrix)); 943 | %%%Simplify input adjacency matrix by removing duplicate edges. The result 944 | %%%will treat duplicate edges as equivalent. 945 | adjacencyMatrix = sign(adjacencyMatrix); 946 | %%%Choose a random starting vertex and add it to the list of nodes visited 947 | currentNode = randi(size(adjacencyMatrix,1)); 948 | nodesVisited = [currentNode]; 949 | while length(unique(nodesVisited)) < size(adjacencyMatrix,1) 950 | %%%Find all edges INTO the current node...edges into the node are 951 | %%%considered because this is a BACKWARD walk 952 | availableSourceNodes = find(adjacencyMatrix(:,currentNode) > 0); 953 | %%%Randomly choose one of the edges into of the node and designate as 954 | %%%source 955 | selectedSource = availableSourceNodes(randi(length(availableSourceNodes))); 956 | 957 | %%%If this is the first visit to the source node, then mark the edge as 958 | %%%part of the arborescence 959 | if sum([nodesVisited == selectedSource]) == 0 960 | arb(selectedSource,currentNode) = 1; end 961 | 962 | %%%Add target node to list of visited nodes 963 | nodesVisited = [nodesVisited,selectedSource]; 964 | 965 | currentNode = selectedSource; 966 | end 967 | end 968 | function result = nTuples_brooks(numItems,n) 969 | % 970 | % Create all possible nTuples of length n from a list of items of length = 971 | % numItems 972 | % 973 | % OUTPUT: 974 | % -result: matrix where each row corresponds to an nTuple of length n. Size 975 | % of matrix will be numItems^n x n 976 | % 977 | % INPUT: 978 | % -numItems: this is the number of items that are possible in each position 979 | % of each nTuple. 980 | % -n: this is the length of each nTuple. 981 | % 982 | % EXAMPLE: For all of the nTuples of length 2 of 3 items, use nTuples(3,2). 983 | % The result of this computation is: 984 | % 1 1 985 | % 1 2 986 | % 1 3 987 | % 2 1 988 | % 2 2 989 | % 2 3 990 | % 3 1 991 | % 3 2 992 | % 3 3 993 | % 994 | % VERSION: 1.0.05.03.2012 995 | % by Joseph Brooks, UCL Institute of Cognitive Neuroscience 996 | % brooks.jl@gmail.com 997 | result = zeros(numItems^n,n); 998 | for v = 1:numItems^n 999 | for i = 1:n 1000 | result(v,i) = mod(ceil(v/numItems^(i-1)),numItems)+1; 1001 | end 1002 | end 1003 | end 1004 | function result = nTuples_removeSelfAdjacencies_brooks(nTuplesList) 1005 | % 1006 | % VERSION: 1.0.05.03.2012 1007 | % by Joseph Brooks, UCL Institute of Cognitive Neuroscience 1008 | % brooks.jl@gmail.com 1009 | result = []; 1010 | for i = 1:size(nTuplesList,1) 1011 | containsAdjacency = false; 1012 | 1013 | for n = 1:size(nTuplesList,2)-1 1014 | if nTuplesList(i,n) == nTuplesList(i,n+1) containsAdjacency = true; 1015 | end; 1016 | end; 1017 | 1018 | if ~containsAdjacency 1019 | result(end+1,:) = nTuplesList(i,:); 1020 | end 1021 | end 1022 | end 1023 | % ========================================================================= 1024 | % * 1025 | % * SUBFUNCTIONS (SPM) 1026 | % * 1027 | % ========================================================================= 1028 | function [hrf,p] = spm_hrf(RT,P,T) 1029 | % Return a hemodynamic response function 1030 | % FORMAT [hrf,p] = spm_hrf(RT,p,T) 1031 | % RT - scan repeat time 1032 | % p - parameters of the response function (two Gamma functions) 1033 | % 1034 | % defaults 1035 | % (seconds) 1036 | % p(1) - delay of response (relative to onset) 6 1037 | % p(2) - delay of undershoot (relative to onset) 16 1038 | % p(3) - dispersion of response 1 1039 | % p(4) - dispersion of undershoot 1 1040 | % p(5) - ratio of response to undershoot 6 1041 | % p(6) - onset (seconds) 0 1042 | % p(7) - length of kernel (seconds) 32 1043 | % 1044 | % T - microtime resolution [Default: 16] 1045 | % 1046 | % hrf - hemodynamic response function 1047 | % p - parameters of the response function 1048 | %__________________________________________________________________________ 1049 | % Copyright (C) 1996-2014 Wellcome Trust Centre for Neuroimaging 1050 | 1051 | % Karl Friston 1052 | % $Id: spm_hrf.m 6108 2014-07-16 15:24:06Z guillaume $ 1053 | 1054 | 1055 | %-Parameters of the response function 1056 | %-------------------------------------------------------------------------- 1057 | p = [6 16 1 1 6 0 32]; 1058 | if nargin > 1, p(1:length(P)) = P; end 1059 | 1060 | %-Microtime resolution 1061 | %-------------------------------------------------------------------------- 1062 | if nargin > 2, fMRI_T = T; else fMRI_T = 16; end 1063 | 1064 | %-Modelled hemodynamic response function - {mixture of Gammas} 1065 | %-------------------------------------------------------------------------- 1066 | dt = RT/fMRI_T; 1067 | u = [0:ceil(p(7)/dt)] - p(6)/dt; 1068 | hrf = spm_Gpdf(u,p(1)/p(3),dt/p(3)) - spm_Gpdf(u,p(2)/p(4),dt/p(4))/p(5); 1069 | hrf = hrf([0:floor(p(7)/RT)]*fMRI_T + 1); 1070 | hrf = hrf'/sum(hrf); 1071 | end 1072 | function varargout = spm_filter(K,Y) 1073 | % Removes low frequency confounds X0 1074 | % FORMAT [Y] = spm_filter(K,Y) 1075 | % FORMAT [K] = spm_filter(K) 1076 | % 1077 | % K - filter matrix or: 1078 | % K(s) - struct array containing partition-specific specifications 1079 | % 1080 | % K(s).RT - observation interval in seconds 1081 | % K(s).row - row of Y constituting block/partition s 1082 | % K(s).HParam - cut-off period in seconds 1083 | % 1084 | % K(s).X0 - low frequencies to be removed (DCT) 1085 | % 1086 | % Y - data matrix 1087 | % 1088 | % K - filter structure 1089 | % Y - filtered data 1090 | %__________________________________________________________________________ 1091 | % 1092 | % spm_filter implements high-pass filtering in an efficient way by 1093 | % using the residual forming matrix of X0 - low frequency confounds 1094 | %.spm_filter also configures the filter structure in accord with the 1095 | % specification fields if called with one argument 1096 | %__________________________________________________________________________ 1097 | % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging 1098 | 1099 | % Karl Friston 1100 | % $Id: spm_filter.m 4498 2011-09-23 18:40:43Z guillaume $ 1101 | 1102 | 1103 | %-Configure filter 1104 | %========================================================================== 1105 | if nargin == 1 && isstruct(K) 1106 | 1107 | % set K.X0 1108 | %---------------------------------------------------------------------- 1109 | for s = 1:length(K) 1110 | 1111 | % make high pass filter 1112 | %------------------------------------------------------------------ 1113 | k = length(K(s).row); 1114 | n = fix(2*(k*K(s).RT)/K(s).HParam + 1); 1115 | X0 = spm_dctmtx(k,n); 1116 | K(s).X0 = X0(:,2:end); 1117 | 1118 | end 1119 | 1120 | % return filter structure 1121 | %---------------------------------------------------------------------- 1122 | varargout = { K }; 1123 | 1124 | %-Apply filter 1125 | %========================================================================== 1126 | else 1127 | 1128 | % K is a filter structure 1129 | %---------------------------------------------------------------------- 1130 | if isstruct(K) 1131 | 1132 | % ensure requisite fields are present 1133 | %------------------------------------------------------------------ 1134 | if ~isfield(K(1),'X0') 1135 | K = spm_filter(K); 1136 | end 1137 | 1138 | if numel(K) == 1 && length(K.row) == size(Y,1) 1139 | 1140 | % apply high pass filter 1141 | %-------------------------------------------------------------- 1142 | Y = Y - K.X0*(K.X0'*Y); 1143 | 1144 | else 1145 | 1146 | for s = 1:length(K) 1147 | 1148 | % select data 1149 | %---------------------------------------------------------- 1150 | y = Y(K(s).row,:); 1151 | 1152 | % apply high pass filter 1153 | %---------------------------------------------------------- 1154 | y = y - K(s).X0*(K(s).X0'*y); 1155 | 1156 | % reset filtered data in Y 1157 | %---------------------------------------------------------- 1158 | Y(K(s).row,:) = y; 1159 | 1160 | end 1161 | 1162 | end 1163 | 1164 | % K is simply a filter matrix 1165 | %---------------------------------------------------------------------- 1166 | else 1167 | 1168 | Y = K * Y; 1169 | 1170 | end 1171 | 1172 | % return filtered data 1173 | %---------------------------------------------------------------------- 1174 | varargout = { Y }; 1175 | end 1176 | end 1177 | function C = spm_dctmtx(N,K,n,f) 1178 | % Creates basis functions for Discrete Cosine Transform. 1179 | % FORMAT C = spm_dctmtx(N,K,n) 1180 | % OR C = spm_dctmtx(N,K) 1181 | % OR D = spm_dctmtx(N,K,n,'diff') 1182 | % OR D = spm_dctmtx(N,K,'diff') 1183 | % N - dimension 1184 | % K - order 1185 | % n - optional points to sample 1186 | %____________________________________________________________________________ 1187 | % spm_dctmtx creates a matrix for the first few basis functions of a one 1188 | % dimensional discrete cosine transform. 1189 | % With the 'diff' argument, spm_dctmtx produces the derivatives of the 1190 | % DCT. 1191 | % 1192 | % See: Fundamentals of Digital Image Processing (p 150-154). 1193 | % Anil K. Jain 1989. 1194 | %____________________________________________________________________________ 1195 | % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging 1196 | 1197 | % John Ashburner 1198 | % $Id: spm_dctmtx.m 1143 2008-02-07 19:33:33Z spm $ 1199 | 1200 | 1201 | d = 0; 1202 | 1203 | if nargin == 1, K = N; end; 1204 | 1205 | if any(nargin == [1 2]), 1206 | n = (0:(N-1))'; 1207 | elseif nargin == 3, 1208 | if strcmp(n,'diff'), 1209 | d = 1; 1210 | n = (0:(N-1))'; 1211 | elseif strcmp(n,'diff2'), 1212 | d = 2; 1213 | n = (0:(N-1))'; 1214 | else 1215 | n = n(:); 1216 | end 1217 | elseif nargin == 4, 1218 | n = n(:); 1219 | if strcmp(f,'diff'), 1220 | d = 1; 1221 | elseif strcmp(f,'diff2'), 1222 | d = 2; 1223 | else 1224 | error('Incorrect Usage'); 1225 | end 1226 | else 1227 | error('Incorrect Usage'); 1228 | end 1229 | 1230 | C = zeros(size(n,1),K); 1231 | 1232 | if d == 0, 1233 | C(:,1)=ones(size(n,1),1)/sqrt(N); 1234 | for k=2:K 1235 | C(:,k) = sqrt(2/N)*cos(pi*(2*n+1)*(k-1)/(2*N)); 1236 | end 1237 | elseif d == 1, 1238 | for k=2:K 1239 | C(:,k) = -2^(1/2)*(1/N)^(1/2)*sin(1/2*pi*(2*n*k-2*n+k-1)/N)*pi*(k-1)/N; 1240 | end 1241 | elseif d == 2, 1242 | for k=2:K, 1243 | C(:,k) = -2^(1/2)*(1/N)^(1/2)*cos(1/2*pi*(2*n+1)*(k-1)/N)*pi^2*(k-1)^2/N^2; 1244 | end; 1245 | else 1246 | error('Can''t do this'); 1247 | end 1248 | end 1249 | function f = spm_Gpdf(x,h,l) 1250 | % Probability Density Function (PDF) of Gamma distribution 1251 | % FORMAT f = spm_Gpdf(x,h,l) 1252 | % 1253 | % x - Gamma-variate (Gamma has range [0,Inf) ) 1254 | % h - Shape parameter (h>0) 1255 | % l - Scale parameter (l>0) 1256 | % f - PDF of Gamma-distribution with shape & scale parameters h & l 1257 | %__________________________________________________________________________ 1258 | % 1259 | % spm_Gpdf implements the Probability Density Function of the Gamma 1260 | % distribution. 1261 | % 1262 | % Definition: 1263 | %-------------------------------------------------------------------------- 1264 | % The PDF of the Gamma distribution with shape parameter h and scale l 1265 | % is defined for h>0 & l>0 and for x in [0,Inf) by: (See Evans et al., 1266 | % Ch18, but note that this reference uses the alternative 1267 | % parameterisation of the Gamma with scale parameter c=1/l) 1268 | % 1269 | % l^h * x^(h-1) exp(-lx) 1270 | % f(x) = ---------------------- 1271 | % gamma(h) 1272 | % 1273 | % Variate relationships: (Evans et al., Ch18 & Ch8) 1274 | %-------------------------------------------------------------------------- 1275 | % For natural (strictly +ve integer) shape h this is an Erlang distribution. 1276 | % 1277 | % The Standard Gamma distribution has a single parameter, the shape h. 1278 | % The scale taken as l=1. 1279 | % 1280 | % The Chi-squared distribution with v degrees of freedom is equivalent 1281 | % to the Gamma distribution with scale parameter 1/2 and shape parameter v/2. 1282 | % 1283 | % Algorithm: 1284 | %-------------------------------------------------------------------------- 1285 | % Direct computation using logs to avoid roundoff errors. 1286 | % 1287 | % References: 1288 | %-------------------------------------------------------------------------- 1289 | % Evans M, Hastings N, Peacock B (1993) 1290 | % "Statistical Distributions" 1291 | % 2nd Ed. Wiley, New York 1292 | % 1293 | % Abramowitz M, Stegun IA, (1964) 1294 | % "Handbook of Mathematical Functions" 1295 | % US Government Printing Office 1296 | % 1297 | % Press WH, Teukolsky SA, Vetterling AT, Flannery BP (1992) 1298 | % "Numerical Recipes in C" 1299 | % Cambridge 1300 | %__________________________________________________________________________ 1301 | % Copyright (C) 1993-2011 Wellcome Trust Centre for Neuroimaging 1302 | 1303 | % Andrew Holmes 1304 | % $Id: spm_Gpdf.m 4182 2011-02-01 12:29:09Z guillaume $ 1305 | 1306 | 1307 | %-Format arguments, note & check sizes 1308 | %-------------------------------------------------------------------------- 1309 | if nargin<3, error('Insufficient arguments'), end 1310 | 1311 | ad = [ndims(x);ndims(h);ndims(l)]; 1312 | rd = max(ad); 1313 | as = [[size(x),ones(1,rd-ad(1))];... 1314 | [size(h),ones(1,rd-ad(2))];... 1315 | [size(l),ones(1,rd-ad(3))]]; 1316 | rs = max(as); 1317 | xa = prod(as,2)>1; 1318 | if sum(xa)>1 && any(any(diff(as(xa,:)),1)) 1319 | error('non-scalar args must match in size'); 1320 | end 1321 | 1322 | %-Computation 1323 | %-------------------------------------------------------------------------- 1324 | %-Initialise result to zeros 1325 | f = zeros(rs); 1326 | 1327 | %-Only defined for strictly positive h & l. Return NaN if undefined. 1328 | md = ( ones(size(x)) & h>0 & l>0 ); 1329 | if any(~md(:)) 1330 | f(~md) = NaN; 1331 | warning('Returning NaN for out of range arguments'); 1332 | end 1333 | 1334 | %-Degenerate cases at x==0: h<1 => f=Inf; h==1 => f=l; h>1 => f=0 1335 | ml = ( md & x==0 & h<1 ); 1336 | f(ml) = Inf; 1337 | ml = ( md & x==0 & h==1 ); if xa(3), mll=ml; else mll=1; end 1338 | f(ml) = l(mll); 1339 | 1340 | %-Compute where defined and x>0 1341 | Q = find( md & x>0 ); 1342 | if isempty(Q), return, end 1343 | if xa(1), Qx=Q; else Qx=1; end 1344 | if xa(2), Qh=Q; else Qh=1; end 1345 | if xa(3), Ql=Q; else Ql=1; end 1346 | 1347 | %-Compute 1348 | f(Q) = exp( (h(Qh)-1).*log(x(Qx)) +h(Qh).*log(l(Ql)) - l(Ql).*x(Qx)... 1349 | -gammaln(h(Qh)) ); 1350 | end 1351 | % ========================================================================= 1352 | % * 1353 | % * SUBFUNCTIONS (UTILITIES) 1354 | % * 1355 | % ========================================================================= 1356 | function [settings, button] = settingsdlg(varargin) 1357 | % SETTINGSDLG Default dialog to produce a settings-structure 1358 | % 1359 | % settings = SETTINGSDLG('fieldname', default_value, ...) creates a modal 1360 | % dialog box that returns a structure formed according to user input. The 1361 | % input should be given in the form of 'fieldname', default_value - pairs, 1362 | % where 'fieldname' is the fieldname in the structure [settings], and 1363 | % default_value the initial value displayed in the dialog box. 1364 | % 1365 | % SETTINGSDLG uses UIWAIT to suspend execution until the user responds. 1366 | % 1367 | % settings = SETTINGSDLG(settings) uses the structure [settings] to form 1368 | % the various input fields. This is the most basic (and limited) usage of 1369 | % SETTINGSDLG. 1370 | % 1371 | % [settings, button] = SETTINGSDLG(settings) returns which button was 1372 | % pressed, in addition to the (modified) structure [settings]. Either 'ok', 1373 | % 'cancel' or [] are possible values. The empty output means that the 1374 | % dialog was closed before either Cancel or OK were pressed. 1375 | % 1376 | % SETTINGSDLG('title', 'window_title') uses 'window_title' as the dialog's 1377 | % title. The default is 'Adjust settings'. 1378 | % 1379 | % SETTINGSDLG('description', 'brief_description',...) starts the dialog box 1380 | % with 'brief_description', followed by the input fields. 1381 | % 1382 | % SETTINGSDLG('windowposition', P, ...) positions the dialog box according to 1383 | % the string or vector [P]; see movegui() for valid values. 1384 | % 1385 | % SETTINGSDLG( {'display_string', 'fieldname'}, default_value,...) uses the 1386 | % 'display_string' in the dialog box, while assigning the corresponding 1387 | % user-input to fieldname 'fieldname'. 1388 | % 1389 | % SETTINGSDLG(..., 'checkbox_string', true, ...) displays a checkbox in 1390 | % stead of the default edit box, and SETTINGSDLG('fieldname', {'string1', 1391 | % 'string2'},... ) displays a popup box with the strings given in 1392 | % the second cell-array. 1393 | % 1394 | % Additionally, you can put [..., 'separator', 'seperator_string',...] 1395 | % anywhere in the argument list, which will divide all the arguments into 1396 | % sections, with section headings 'seperator_string'. 1397 | % 1398 | % You can also modify the display behavior in the case of checkboxes. When 1399 | % defining checkboxes with a 2-element logical array, the second boolean 1400 | % determines whether all fields below that checkbox are initially disabled 1401 | % (true) or not (false). 1402 | % 1403 | % Example: 1404 | % 1405 | % [settings, button] = settingsdlg(... 1406 | % 'Description', 'This dialog will set the parameters used by FMINCON()',... 1407 | % 'title' , 'FMINCON() options',... 1408 | % 'separator' , 'Unconstrained/General',... 1409 | % {'This is a checkbox'; 'Check'}, [true true],... 1410 | % {'Tolerance X';'TolX'}, 1e-6,... 1411 | % {'Tolerance on Function';'TolFun'}, 1e-6,... 1412 | % 'Algorithm' , {'active-set','interior-point'},... 1413 | % 'separator' , 'Constrained',... 1414 | % {'Tolerance on Constraints';'TolCon'}, 1e-6) 1415 | % 1416 | % See also inputdlg, dialog, errordlg, helpdlg, listdlg, msgbox, questdlg, textwrap, 1417 | % uiwait, warndlg. 1418 | 1419 | 1420 | % Please report bugs and inquiries to: 1421 | % 1422 | % Name : Rody P.S. Oldenhuis 1423 | % E-mail : oldenhuis@gmail.com (personal) 1424 | % oldenhuis@luxspace.lu (professional) 1425 | % Affiliation: LuxSpace s?rl 1426 | % Licence : BSDnar 1427 | 1428 | 1429 | % Changelog 1430 | %{ 1431 | 2014/July/07 (Rody Oldenhuis) 1432 | - FIXED: The example in the help section didn't work correctly 1433 | (thanks Elco Bakker) 1434 | 1435 | 2014/February/14 (Rody Oldenhuis) 1436 | - Implemented window positioning option as suggested by Terrance Nearey. 1437 | The option is called 'WindowPosition', with valid values equal to those 1438 | of movegui(). 1439 | - Updated contact info, donation link 1440 | - Started changelog 1441 | 1442 | %} 1443 | 1444 | 1445 | % If you find this work useful, please consider a donation: 1446 | % https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6G3S5UYM7HJ3N 1447 | 1448 | %% Initialize 1449 | 1450 | % errortraps 1451 | narg = nargin; 1452 | if narg < 1, error('FEED ME MORE INPUTS, SEYMOUR!'); end 1453 | % 1454 | % error(narginchk(1, inf, narg, 'struct')); 1455 | 1456 | % parse input (+errortrap) 1457 | have_settings = 0; 1458 | if isstruct(varargin{1}) 1459 | settings = varargin{1}; have_settings = 1; end 1460 | if (narg == 1) 1461 | if isstruct(varargin{1}) 1462 | parameters = fieldnames(settings); 1463 | values = cellfun(@(x)settings.(x), parameters, 'UniformOutput', false); 1464 | else 1465 | error('settingsdlg:incorrect_input',... 1466 | 'When pasing a single argument, that argument must be a structure.') 1467 | end 1468 | else 1469 | parameters = varargin(1+have_settings : 2 : end); 1470 | values = varargin(2+have_settings : 2 : end); 1471 | end 1472 | 1473 | % Initialize data 1474 | button = []; 1475 | fields = cell(numel(parameters),1); 1476 | tags = fields; 1477 | 1478 | % Fill [settings] with default values & collect data 1479 | for ii = 1:numel(parameters) 1480 | 1481 | % Extract fields & tags 1482 | if iscell(parameters{ii}) 1483 | tags{ii} = parameters{ii}{1}; 1484 | fields{ii} = parameters{ii}{2}; 1485 | else 1486 | % More errortraps 1487 | if ~ischar(parameters{ii}) 1488 | error('settingsdlg:nonstring_parameter',... 1489 | 'Arguments should be given as [''parameter'', value,...] pairs.') 1490 | end 1491 | tags{ii} = parameters{ii}; 1492 | fields{ii} = parameters{ii}; 1493 | end 1494 | 1495 | % More errortraps 1496 | if ~ischar(fields{ii}) 1497 | error('settingsdlg:fieldname_not_char',... 1498 | 'Fieldname should be a string.') 1499 | end 1500 | if ~ischar(tags{ii}) 1501 | error('settingsdlg:tag_not_char',... 1502 | 'Display name should be a string.') 1503 | end 1504 | 1505 | % NOTE: 'Separator' is now in 'fields' even though 1506 | % it will not be used as a fieldname 1507 | 1508 | % Make sure all fieldnames are properly formatted 1509 | % (alternating capitals, no whitespace) 1510 | if ~strcmpi(fields{ii}, {'Separator';'Title';'Description'}) 1511 | whitespace = isspace(fields{ii}); 1512 | capitalize = circshift(whitespace,[0,1]); 1513 | fields{ii}(capitalize) = upper(fields{ii}(capitalize)); 1514 | fields{ii} = fields{ii}(~whitespace); 1515 | % insert associated value in output 1516 | if iscell(values{ii}) 1517 | settings.(fields{ii}) = values{ii}{1}; 1518 | elseif (length(values{ii}) > 1) 1519 | settings.(fields{ii}) = values{ii}(1); 1520 | else 1521 | settings.(fields{ii}) = values{ii}; 1522 | end 1523 | end 1524 | end 1525 | 1526 | % Avoid (some) confusion 1527 | clear parameters 1528 | 1529 | % Use default colorscheme from the OS 1530 | bgcolor = get(0, 'defaultUicontrolBackgroundColor'); 1531 | bgcolor = [234 234 230]/255; 1532 | fgcolor = [0 0 0]; 1533 | % Default fontsize 1534 | fontsize = get(0, 'defaultuicontrolfontsize'); 1535 | fontsize = fontsize*1.33; 1536 | % Edit-bgcolor is platform-dependent. 1537 | % MS/Windows: white. 1538 | % UNIX: same as figure bgcolor 1539 | % if ispc, edit_bgcolor = 'White'; 1540 | % else edit_bgcolor = bgcolor; 1541 | % end 1542 | 1543 | % TODO: not really applicable since defaultUicontrolBackgroundColor 1544 | % doesn't really seem to work on Unix... 1545 | edit_bgcolor = 'White'; 1546 | 1547 | % Get basic window properties 1548 | title = getValue('Adjust settings', 'Title'); 1549 | description = getValue( [], 'Description'); 1550 | total_width = getValue(325, 'WindowWidth'); 1551 | control_width = getValue(100, 'ControlWidth'); 1552 | 1553 | % Window positioning: 1554 | % Put the window in the center of the screen by default. 1555 | % This will usually work fine, except on some multi-monitor setups. 1556 | scz = get(0, 'ScreenSize'); 1557 | scxy = round(scz(3:4)/2-control_width/2); 1558 | scx = min(scz(3),max(1,scxy(1))); 1559 | scy = min(scz(4),max(1,scxy(2))); 1560 | 1561 | % String to pass on to movegui 1562 | window_position = getValue('center', 'WindowPosition'); 1563 | 1564 | 1565 | % Calculate best height for all uicontrol() 1566 | control_height = max(18, (fontsize+6)); 1567 | 1568 | % Calculate figure height (will be adjusted later according to description) 1569 | total_height = numel(fields)*1.25*control_height + ... % to fit all controls 1570 | 1.5*control_height + 20; % to fit "OK" and "Cancel" buttons 1571 | 1572 | % Total number of separators 1573 | num_separators = nnz(strcmpi(fields,'Separator')); 1574 | 1575 | % Draw figure in background 1576 | fighandle = figure(... 1577 | 'integerhandle' , 'off',... % use non-integers for the handle (prevents accidental plots from going to the dialog) 1578 | 'Handlevisibility', 'off',... % only visible from within this function 1579 | 'position' , [scx, scy, total_width, total_height],...% figure position 1580 | 'visible' , 'off',... % hide the dialog while it is being constructed 1581 | 'backingstore' , 'off',... % DON'T save a copy in the background 1582 | 'resize' , 'off', ... % but just keep it resizable 1583 | 'renderer' , 'zbuffer', ... % best choice for speed vs. compatibility 1584 | 'WindowStyle' ,'modal',... % window is modal 1585 | 'units' , 'pixels',... % better for drawing 1586 | 'DockControls' , 'off',... % force it to be non-dockable 1587 | 'name' , title,... % dialog title 1588 | 'menubar' ,'none', ... % no menubar of course 1589 | 'toolbar' ,'none', ... % no toolbar 1590 | 'NumberTitle' , 'off',... % "Figure 1.4728...:" just looks corny 1591 | 'Defaultuicontrolfontsize', fontsize, ... 1592 | 'color' , bgcolor); % use default colorscheme 1593 | 1594 | %% Draw all required uicontrols(), and unhide window 1595 | 1596 | % Define X-offsets (different when separators are used) 1597 | separator_offset_X = 2; 1598 | if num_separators > 0 1599 | text_offset_X = 20; 1600 | text_width = (total_width-control_width-text_offset_X); 1601 | else 1602 | text_offset_X = separator_offset_X; 1603 | text_width = (total_width-control_width); 1604 | end 1605 | 1606 | % Handle description 1607 | description_offset = 0; 1608 | if ~isempty(description) 1609 | 1610 | % create textfield (negligible height initially) 1611 | description_panel = uicontrol(... 1612 | 'parent' , fighandle,... 1613 | 'style' , 'text',... 1614 | 'backgroundcolor', bgcolor, ... 1615 | 'foreg', fgcolor, ... 1616 | 'Horizontalalignment', 'left',... 1617 | 'position', [separator_offset_X,... 1618 | total_height,total_width,1]); 1619 | 1620 | % wrap the description 1621 | description = textwrap(description_panel, {description}); 1622 | 1623 | % adjust the height of the figure 1624 | textheight = size(description,1)*(fontsize+6); 1625 | description_offset = textheight + 20; 1626 | total_height = total_height + description_offset; 1627 | set(fighandle,... 1628 | 'position', [scx, scy, total_width, total_height]) 1629 | 1630 | % adjust the position of the textfield and insert the description 1631 | set(description_panel, ... 1632 | 'string' , description,... 1633 | 'position', [separator_offset_X, total_height-textheight, ... 1634 | total_width, textheight]); 1635 | end 1636 | 1637 | % Define Y-offsets (different when descriptions are used) 1638 | control_offset_Y = total_height-control_height-description_offset; 1639 | 1640 | % initialize loop 1641 | controls = zeros(numel(tags)-num_separators,1); 1642 | ii = 1; sep_ind = 1; 1643 | enable = 'on'; separators = zeros(num_separators,1); 1644 | 1645 | % loop through the controls 1646 | if numel(tags) > 0 1647 | while true 1648 | 1649 | % Should we draw a separator? 1650 | if strcmpi(tags{ii}, 'Separator') 1651 | 1652 | % Print separator 1653 | uicontrol(... 1654 | 'style' , 'text',... 1655 | 'parent' , fighandle,... 1656 | 'string' , values{ii},... 1657 | 'horizontalalignment', 'left',... 1658 | 'fontweight', 'bold',... 1659 | 'backgroundcolor', bgcolor, ... 1660 | 'foreg',fgcolor, ... 1661 | 'fontsize', fontsize, ... 1662 | 'position', [separator_offset_X,control_offset_Y-4, ... 1663 | total_width, control_height]); 1664 | 1665 | % remove separator, but save its position 1666 | fields(ii) = []; 1667 | tags(ii) = []; separators(sep_ind) = ii; 1668 | values(ii) = []; sep_ind = sep_ind + 1; 1669 | 1670 | % reset enable (when neccessary) 1671 | if strcmpi(enable, 'off') 1672 | enable = 'on'; end 1673 | 1674 | % NOTE: DON'T increase loop index 1675 | 1676 | % ... or a setting? 1677 | else 1678 | 1679 | % logicals: use checkbox 1680 | if islogical(values{ii}) 1681 | 1682 | % First draw control 1683 | controls(ii) = uicontrol(... 1684 | 'style' , 'checkbox',... 1685 | 'parent' , fighandle,... 1686 | 'backgroundcolor', bgcolor, ... 1687 | 'foreg', 'white', ... 1688 | 'enable' , enable,... 1689 | 'string' , tags{ii},... 1690 | 'value' , values{ii}(1),... 1691 | 'position', [text_offset_X,control_offset_Y-4, ... 1692 | total_width, control_height]); 1693 | 1694 | % Should everything below here be OFF? 1695 | if (length(values{ii})>1) 1696 | % turn next controls off when asked for 1697 | if values{ii}(2) 1698 | enable = 'off'; end 1699 | % Turn on callback function 1700 | set(controls(ii),... 1701 | 'Callback', @(varargin) EnableDisable(ii,varargin{:})); 1702 | end 1703 | 1704 | % doubles : use edit box 1705 | % cells : use popup 1706 | % cell-of-cells: use table 1707 | else 1708 | % First print parameter 1709 | uicontrol(... 1710 | 'style' , 'text',... 1711 | 'parent' , fighandle,... 1712 | 'string' , [tags{ii}, ':'],... 1713 | 'horizontalalignment', 'left',... 1714 | 'backgroundcolor', bgcolor, ... 1715 | 'foreg', fgcolor, ... 1716 | 'position', [text_offset_X,control_offset_Y-4, ... 1717 | text_width, control_height]); 1718 | 1719 | % Popup, edit box or table? 1720 | style = 'edit'; 1721 | draw_table = false; 1722 | if iscell(values{ii}) 1723 | style = 'popup'; 1724 | if all(cellfun('isclass', values{ii}, 'cell')) 1725 | draw_table = true; end 1726 | end 1727 | 1728 | % Draw appropriate control 1729 | if ~draw_table 1730 | controls(ii) = uicontrol(... 1731 | 'enable' , enable,... 1732 | 'style' , style,... 1733 | 'Background', edit_bgcolor,... 1734 | 'parent' , fighandle,... 1735 | 'string' , values{ii},... 1736 | 'position', [text_width,control_offset_Y,... 1737 | control_width, control_height]); 1738 | else 1739 | % TODO 1740 | % ...table? ...radio buttons? How to do this? 1741 | warning(... 1742 | 'settingsdlg:not_yet_implemented',... 1743 | 'Treatment of cells is not yet implemented.'); 1744 | 1745 | end 1746 | end 1747 | 1748 | % increase loop index 1749 | ii = ii + 1; 1750 | end 1751 | 1752 | % end loop? 1753 | if ii > numel(tags) 1754 | break, end 1755 | 1756 | % Decrease offset 1757 | control_offset_Y = control_offset_Y - 1.25*control_height; 1758 | end 1759 | end 1760 | 1761 | % Draw cancel button 1762 | uicontrol(... 1763 | 'style' , 'pushbutton',... 1764 | 'parent' , fighandle,... 1765 | 'string' , 'Cancel',... 1766 | 'position', [separator_offset_X,2, total_width/2.5,control_height*1.5],... 1767 | 'Callback', @Cancel) 1768 | 1769 | % Draw OK button 1770 | uicontrol(... 1771 | 'style' , 'pushbutton',... 1772 | 'parent' , fighandle,... 1773 | 'string' , 'OK',... 1774 | 'position', [total_width*(1-1/2.5)-separator_offset_X,2, ... 1775 | total_width/2.5,control_height*1.5],... 1776 | 'Callback', @OK) 1777 | 1778 | % move to center of screen and make visible 1779 | movegui(fighandle, window_position); 1780 | set(fighandle, 'Visible', 'on'); 1781 | 1782 | % WAIT until OK/Cancel is pressed 1783 | uiwait(fighandle); 1784 | 1785 | 1786 | 1787 | %% Helper funcitons 1788 | 1789 | % Get a value from the values array: 1790 | % - if it does not exist, return the default value 1791 | % - if it exists, assign it and delete the appropriate entries from the 1792 | % data arrays 1793 | function val = getValue(default, tag) 1794 | index = strcmpi(fields, tag); 1795 | if any(index) 1796 | val = values{index}; 1797 | values(index) = []; 1798 | fields(index) = []; 1799 | tags(index) = []; 1800 | else 1801 | val = default; 1802 | end 1803 | end 1804 | 1805 | %% callback functions 1806 | 1807 | % Enable/disable controls associated with (some) checkboxes 1808 | function EnableDisable(which, varargin) %#ok 1809 | 1810 | % find proper range of controls to switch 1811 | if (num_separators > 1) 1812 | range = (which+1):(separators(separators > which)-1); 1813 | else range = (which+1):numel(controls); 1814 | end 1815 | 1816 | % enable/disable these controls 1817 | if strcmpi(get(controls(range(1)), 'enable'), 'off') 1818 | set(controls(range), 'enable', 'on') 1819 | else 1820 | set(controls(range), 'enable', 'off') 1821 | end 1822 | end 1823 | 1824 | % OK button: 1825 | % - update fields in [settings] 1826 | % - assign [button] output argument ('ok') 1827 | % - kill window 1828 | function OK(varargin) %#ok 1829 | 1830 | % button pressed 1831 | button = 'OK'; 1832 | 1833 | % fill settings 1834 | for i = 1:numel(controls) 1835 | 1836 | % extract current control's string, value & type 1837 | str = get(controls(i), 'string'); 1838 | val = get(controls(i), 'value'); 1839 | style = get(controls(i), 'style'); 1840 | 1841 | % popups/edits 1842 | if ~strcmpi(style, 'checkbox') 1843 | % extract correct string (popups only) 1844 | if strcmpi(style, 'popupmenu'), str = str{val}; end 1845 | % try to convert string to double 1846 | val = str2double(str); 1847 | % insert this double in [settings]. If it was not a 1848 | % double, insert string instead 1849 | if ~isnan(val), settings.(fields{i}) = val; 1850 | else settings.(fields{i}) = str; 1851 | end 1852 | 1853 | % checkboxes 1854 | else 1855 | % we can insert value immediately 1856 | settings.(fields{i}) = val; 1857 | end 1858 | end 1859 | 1860 | % kill window 1861 | delete(fighandle); 1862 | end 1863 | 1864 | % Cancel button: 1865 | % - assign [button] output argument ('cancel') 1866 | % - delete figure (so: return default settings) 1867 | function Cancel(varargin) %#ok 1868 | button = 'cancel'; 1869 | delete(fighandle); 1870 | end 1871 | 1872 | end 1873 | function [day, time] = get_timestamp(varargin) 1874 | % GET_TIMESTAMP 1875 | % 1876 | % USAGE: [day time] = get_timestamp 1877 | % 1878 | % day: mmm_DD_YYYY 1879 | % time: HHMMSSPM 1880 | % ===============================================% 1881 | day = strtrim(datestr(now,'YYYY_mmmDD')); 1882 | time = strtrim(datestr(now,'HHMMPM')); 1883 | end 1884 | function out = scalemat(in) 1885 | % SCALEMAT Columnwise rescaling to min 0 and max 1 1886 | % 1887 | % USAGE: out = scalemat(in) 1888 | % 1889 | % ------------------------------------------------------------------------- 1890 | if nargin<1, display('out = scalemat(in)'); return; end 1891 | nrow = size(in, 1); 1892 | out = (in - repmat(min(in), nrow, 1)) ./ (repmat(max(in), nrow, 1) - repmat(min(in), nrow, 1)); 1893 | end 1894 | function [d, id] = getchunks(a, opt) 1895 | 1896 | %GETCHUNKS Get the number of repetitions that occur in consecutive chunks. 1897 | % C = GETCHUNKS(A) returns an array of n elements, where n is the number 1898 | % of consecutive chunks (2 or more repetitions) in A, and each element is 1899 | % the number of repetitions in each chunk. A can be LOGICAL, any 1900 | % numeric vector, or CELL array of strings. It can also be a character 1901 | % array (see below, for its special treatment). 1902 | % 1903 | % [C, I] = GETCHUNKS(A) also returns the indices of the beginnings of the 1904 | % chunks. 1905 | % 1906 | % If A is a character array, then it finds words (consecutive 1907 | % non-spaces), returning the number of chararcters in each word and the 1908 | % indices to the beginnings of the words. 1909 | % 1910 | % GETCHUNKS(A, OPT) accepts an optional argument OPT, which can be any of 1911 | % the following three: 1912 | % 1913 | % '-reps' : return repeating chunks only. (default) 1914 | % '-full' : return chunks including single-element chunks. 1915 | % '-alpha' : (for CHAR arrays) only consider alphabets and numbers as 1916 | % part of words. Punctuations and symbols are regarded as 1917 | % spaces. 1918 | % 1919 | % Examples: 1920 | % A = [1 2 2 3 4 4 4 5 6 7 8 8 8 8 9]; 1921 | % getchunks(A) 1922 | % ans = 1923 | % 2 3 4 1924 | % 1925 | % 1926 | % B = 'This is a generic (simple) sentence'; 1927 | % [C, I] = getchunks(B) 1928 | % C = 1929 | % 4 2 1 7 8 8 1930 | % I = 1931 | % 1 6 9 11 19 28 1932 | % 1933 | % 1934 | % [C, I] = getchunks(B, '-alpha') 1935 | % C = 1936 | % 4 2 1 7 6 8 1937 | % I = 1938 | % 1 6 9 11 20 28 1939 | % 1940 | % See also HIST, HISTC. 1941 | % 1942 | % VERSIONS: 1943 | % v1.0 - first version 1944 | % v1.0.1 - added option '-alpha' 1945 | % 1946 | 1947 | % Copyright 2009 The MathWorks, Inc. 1948 | 1949 | %-------------------------------------------------------------------------- 1950 | % Error checking 1951 | %-------------------------------------------------------------------------- 1952 | % error(nargchk(1, 2, nargin)); 1953 | if nargin < 1, error('FEED ME MORE INPUTS, SEYMOUR!'); end 1954 | if ndims(a) > 2 || min(size(a)) > 1 1955 | error('Input must be a 2-D vector'); 1956 | end 1957 | 1958 | alphanumeric = false; 1959 | fullList = false; 1960 | 1961 | %-------------------------------------------------------------------------- 1962 | % Process options 1963 | %-------------------------------------------------------------------------- 1964 | if nargin == 2 1965 | if ~ischar(opt) 1966 | error('Additional argument must be a string array'); 1967 | end 1968 | 1969 | % Allow for partial arguments 1970 | possibleOptions = ['-full '; '-reps '; '-alpha']; 1971 | iOpt = strmatch(lower(opt), possibleOptions); 1972 | 1973 | if isempty(iOpt) || length(iOpt) > 1 1974 | error('Invalid option. Allowed option: ''-full'', ''-reps'', ''-alpha'''); 1975 | else 1976 | switch iOpt 1977 | 1978 | case 1 % '-full' 1979 | % Include single-element chunks 1980 | fullList = true; 1981 | if ischar(a) 1982 | fprintf('''-full'' option not applicable to CHAR arrays.\n'); 1983 | end 1984 | 1985 | case 2 % '-reps' 1986 | % Only find 2 or more repeating blocks 1987 | fullList = false; 1988 | 1989 | case 3 % '-alpha' 1990 | % For char arrays, only consider alphabets and numbers as part of 1991 | % words. Punctuations and symbols are regarded as space. 1992 | alphanumeric = true; 1993 | if ~ischar(a) 1994 | fprintf('''-alpha'' option only applicable to CHAR arrays.\n'); 1995 | end 1996 | 1997 | end 1998 | end 1999 | end 2000 | 2001 | %-------------------------------------------------------------------------- 2002 | % Convert to a row vector for STRFIND 2003 | %-------------------------------------------------------------------------- 2004 | a = a(:)'; 2005 | 2006 | %-------------------------------------------------------------------------- 2007 | % Deal with differet classes 2008 | %-------------------------------------------------------------------------- 2009 | switch class(a) 2010 | 2011 | case 'double' 2012 | % Leave as is 2013 | 2014 | case {'logical', 'uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32', 'single'} 2015 | % Convert to DOUBLE 2016 | a = double(a); 2017 | 2018 | case 'char' 2019 | if alphanumeric % Get alphabet and number locations 2020 | try % call C-helper function directly (slightly faster) 2021 | a = isletter(a) | ismembc(a, 48:57); 2022 | catch %#ok 2023 | a = isletter(a) | ismember(a, 48:57); 2024 | end 2025 | 2026 | else % Get non-space locations 2027 | a = ~isspace(a); 2028 | end 2029 | 2030 | case 'cell' 2031 | % Convert cell array of strings into unique numbers 2032 | if all(cellfun('isclass', a, 'char')) 2033 | [tmp, tmp, a] = unique(a); %#ok 2034 | else 2035 | error('Cell arrays must be array of strings.'); 2036 | end 2037 | 2038 | otherwise 2039 | error('Invalid type. Allowed type: CHAR, LOGICAL, NUMERIC, and CELL arrays of strings.'); 2040 | end 2041 | 2042 | %-------------------------------------------------------------------------- 2043 | % Character arrays (now LOGICAL) are dealt differently 2044 | %-------------------------------------------------------------------------- 2045 | if islogical(a) 2046 | % Pad the array 2047 | a = [false, a, false]; 2048 | 2049 | % Here's a very convoluted engine 2050 | b = diff(a); 2051 | id = strfind(b, 1); 2052 | d = strfind(b, -1) - id; 2053 | 2054 | %-------------------------------------------------------------------------- 2055 | % Everything else (numeric arrays) are processed here 2056 | else 2057 | % Pad the array 2058 | a = [NaN, a, NaN]; 2059 | 2060 | % Here's more convoluted code 2061 | b = diff(a); 2062 | b1 = b; % to be used in fullList (below) 2063 | ii = true(size(b)); 2064 | ii(strfind(b, 0)) = false; 2065 | b(ii) = 1; 2066 | c = diff(b); 2067 | id = strfind(c, -1); 2068 | 2069 | % Get single-element chunks also 2070 | if fullList 2071 | 2072 | % And more convoluted code 2073 | b1(id) = 0; 2074 | ii2 = find(b1(1:end-1)); 2075 | d = [strfind(c, 1) - id + 1, ones(1, length(ii2))]; 2076 | id = [id,ii2]; 2077 | [id,tmp] = sort(id); 2078 | d = d(tmp); 2079 | 2080 | else 2081 | 2082 | d = strfind(c, 1) - id + 1; 2083 | 2084 | end 2085 | end 2086 | function jitsample = makeJitters(minSOA, meanSOA, nTrials) 2087 | % MAKEJITTERS 2088 | % Make jitters from Poisson distributin with specified min and mean values 2089 | % 2090 | % USAGE: jitsample = makeJitters(minSOA, meanSOA, nTrials) 2091 | % 2092 | goodjit = 0; 2093 | while ~goodjit 2094 | jitsample = minSOA + poissrnd(meanSOA-minSOA, nTrials, 1); 2095 | if round(mean(jitsample)*100)==meanSOA*100, goodjit = 1; end 2096 | end 2097 | 2098 | end 2099 | function strucdisp(Structure, depth, printValues, maxArrayLength, fileName) 2100 | %STRUCDISP display structure outline 2101 | % 2102 | % STRUCDISP(STRUC, DEPTH, PRINTVALUES, MAXARRAYLENGTH, FILENAME) displays 2103 | % the hierarchical outline of a structure and its substructures. 2104 | % 2105 | % STRUC is a structure datatype with unknown field content. It can be 2106 | % either a scalar or a vector, but not a matrix. STRUC is the only 2107 | % mandatory argument in this function. All other arguments are optional. 2108 | % 2109 | % DEPTH is the number of hierarchical levels of the structure that are 2110 | % printed. If DEPTH is smaller than zero, all levels are printed. Default 2111 | % value for DEPTH is -1 (print all levels). 2112 | % 2113 | % PRINTVALUES is a flag that states if the field values should be printed 2114 | % as well. The default value is 1 (print values) 2115 | % 2116 | % MAXARRAYLENGTH is a positive integer, which determines up to which 2117 | % length or size the values of a vector or matrix are printed. For a 2118 | % vector holds that if the length of the vector is smaller or equal to 2119 | % MAXARRAYLENGTH, the values are printed. If the vector is longer than 2120 | % MAXARRAYLENGTH, then only the size of the vector is printed. 2121 | % The values of a 2-dimensional (m,n) array are printed if the number of 2122 | % elements (m x n) is smaller or equal to MAXARRAYLENGTH. 2123 | % For vectors and arrays, this constraint overrides the PRINTVALUES flag. 2124 | % 2125 | % FILENAME is the name of the file to which the output should be printed. 2126 | % if this argument is not defined, the output is printed to the command 2127 | % window. 2128 | % 2129 | % Contact author: B. Roossien 2130 | % (c) ECN 2007-2008 2131 | % 2132 | % Version 1.3.0 2133 | 2134 | 2135 | %% Creator and Version information 2136 | % Created by B. Roossien 14-12-2006 2137 | % 2138 | % Based on the idea of 2139 | % M. Jobse - display_structure (Matlab Central FileID 2031) 2140 | % 2141 | % Acknowledgements: 2142 | % S. Wegerich - printmatrix (Matlab Central FileID 971) 2143 | % 2144 | % Beta tested by: 2145 | % K. Visscher 2146 | % 2147 | % Feedback provided by: 2148 | % J. D'Errico 2149 | % H. Krause 2150 | % J.K. Kok 2151 | % J. Kurzmann 2152 | % K. Visscher 2153 | % 2154 | % 2155 | % (c) ECN 2006-2007 2156 | % www.ecn.nl 2157 | % 2158 | % Last edited on 08-03-2008 2159 | 2160 | 2161 | 2162 | %% Version History 2163 | % 2164 | % 1.3.0 : Bug fixes and added logicals 2165 | % 1.2.3 : Buf fix - Solved multi-line string content bug 2166 | % 1.2.2 : Bug fix - a field being an empty array gave an error 2167 | % 1.2.1 : Bug fix 2168 | % 1.2.0 : Increased readability of code 2169 | % Makes use of 'structfun' and 'cellfun' to increase speed and 2170 | % reduce the amount of code 2171 | % Solved bug with empty fieldname parameter 2172 | % 1.1.2 : Command 'eval' removed with a more simple and efficient solution 2173 | % 1.1.1 : Solved a bug with cell array fields 2174 | % 1.1.0 : Added support for arrayed structures 2175 | % Added small matrix size printing 2176 | % 1.0.1 : Bug with empty function parameters fixed 2177 | % 1.0.0 : Initial release 2178 | 2179 | 2180 | 2181 | %% Main program 2182 | %%%%% start program %%%%% 2183 | 2184 | % first argument must be structure 2185 | if ~isstruct(Structure) 2186 | error('First input argument must be structure'); 2187 | end 2188 | 2189 | % first argument can be a scalar or vector, but not a matrix 2190 | if ~isvector(Structure) 2191 | error('First input argument can be a scalar or vector, but not a matrix'); 2192 | end 2193 | 2194 | % default value for second argument is -1 (print all levels) 2195 | if nargin < 2 || isempty(depth) 2196 | depth = -1; 2197 | end 2198 | 2199 | % second argument must be an integer 2200 | if ~isnumeric(depth) 2201 | error('Second argument must be an integer'); 2202 | end 2203 | 2204 | % second argument only works if it is an integer, therefore floor it 2205 | depth = floor(depth); 2206 | 2207 | % default value for third argument is 1 2208 | if nargin < 3 || isempty(printValues) 2209 | printValues = 1; 2210 | end 2211 | 2212 | % default value for fourth argument is 10 2213 | if nargin < 4 || isempty(maxArrayLength) 2214 | maxArrayLength = 10; 2215 | end 2216 | 2217 | 2218 | % start recursive function 2219 | listStr = recFieldPrint(Structure, 0, depth, printValues, ... 2220 | maxArrayLength); 2221 | 2222 | 2223 | % 'listStr' is a cell array containing the output 2224 | % Now it's time to actually output the data 2225 | % Default is to output to the command window 2226 | % However, if the filename argument is defined, output it into a file 2227 | 2228 | if nargin < 5 || isempty(fileName) 2229 | 2230 | % write data to screen 2231 | for i = 1 : length(listStr) 2232 | disp(cell2mat(listStr(i, 1))); 2233 | end 2234 | 2235 | else 2236 | 2237 | % open file and check for errors 2238 | fid = fopen(fileName, 'wt'); 2239 | 2240 | if fid < 0 2241 | error('Unable to open output file'); 2242 | end 2243 | 2244 | % write data to file 2245 | for i = 1 : length(listStr) 2246 | fprintf(fid, '%s\n', cell2mat(listStr(i, 1))); 2247 | end 2248 | 2249 | % close file 2250 | fclose(fid); 2251 | 2252 | end 2253 | 2254 | end 2255 | function listStr = recFieldPrint(Structure, indent, depth, printValues, ... 2256 | maxArrayLength) 2257 | 2258 | 2259 | % Start to initialiase the cell listStr. This cell is used to store all the 2260 | % output, as this is much faster then directly printing it to screen. 2261 | 2262 | listStr = {}; 2263 | 2264 | 2265 | % "Structure" can be a scalar or a vector. 2266 | % In case of a vector, this recursive function is recalled for each of 2267 | % the vector elements. But if the values don't have to be printed, only 2268 | % the size of the structure and its fields are printed. 2269 | 2270 | if length(Structure) > 1 2271 | 2272 | if (printValues == 0) 2273 | 2274 | varStr = createArraySize(Structure, 'Structure'); 2275 | 2276 | listStr = [{' '}; {['Structure', varStr]}]; 2277 | 2278 | body = recFieldPrint(Structure(1), indent, depth, ... 2279 | printValues, maxArrayLength); 2280 | 2281 | listStr = [listStr; body; {' O'}]; 2282 | 2283 | else 2284 | 2285 | for iStruc = 1 : length(Structure) 2286 | 2287 | listStr = [listStr; {' '}; {sprintf('Structure(%d)', iStruc)}]; 2288 | 2289 | body = recFieldPrint(Structure(iStruc), indent, depth, ... 2290 | printValues, maxArrayLength); 2291 | 2292 | listStr = [listStr; body; {' O'}]; 2293 | 2294 | end 2295 | 2296 | end 2297 | 2298 | return 2299 | 2300 | end 2301 | 2302 | 2303 | %% Select structure fields 2304 | % The fields of the structure are distinguished between structure and 2305 | % non-structure fields. The structure fields are printed first, by 2306 | % recalling this function recursively. 2307 | 2308 | % First, select all fields. 2309 | 2310 | fields = fieldnames(Structure); 2311 | 2312 | % Next, structfun is used to return an boolean array with information of 2313 | % which fields are of type structure. 2314 | 2315 | isStruct = structfun(@isstruct, Structure); 2316 | 2317 | % Finally, select all the structure fields 2318 | 2319 | strucFields = fields(isStruct == 1); 2320 | 2321 | 2322 | %% Recursively print structure fields 2323 | % The next step is to select each structure field and handle it 2324 | % accordingly. Each structure can be empty, a scalar, a vector or a matrix. 2325 | % Matrices and long vectors are only printed with their fields and not with 2326 | % their values. Long vectors are defined as vectors with a length larger 2327 | % then the maxArrayLength value. The fields of an empty structure are not 2328 | % printed at all. 2329 | % It is not necessary to look at the length of the vector if the values 2330 | % don't have to be printed, as the fields of a vector or matrix structure 2331 | % are the same for each element. 2332 | 2333 | % First, some indentation calculations are required. 2334 | 2335 | strIndent = getIndentation(indent + 1); 2336 | listStr = [listStr; {strIndent}]; 2337 | 2338 | strIndent = getIndentation(indent); 2339 | 2340 | % Next, select each field seperately and handle it accordingly 2341 | 2342 | for iField = 1 : length(strucFields) 2343 | 2344 | fieldName = cell2mat(strucFields(iField)); 2345 | Field = Structure.(fieldName); 2346 | 2347 | % Empty structure 2348 | if isempty(Field) 2349 | 2350 | strSize = createArraySize(Field, 'Structure'); 2351 | 2352 | line = sprintf('%s |--- %s :%s', ... 2353 | strIndent, fieldName, strSize); 2354 | 2355 | listStr = [listStr; {line}]; 2356 | 2357 | % Scalar structure 2358 | elseif isscalar(Field) 2359 | 2360 | line = sprintf('%s |--- %s', strIndent, fieldName); 2361 | 2362 | % Recall this function if the tree depth is not reached yet 2363 | if (depth < 0) || (indent + 1 < depth) 2364 | lines = recFieldPrint(Field, indent + 1, depth, ... 2365 | printValues, maxArrayLength); 2366 | 2367 | listStr = [listStr; {line}; lines; ... 2368 | {[strIndent ' | O']}]; 2369 | else 2370 | listStr = [listStr; {line}]; 2371 | end 2372 | 2373 | % Short vector structure of which the values should be printed 2374 | elseif (isvector(Field)) && ... 2375 | (printValues > 0) && ... 2376 | (length(Field) < maxArrayLength) && ... 2377 | ((depth < 0) || (indent + 1 < depth)) 2378 | 2379 | % Use a for-loop to print all structures in the array 2380 | for iFieldElement = 1 : length(Field) 2381 | 2382 | line = sprintf('%s |--- %s(%g)', ... 2383 | strIndent, fieldName, iFieldElement); 2384 | 2385 | lines = recFieldPrint(field(iFieldElement), indent + 1, ... 2386 | depth, printValues, maxArrayLength); 2387 | 2388 | listStr = [listStr; {line}; lines; ... 2389 | {[strIndent ' | O']}]; 2390 | 2391 | if iFieldElement ~= length(Field) 2392 | listStr = [listStr; {[strIndent ' | ']}]; 2393 | end 2394 | 2395 | end 2396 | 2397 | % Structure is a matrix or long vector 2398 | % No values have to be printed or depth limit is reached 2399 | else 2400 | 2401 | varStr = createArraySize(Field, 'Structure'); 2402 | 2403 | line = sprintf('%s |--- %s :%s', ... 2404 | strIndent, fieldName, varStr); 2405 | 2406 | lines = recFieldPrint(Field(1), indent + 1, depth, ... 2407 | 0, maxArrayLength); 2408 | 2409 | listStr = [listStr; {line}; lines; ... 2410 | {[strIndent ' | O']}]; 2411 | 2412 | end 2413 | 2414 | % Some extra blank lines to increase readability 2415 | listStr = [listStr; {[strIndent ' | ']}]; 2416 | 2417 | end % End iField for-loop 2418 | 2419 | 2420 | %% Field Filler 2421 | % To properly align the field names, a filler is required. To know how long 2422 | % the filler must be, the length of the longest fieldname must be found. 2423 | % Because 'fields' is a cell array, the function 'cellfun' can be used to 2424 | % extract the lengths of all fields. 2425 | maxFieldLength = max(cellfun(@length, fields)); 2426 | 2427 | %% Print non-structure fields without values 2428 | % Print non-structure fields without the values. This can be done very 2429 | % quick. 2430 | if printValues == 0 2431 | 2432 | noStrucFields = fields(isStruct == 0); 2433 | 2434 | for iField = 1 : length(noStrucFields) 2435 | 2436 | Field = cell2mat(noStrucFields(iField)); 2437 | 2438 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2439 | 2440 | listStr = [listStr; {[strIndent ' |' filler ' ' Field]}]; 2441 | 2442 | end 2443 | 2444 | return 2445 | 2446 | end 2447 | 2448 | 2449 | %% Select non-structure fields (to print with values) 2450 | % Select fields that are not a structure and group them by data type. The 2451 | % following groups are distinguished: 2452 | % - characters and strings 2453 | % - numeric arrays 2454 | % - logical 2455 | % - empty arrays 2456 | % - matrices 2457 | % - numeric scalars 2458 | % - cell arrays 2459 | % - other data types 2460 | 2461 | % Character or string (array of characters) 2462 | isChar = structfun(@ischar, Structure); 2463 | charFields = fields(isChar == 1); 2464 | 2465 | % Numeric fields 2466 | isNumeric = structfun(@isnumeric, Structure); 2467 | 2468 | % Numeric scalars 2469 | isScalar = structfun(@isscalar, Structure); 2470 | isScalar = isScalar .* isNumeric; 2471 | scalarFields = fields(isScalar == 1); 2472 | 2473 | % Numeric vectors (arrays) 2474 | isVector = structfun(@isvector, Structure); 2475 | isVector = isVector .* isNumeric .* not(isScalar); 2476 | vectorFields = fields(isVector == 1); 2477 | 2478 | % Logical fields 2479 | isLogical = structfun(@islogical, Structure); 2480 | logicalFields = fields(isLogical == 1); 2481 | 2482 | % Empty arrays 2483 | isEmpty = structfun(@isempty, Structure); 2484 | emptyFields = fields(isEmpty == 1); 2485 | 2486 | % Numeric matrix with dimension size 2 or higher 2487 | isMatrix = structfun(@(x) ndims(x) >= 2, Structure); 2488 | isMatrix = isMatrix .* isNumeric .* not(isVector) ... 2489 | .* not(isScalar) .* not(isEmpty); 2490 | matrixFields = fields(isMatrix == 1); 2491 | 2492 | % Cell array 2493 | isCell = structfun(@iscell, Structure); 2494 | cellFields = fields(isCell == 1); 2495 | 2496 | % Datatypes that are not checked for 2497 | isOther = not(isChar + isNumeric + isCell + isStruct + isLogical + isEmpty); 2498 | otherFields = fields(isOther == 1); 2499 | 2500 | 2501 | 2502 | %% Print non-structure fields 2503 | % Print all the selected non structure fields 2504 | % - Strings are printed to a certain amount of characters 2505 | % - Vectors are printed as long as they are shorter than maxArrayLength 2506 | % - Matrices are printed if they have less elements than maxArrayLength 2507 | % - The values of cells are not printed 2508 | 2509 | 2510 | % Start with printing strings and characters. To avoid the display screen 2511 | % becoming a mess, the part of the string that is printed is limited to 31 2512 | % characters. In the future this might become an optional parameter in this 2513 | % function, but for now, it is placed in the code itself. 2514 | % if the string is longer than 31 characters, only the first 31 characters 2515 | % are printed, plus three dots to denote that the string is longer than 2516 | % printed. 2517 | 2518 | maxStrLength = 31; 2519 | 2520 | for iField = 1 : length(charFields) 2521 | 2522 | Field = cell2mat(charFields(iField)); 2523 | 2524 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2525 | 2526 | if (size(Structure.(Field), 1) > 1) && (size(Structure.(Field), 2) > 1) 2527 | 2528 | varStr = createArraySize(Structure.(Field), 'char'); 2529 | 2530 | elseif length(Field) > maxStrLength 2531 | 2532 | varStr = sprintf(' ''%s...''', Structure.(Field(1:maxStrLength))); 2533 | 2534 | else 2535 | 2536 | varStr = sprintf(' ''%s''', Structure.(Field)); 2537 | 2538 | end 2539 | 2540 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2541 | end 2542 | 2543 | 2544 | % Print empty fields 2545 | 2546 | for iField = 1 : length(emptyFields) 2547 | 2548 | 2549 | Field = cell2mat(emptyFields(iField)); 2550 | 2551 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2552 | 2553 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : [ ]' ]}]; 2554 | 2555 | end 2556 | 2557 | 2558 | % Print logicals. If it is a scalar, print true/false, else print vector 2559 | % information 2560 | 2561 | for iField = 1 : length(logicalFields) 2562 | 2563 | Field = cell2mat(logicalFields(iField)); 2564 | 2565 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2566 | 2567 | if isscalar(Structure.(Field)) 2568 | 2569 | logicalValue = {'False', 'True'}; 2570 | 2571 | varStr = sprintf(' %s', logicalValue{Structure.(Field) + 1}); 2572 | 2573 | else 2574 | 2575 | varStr = createArraySize(Structure.(Field), 'Logic array'); 2576 | 2577 | end 2578 | 2579 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2580 | 2581 | end 2582 | 2583 | 2584 | % Print numeric scalar field. The %g format is used, so that integers, 2585 | % floats and exponential numbers are printed in their own format. 2586 | 2587 | for iField = 1 : length(scalarFields) 2588 | 2589 | Field = cell2mat(scalarFields(iField)); 2590 | 2591 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2592 | 2593 | varStr = sprintf(' %g', Structure.(Field)); 2594 | 2595 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2596 | 2597 | end 2598 | 2599 | 2600 | % Print numeric array. If the length of the array is smaller then 2601 | % maxArrayLength, then the values are printed. Else, print the length of 2602 | % the array. 2603 | 2604 | for iField = 1 : length(vectorFields) 2605 | 2606 | Field = cell2mat(vectorFields(iField)); 2607 | 2608 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2609 | 2610 | if length(Structure.(Field)) > maxArrayLength 2611 | 2612 | varStr = createArraySize(Structure.(Field), 'Array'); 2613 | 2614 | else 2615 | 2616 | varStr = sprintf('%g ', Structure.(Field)); 2617 | 2618 | varStr = ['[' varStr(1:length(varStr) - 1) ']']; 2619 | 2620 | end 2621 | 2622 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : ' varStr]}]; 2623 | 2624 | end 2625 | 2626 | 2627 | % Print numeric matrices. If the matrix is two-dimensional and has more 2628 | % than maxArrayLength elements, only its size is printed. 2629 | % If the matrix is 'small', the elements are printed in a matrix structure. 2630 | % The top and the bottom of the matrix is indicated by a horizontal line of 2631 | % dashes. The elements are also lined out by using a fixed format 2632 | % (%#10.2e). Because the name of the matrix is only printed on the first 2633 | % line, the space is occupied by this name must be filled up on the other 2634 | % lines. This is done by defining a 'filler2'. 2635 | % This method was developed by S. Wegerich. 2636 | 2637 | for iField = 1 : length(matrixFields) 2638 | 2639 | Field = cell2mat(matrixFields(iField)); 2640 | 2641 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2642 | 2643 | if numel(Structure.(Field)) > maxArrayLength 2644 | 2645 | varStr = createArraySize(Structure.(Field), 'Array'); 2646 | 2647 | varCell = {[strIndent ' |' filler ' ' Field ' :' varStr]}; 2648 | 2649 | else 2650 | 2651 | matrixSize = size(Structure.(Field)); 2652 | 2653 | filler2 = char(ones(1, maxFieldLength + 6) * 32); 2654 | 2655 | dashes = char(ones(1, 12 * matrixSize(2) + 1) * 45); 2656 | 2657 | varCell = {[strIndent ' |' filler2 dashes]}; 2658 | 2659 | % first line with field name 2660 | varStr = sprintf('%#10.2e |', Structure.(Field)(1, :)); 2661 | 2662 | varCell = [varCell; {[strIndent ' |' filler ' ' ... 2663 | Field ' : |' varStr]}]; 2664 | 2665 | % second and higher number rows 2666 | for j = 2 : matrixSize(1) 2667 | 2668 | varStr = sprintf('%#10.2e |', Structure.(Field)(j, :)); 2669 | 2670 | varCell = [varCell; {[strIndent ' |' filler2 '|' varStr]}]; 2671 | end 2672 | 2673 | varCell = [varCell; {[strIndent ' |' filler2 dashes]}]; 2674 | 2675 | end 2676 | 2677 | listStr = [listStr; varCell]; 2678 | 2679 | end 2680 | 2681 | 2682 | % Print cell array information, i.e. the size of the cell array. The 2683 | % content of the cell array is not printed. 2684 | 2685 | for iField = 1 : length(cellFields) 2686 | 2687 | Field = cell2mat(cellFields(iField)); 2688 | 2689 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2690 | 2691 | varStr = createArraySize(Structure.(Field), 'Cell'); 2692 | 2693 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2694 | 2695 | end 2696 | 2697 | 2698 | % Print unknown datatypes. These include objects and user-defined classes 2699 | 2700 | for iField = 1 : length(otherFields) 2701 | 2702 | Field = cell2mat(otherFields(iField)); 2703 | 2704 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2705 | 2706 | varStr = createArraySize(Structure.(Field), 'Unknown'); 2707 | 2708 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2709 | 2710 | end 2711 | 2712 | end 2713 | function str = getIndentation(indent) 2714 | x = ' | '; 2715 | str = ''; 2716 | 2717 | for i = 1 : indent 2718 | str = cat(2, str, x); 2719 | end 2720 | end 2721 | function varStr = createArraySize(varName, type) 2722 | varSize = size(varName); 2723 | 2724 | arraySizeStr = sprintf('%gx', varSize); 2725 | arraySizeStr(length(arraySizeStr)) = []; 2726 | 2727 | varStr = [' [' arraySizeStr ' ' type ']']; 2728 | end 2729 | 2730 | 2731 | 2732 | end 2733 | function writedesign(in, outname) 2734 | % CS_WRITEREPORT 2735 | % 2736 | % USAGE: cs_writereport(in, basename) 2737 | % 2738 | % ARGUMENTS 2739 | % in: cell array of character arrays 2740 | % basename: name for output csv file 2741 | % 2742 | % ===============================================% 2743 | if nargin<2, error('USAGE: writereport(in, outname)'); end 2744 | [nrow, ncol] = size(in); 2745 | for i = 1:numel(in) 2746 | if isnumeric(in{i}), in{i} = num2str(in{i}); end 2747 | if strcmp(in{i},'NaN'), in{i} = ''; end 2748 | end 2749 | in = regexprep(in, ',', ''); 2750 | fid = fopen(outname,'w'); 2751 | for r = 1:nrow 2752 | fprintf(fid,[repmat('%s,',1,ncol) '\n'],in{r,:}); 2753 | end 2754 | fclose(fid); 2755 | end 2756 | function error_handler(err) 2757 | 2758 | newlog1 = cellstr(sprintf('\n\nUNDEFINED ERROR => %s', err.message)); 2759 | newlog2 = [printstruct(err.stack, 'name', 'Error Trace'); {''}]; 2760 | disp(char([newlog1; newlog2])); 2761 | 2762 | end 2763 | % ========================================================================= 2764 | % * 2765 | % * PRINTSTRUCT 2766 | % * 2767 | % ========================================================================= 2768 | function varargout = printstruct(S, varargin) 2769 | % PRINTSTRUCT Print a structure 2770 | % - adapted from STRUCDISP.m by B. Roossien 2771 | % 2772 | % USAGE: varargout = printstruct(S, varargin) 2773 | % 2774 | 2775 | % ---------------------- Copyright (C) 2015 Bob Spunt ---------------------- 2776 | % Created: 2015-08-13 2777 | % Email: spunt@caltech.edu 2778 | % __________________________________________________________________________ 2779 | def = { ... 2780 | 'nlevels', -1, ... 2781 | 'printvalues', 1, ... 2782 | 'maxarraylength', 25, ... 2783 | 'nindent', 0, ... 2784 | 'name', '', ... 2785 | }; 2786 | vals = setargs(def, varargin); 2787 | if nlevels==0, nlevels = -1; end; 2788 | if all([isempty(name) length(S)==1]), name = inputname(1); end 2789 | if all([isempty(name) length(S)>1]), name = 'STRUCTURE'; end 2790 | % | GET THE CONTENTS 2791 | str = recFieldPrint(S, nindent, nlevels, printvalues, maxarraylength, name); 2792 | if length(S)==1, str = [cellstr(name); str]; end 2793 | if nargout==0 2794 | for i = 1:length(str), disp(cell2mat(str(i, 1))); end 2795 | else 2796 | varargout{1} = str; 2797 | end 2798 | end 2799 | function listStr = recFieldPrint(Structure, indent, depth, printValues, maxArrayLength, structname) 2800 | % RECFIELDPRINT Recursive printing of a structure variable 2801 | % This function and its dependencies were taken from: 2802 | % STRUCDISP.m by 2803 | % Contact author: B. Roossien 2804 | % (c) ECN 2007-2008 2805 | % Version 1.3.0 2806 | % 2807 | listStr = {}; 2808 | if length(Structure) > 1 2809 | if (printValues == 0) 2810 | varStr = createArraySize(Structure, structname); 2811 | listStr = [{' '}; {[structname, varStr]}]; 2812 | body = recFieldPrint(Structure(1), indent, depth, ... 2813 | printValues, maxArrayLength); 2814 | listStr = [listStr; body]; 2815 | else 2816 | for iStruc = 1 : length(Structure) 2817 | listStr = [listStr; {' '}; {sprintf('%s(%d)', structname, iStruc)}]; 2818 | body = recFieldPrint(Structure(iStruc), indent, depth, ... 2819 | printValues, maxArrayLength); 2820 | listStr = [listStr; body]; 2821 | end 2822 | end 2823 | return 2824 | end 2825 | fields = fieldnames(Structure); 2826 | isStruct = structfun(@isstruct, Structure); 2827 | strucFields = fields(isStruct == 1); 2828 | strIndent = getIndentation(indent + 1); 2829 | listStr = [listStr; {strIndent}]; 2830 | strIndent = getIndentation(indent); 2831 | for iField = 1 : length(strucFields) 2832 | fieldName = cell2mat(strucFields(iField)); 2833 | Field = Structure.(fieldName); 2834 | 2835 | % Empty structure 2836 | if isempty(Field) 2837 | strSize = createArraySize(Field, structname); 2838 | line = sprintf('%s |--- %s :%s', ... 2839 | strIndent, fieldName, strSize); 2840 | listStr = [listStr; {line}]; 2841 | % Scalar structure 2842 | elseif isscalar(Field) 2843 | line = sprintf('%s |--- %s', strIndent, fieldName); 2844 | % Recall this function if the tree depth is not reached yet 2845 | if (depth < 0) || (indent + 1 < depth) 2846 | lines = recFieldPrint(Field, indent + 1, depth, ... 2847 | printValues, maxArrayLength); 2848 | listStr = [listStr; {line}; lines]; 2849 | else 2850 | listStr = [listStr; {line}]; 2851 | end 2852 | % Short vector structure of which the values should be printed 2853 | elseif (isvector(Field)) && ... 2854 | (printValues > 0) && ... 2855 | (length(Field) < maxArrayLength) && ... 2856 | ((depth < 0) || (indent + 1 < depth)) 2857 | % Use a for-loop to print all structures in the array 2858 | for iFieldElement = 1 : length(Field) 2859 | line = sprintf('%s |--- %s(%g)', ... 2860 | strIndent, fieldName, iFieldElement); 2861 | lines = recFieldPrint(Field(iFieldElement), indent + 1, ... 2862 | depth, printValues, maxArrayLength); 2863 | listStr = [listStr; {line}; lines]; 2864 | if iFieldElement ~= length(Field) 2865 | listStr = [listStr; {[strIndent ' | ']}]; 2866 | end 2867 | end 2868 | % Structure is a matrix or long vector 2869 | % No values have to be printed or depth limit is reached 2870 | else 2871 | varStr = createArraySize(Field, structname); 2872 | line = sprintf('%s |--- %s :%s', ... 2873 | strIndent, fieldName, varStr); 2874 | lines = recFieldPrint(Field(1), indent + 1, depth, ... 2875 | 0, maxArrayLength); 2876 | listStr = [listStr; {line}; lines]; 2877 | end 2878 | % Some extra blank lines to increase readability 2879 | listStr = [listStr; {[strIndent ' | ']}]; 2880 | end % End iField for-loop 2881 | %% Field Filler 2882 | % To properly align the field names, a filler is required. To know how long 2883 | % the filler must be, the length of the longest fieldname must be found. 2884 | % Because 'fields' is a cell array, the function 'cellfun' can be used to 2885 | % extract the lengths of all fields. 2886 | maxFieldLength = max(cellfun(@length, fields)); 2887 | %% Print non-structure fields without values 2888 | % Print non-structure fields without the values. This can be done very 2889 | % quick. 2890 | if printValues == 0 2891 | noStrucFields = fields(isStruct == 0); 2892 | for iField = 1 : length(noStrucFields) 2893 | Field = cell2mat(noStrucFields(iField)); 2894 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2895 | listStr = [listStr; {[strIndent ' |' filler ' ' Field]}]; 2896 | end 2897 | return 2898 | end 2899 | %% Select non-structure fields (to print with values) 2900 | % Select fields that are not a structure and group them by data type. The 2901 | % following groups are distinguished: 2902 | % - characters and strings 2903 | % - numeric arrays 2904 | % - logical 2905 | % - empty arrays 2906 | % - matrices 2907 | % - numeric scalars 2908 | % - cell arrays 2909 | % - other data types 2910 | % Character or string (array of characters) 2911 | isChar = structfun(@ischar, Structure); 2912 | charFields = fields(isChar == 1); 2913 | % Numeric fields 2914 | isNumeric = structfun(@isnumeric, Structure); 2915 | % Numeric scalars 2916 | isScalar = structfun(@isscalar, Structure); 2917 | isScalar = isScalar .* isNumeric; 2918 | scalarFields = fields(isScalar == 1); 2919 | % Numeric vectors (arrays) 2920 | isVector = structfun(@isvector, Structure); 2921 | isVector = isVector .* isNumeric .* not(isScalar); 2922 | vectorFields = fields(isVector == 1); 2923 | % Logical fields 2924 | isLogical = structfun(@islogical, Structure); 2925 | logicalFields = fields(isLogical == 1); 2926 | % Empty arrays 2927 | isEmpty = structfun(@isempty, Structure); 2928 | emptyFields = fields(isEmpty == 1); 2929 | % Numeric matrix with dimension size 2 or higher 2930 | isMatrix = structfun(@(x) ndims(x) >= 2, Structure); 2931 | isMatrix = isMatrix .* isNumeric .* not(isVector) .* not(isScalar) .* not(isEmpty); 2932 | matrixFields = fields(isMatrix == 1); 2933 | % Cell array 2934 | isCell = structfun(@iscell, Structure); 2935 | cellFields = fields(isCell == 1); 2936 | % Datatypes that are not checked for 2937 | isOther = not(isChar + isNumeric + isCell + isStruct + isLogical + isEmpty); 2938 | otherFields = fields(isOther == 1); 2939 | %% Print non-structure fields 2940 | % Print all the selected non structure fields 2941 | % - Strings are printed to a certain amount of characters 2942 | % - Vectors are printed as long as they are shorter than maxArrayLength 2943 | % - Matrices are printed if they have less elements than maxArrayLength 2944 | % - The values of cells are not printed 2945 | % Start with printing strings and characters. To avoid the display screen 2946 | % becoming a mess, the part of the string that is printed is limited to 31 2947 | % characters. In the future this might become an optional parameter in this 2948 | % function, but for now, it is placed in the code itself. 2949 | % if the string is longer than 31 characters, only the first 31 characters 2950 | % are printed, plus three dots to denote that the string is longer than 2951 | % printed. 2952 | maxStrLength = 31; 2953 | for iField = 1 : length(charFields) 2954 | Field = cell2mat(charFields(iField)); 2955 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2956 | if (size(Structure.(Field), 1) > 1) && (size(Structure.(Field), 2) > 1) 2957 | varStr = createArraySize(Structure.(Field), 'char'); 2958 | elseif length(Field) > maxStrLength 2959 | Val = Structure.(Field); 2960 | varStr = sprintf(' ''%s...''', Val(1:end)); 2961 | else 2962 | varStr = sprintf(' ''%s''', Structure.(Field)); 2963 | end 2964 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2965 | end 2966 | % Print empty fields 2967 | for iField = 1 : length(emptyFields) 2968 | Field = cell2mat(emptyFields(iField)); 2969 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2970 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : [ ]' ]}]; 2971 | end 2972 | % Print logicals. If it is a scalar, print true/false, else print vector 2973 | % information 2974 | for iField = 1 : length(logicalFields) 2975 | Field = cell2mat(logicalFields(iField)); 2976 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2977 | if isscalar(Structure.(Field)) 2978 | logicalValue = {'False', 'True'}; 2979 | varStr = sprintf(' %s', logicalValue{Structure.(Field) + 1}); 2980 | else 2981 | varStr = createArraySize(Structure.(Field), 'Logic array'); 2982 | end 2983 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2984 | end 2985 | % Print numeric scalar field. The %g format is used, so that integers, 2986 | % floats and exponential numbers are printed in their own format. 2987 | for iField = 1 : length(scalarFields) 2988 | Field = cell2mat(scalarFields(iField)); 2989 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2990 | varStr = sprintf(' %g', Structure.(Field)); 2991 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 2992 | end 2993 | % Print numeric array. If the length of the array is smaller then 2994 | % maxArrayLength, then the values are printed. Else, print the length of 2995 | % the array. 2996 | for iField = 1 : length(vectorFields) 2997 | Field = cell2mat(vectorFields(iField)); 2998 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 2999 | if length(Structure.(Field)) > maxArrayLength 3000 | varStr = createArraySize(Structure.(Field), 'Array'); 3001 | else 3002 | varStr = sprintf('%g ', Structure.(Field)); 3003 | varStr = ['[' varStr(1:length(varStr) - 1) ']']; 3004 | end 3005 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' : ' varStr]}]; 3006 | end 3007 | % Print numeric matrices. If the matrix is two-dimensional and has more 3008 | % than maxArrayLength elements, only its size is printed. 3009 | % If the matrix is 'small', the elements are printed in a matrix structure. 3010 | % The top and the bottom of the matrix is indicated by a horizontal line of 3011 | % dashes. The elements are also lined out by using a fixed format 3012 | % (%#10.2e). Because the name of the matrix is only printed on the first 3013 | % line, the space is occupied by this name must be filled up on the other 3014 | % lines. This is done by defining a 'filler2'. 3015 | % This method was developed by S. Wegerich. 3016 | for iField = 1 : length(matrixFields) 3017 | Field = cell2mat(matrixFields(iField)); 3018 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 3019 | if numel(Structure.(Field)) > maxArrayLength 3020 | varStr = createArraySize(Structure.(Field), 'Array'); 3021 | varCell = {[strIndent ' |' filler ' ' Field ' :' varStr]}; 3022 | else 3023 | matrixSize = size(Structure.(Field)); 3024 | filler2 = char(ones(1, maxFieldLength + 6) * 32); 3025 | dashes = char(ones(1, 12 * matrixSize(2) + 1) * 45); 3026 | varCell = {[strIndent ' |' filler2 dashes]}; 3027 | 3028 | % first line with field name 3029 | varStr = sprintf('%#10.2e |', Structure.(Field)(1, :)); 3030 | varCell = [varCell; {[strIndent ' |' filler ' ' ... 3031 | Field ' : |' varStr]}]; 3032 | % second and higher number rows 3033 | for j = 2 : matrixSize(1) 3034 | varStr = sprintf('%#10.2e |', Structure.(Field)(j, :)); 3035 | varCell = [varCell; {[strIndent ' |' filler2 '|' varStr]}]; 3036 | end 3037 | varCell = [varCell; {[strIndent ' |' filler2 dashes]}]; 3038 | 3039 | end 3040 | 3041 | listStr = [listStr; varCell]; 3042 | end 3043 | % Print cell array information, i.e. the size of the cell array. The 3044 | % content of the cell array is not printed. 3045 | for iField = 1 : length(cellFields) 3046 | Field = cell2mat(cellFields(iField)); 3047 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 3048 | varStr = createArraySize(Structure.(Field), 'Cell'); 3049 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 3050 | end 3051 | % Print unknown datatypes. These include objects and user-defined classes 3052 | for iField = 1 : length(otherFields) 3053 | Field = cell2mat(otherFields(iField)); 3054 | filler = char(ones(1, maxFieldLength - length(Field) + 2) * 45); 3055 | varStr = createArraySize(Structure.(Field), 'Unknown'); 3056 | listStr = [listStr; {[strIndent ' |' filler ' ' Field ' :' varStr]}]; 3057 | end 3058 | end 3059 | function str = getIndentation(indent) 3060 | x = ' | '; 3061 | str = ''; 3062 | for i = 1 : indent 3063 | str = cat(2, str, x); 3064 | end 3065 | end 3066 | function varStr = createArraySize(varName, type) 3067 | varSize = size(varName); 3068 | arraySizeStr = sprintf('%gx', varSize); 3069 | arraySizeStr(length(arraySizeStr)) = []; 3070 | varStr = [' [' arraySizeStr ' ' type ']']; 3071 | end 3072 | function argstruct = setargs(defaults, optargs) 3073 | % SETARGS Name/value parsing and assignment of varargin with default values 3074 | % 3075 | % This is a utility for setting the value of optional arguments to a 3076 | % function. The first argument is required and should be a cell array of 3077 | % "name, default value" pairs for all optional arguments. The second 3078 | % argument is optional and should be a cell array of "name, custom value" 3079 | % pairs for at least one of the optional arguments. 3080 | % 3081 | % USAGE: argstruct = setargs(defaults, args) 3082 | % __________________________________________________________________________ 3083 | % OUTPUT 3084 | % 3085 | % argstruct: structure containing the final argument values 3086 | % __________________________________________________________________________ 3087 | % INPUTS 3088 | % 3089 | % defaults: 3090 | % cell array of "name, default value" pairs for all optional arguments 3091 | % 3092 | % optargs [optional] 3093 | % cell array of "name, custom value" pairs for at least one of the 3094 | % optional arguments. this will typically be the "varargin" array. 3095 | % __________________________________________________________________________ 3096 | % USAGE EXAMPLE (WITHIN FUNCTION) 3097 | % 3098 | % defaults = {'arg1', 0, 'arg2', 'words', 'arg3', rand}; 3099 | % argstruct = setargs(defaults, varargin) 3100 | % 3101 | 3102 | 3103 | % ---------------------- Copyright (C) 2015 Bob Spunt ---------------------- 3104 | % Created: 2015-03-11 3105 | % Email: spunt@caltech.edu 3106 | % 3107 | % This program is free software: you can redistribute it and/or modify 3108 | % it under the terms of the GNU General Public License as published by 3109 | % the Free Software Foundation, either version 3 of the License, or (at 3110 | % your option) any later version. 3111 | % This program is distributed in the hope that it will be useful, but 3112 | % WITHOUT ANY WARRANTY; without even the implied warranty of 3113 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 3114 | % General Public License for more details. 3115 | % You should have received a copy of the GNU General Public License 3116 | % along with this program. If not, see: http://www.gnu.org/licenses/. 3117 | % __________________________________________________________________________ 3118 | if nargin < 1, mfile_showhelp; return; end 3119 | if nargin < 2, optargs = []; end 3120 | defaults = reshape(defaults, 2, length(defaults)/2)'; 3121 | if ~isempty(optargs) 3122 | if mod(length(optargs), 2) 3123 | error('Optional inputs must be entered as Name, Value pairs, e.g., myfunction(''name'', value)'); 3124 | end 3125 | arg = reshape(optargs, 2, length(optargs)/2)'; 3126 | for i = 1:size(arg,1) 3127 | idx = strncmpi(defaults(:,1), arg{i,1}, length(arg{i,1})); 3128 | if sum(idx) > 1 3129 | error(['Input "%s" matches multiple valid inputs:' repmat(' %s', 1, sum(idx))], arg{i,1}, defaults{idx, 1}); 3130 | elseif ~any(idx) 3131 | error('Input "%s" does not match a valid input.', arg{i,1}); 3132 | else 3133 | defaults{idx,2} = arg{i,2}; 3134 | end 3135 | end 3136 | end 3137 | for i = 1:size(defaults,1), assignin('caller', defaults{i,1}, defaults{i,2}); end 3138 | if nargout>0, argstruct = cell2struct(defaults(:,2), defaults(:,1)); end 3139 | end 3140 | function mfile_showhelp(varargin) 3141 | % MFILE_SHOWHELP 3142 | ST = dbstack('-completenames'); 3143 | if isempty(ST), fprintf('\nYou must call this within a function\n\n'); return; end 3144 | eval(sprintf('help %s', ST(2).file)); 3145 | end 3146 | function h = headsup(msg, titlestr, wait4resp) 3147 | % HEADSUP Present message to user and wait for a response 3148 | % 3149 | % USAGE: h = headsup(msg, *titlestr, *wait4resp) *optional input 3150 | % __________________________________________________________________________ 3151 | % INPUTS 3152 | % msg: character array to present to user 3153 | % 3154 | 3155 | % ---------------------- Copyright (C) 2014 Bob Spunt ---------------------- 3156 | % Created: 2014-09-30 3157 | % Email: spunt@caltech.edu 3158 | % __________________________________________________________________________ 3159 | if nargin < 1, disp('USAGE: h = headsup(msg, *titlestr, *wait4resp)'); return; end 3160 | if nargin < 2, titlestr = 'Heads Up'; end 3161 | if nargin < 3, wait4resp = 1; end 3162 | if iscell(msg), msg = char(msg); end 3163 | if iscell(titlestr), titlestr = char(titlestr); end 3164 | h(1) = figure(... 3165 | 'Units', 'norm', ... 3166 | 'WindowStyle', 'modal', ... 3167 | 'Position',[.425 .45 .15 .10],... 3168 | 'Resize','off',... 3169 | 'Color', [0.8941 0.1020 0.1098]*.60, ... 3170 | 'NumberTitle','off',... 3171 | 'DockControls','off',... 3172 | 'Tag', 'headsup', ... 3173 | 'MenuBar','none',... 3174 | 'Name',titlestr,... 3175 | 'Visible','on',... 3176 | 'Toolbar','none'); 3177 | h(2) = uicontrol('parent', h(1), 'units', 'norm', 'style', 'text', 'backg', [0.8941 0.1020 0.1098]*.60,'foreg', [248/255 248/255 248/255], 'horiz', 'center', ... 3178 | 'pos', [.050 .375 .900 .525], 'fontsize', 14, 'fontname', 'arial', 'fontw', 'bold', 'string', msg, 'visible', 'on'); 3179 | if wait4resp 3180 | h(3) = uicontrol('parent', h(1), 'units', 'norm', 'style', 'push', 'foreg', [0 0 0], 'horiz', 'center', ... 3181 | 'pos', [.4 .075 .2 .30], 'fontname', 'arial', 'fontsize', 16, 'fontw', 'bold', 'string', 'OK', 'visible', 'on', 'callback', {@cb_ok, h}); 3182 | uiwait(h(1)); 3183 | end 3184 | drawnow; 3185 | end 3186 | function cb_ok(varargin) 3187 | delete(findobj(0, 'Tag', 'headsup')); 3188 | drawnow; 3189 | end -------------------------------------------------------------------------------- /unused/testcarryorder.m: -------------------------------------------------------------------------------- 1 | clear n 2 | ncond = 2:10; 3 | order = 2; 4 | count = 0; 5 | for i = 1:length(ncond) 6 | for ii = 1:length(order) 7 | seq = carryoverCounterbalance(ncond(i), order(ii), 1); 8 | count = count + 1; 9 | n(count) = length(seq); 10 | end 11 | end 12 | disp(n) --------------------------------------------------------------------------------